, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
\ No newline at end of file
diff --git a/jsprit-analysis/pom.xml b/jsprit-analysis/pom.xml
new file mode 100644
index 00000000..2539d11b
--- /dev/null
+++ b/jsprit-analysis/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+ jsprit
+ jsprit-project
+ 0.0.1
+ ../jsprit-project/pom.xml
+
+ 4.0.0
+ jsprit-analysis
+ jar
+
+
+
+ org.jfree
+ jfreechart
+ 1.0.14
+ compile
+
+
+
+ ${project.groupId}
+ jsprit-core
+ ${project.version}
+ jar
+ provided
+
+
+
+
+
diff --git a/jsprit-analysis/src/main/java/analysis/AlgorithmSearchProgressChartListener.java b/jsprit-analysis/src/main/java/analysis/AlgorithmSearchProgressChartListener.java
new file mode 100644
index 00000000..980ced7a
--- /dev/null
+++ b/jsprit-analysis/src/main/java/analysis/AlgorithmSearchProgressChartListener.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package analysis;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.NumberTickUnit;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.algo.AlgorithmEndsListener;
+import basics.algo.IterationEndsListener;
+
+
+/**
+ * VehicleRoutingAlgorithm-Listener to record the solution-search-progress.
+ *
+ * Register this listener in VehicleRoutingAlgorithm.
+ *
+ * @author stefan schroeder
+ *
+ */
+
+public class AlgorithmSearchProgressChartListener implements IterationEndsListener, AlgorithmEndsListener {
+
+ private static Logger log = Logger.getLogger(AlgorithmSearchProgressChartListener.class);
+
+ private double[] bestResults;
+
+ private double[] worstResults;
+
+ private double[] avgResults;
+
+ private List bestResultList = new ArrayList();
+
+ private List worstResultList = new ArrayList();
+
+ private List avgResultList = new ArrayList();
+
+ private String filename;
+
+ /**
+ * Constructs chart listener with target png-file (filename plus path).
+ *
+ * @param pngFileName
+ */
+ public AlgorithmSearchProgressChartListener(String pngFileName) {
+ super();
+ this.filename = pngFileName;
+ }
+
+
+ @Override
+ public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection solutions) {
+ log.info("create chart " + filename);
+ if(bestResultList.isEmpty()){
+ log.warn("cannot create chart since no results available.");
+ return;
+ }
+ bestResults = new double[bestResultList.size()];
+ worstResults = new double[worstResultList.size()];
+ avgResults = new double[avgResultList.size()];
+
+ double maxValue = 0.0;
+ double minValue = Double.MAX_VALUE;
+
+ double[] iteration = new double[bestResultList.size()];
+ for (int i = 0; i < bestResultList.size(); i++) {
+ if(bestResultList.get(i) < minValue) minValue = bestResultList.get(i);
+ if(worstResultList.get(i) < minValue) minValue = worstResultList.get(i);
+ if(avgResultList.get(i) < minValue) minValue = avgResultList.get(i);
+
+ if(bestResultList.get(i) > maxValue) maxValue = bestResultList.get(i);
+ if(worstResultList.get(i) > maxValue) maxValue = worstResultList.get(i);
+ if(avgResultList.get(i) > maxValue) maxValue = avgResultList.get(i);
+
+ bestResults[i] = bestResultList.get(i);
+ worstResults[i] = worstResultList.get(i);
+ avgResults[i] = avgResultList.get(i);
+ iteration[i] = i;
+ }
+ XYSeriesCollection coll = new XYSeriesCollection();
+ JFreeChart chart = ChartFactory.createXYLineChart("search-progress","iterations", "results",coll, PlotOrientation.VERTICAL,true,true,false);
+ addSeries("bestResults", iteration, bestResults, coll);
+ addSeries("worstResults", iteration, worstResults, coll);
+ addSeries("avgResults", iteration, avgResults, coll);
+
+ XYPlot plot = chart.getXYPlot();
+
+ NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
+ Range rangeY = new Range(minValue-0.05*minValue,maxValue + 0.05*maxValue);
+ yAxis.setRange(rangeY);
+
+ try {
+ ChartUtilities.saveChartAsJPEG(new File(filename), chart, 1000, 600);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void addSeries(String string, double[] iteration, double[] results, XYSeriesCollection coll) {
+ XYSeries series = new XYSeries(string, true, true);
+ for(int i=0;i solutions) {
+ double worst = 0.0;
+ double best = Double.MAX_VALUE;
+ double sum = 0.0;
+ for(VehicleRoutingProblemSolution sol : solutions){
+ if(sol.getCost() > worst) worst = sol.getCost();
+ if(sol.getCost() < best) best = sol.getCost();
+ sum += sol.getCost();
+ }
+ bestResultList.add(best);
+ worstResultList.add(worst);
+ avgResultList.add(sum/(double)solutions.size());
+ }
+
+}
diff --git a/jsprit-analysis/src/main/java/analysis/SolutionPlotter.java b/jsprit-analysis/src/main/java/analysis/SolutionPlotter.java
new file mode 100644
index 00000000..f1902b33
--- /dev/null
+++ b/jsprit-analysis/src/main/java/analysis/SolutionPlotter.java
@@ -0,0 +1,204 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package analysis;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.NumberTickUnit;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import util.Coordinate;
+import util.Locations;
+
+import basics.Job;
+import basics.Service;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.route.TourActivity;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+
+
+/**
+ * A plotter to plot vehicle-routing-solution and routes respectively.
+ *
+ * @author stefan schroeder
+ *
+ */
+public class SolutionPlotter {
+
+ private static Logger log = Logger.getLogger(SolutionPlotter.class);
+
+ /**
+ * Plots the solution to pngFile, with title.
+ *
+ * This can only plot if vehicles and jobs have locationIds and coordinates (@see Coordinate). Otherwise a warning message is logged
+ * and method returns but does not plot.
+ *
+ * @param vrp
+ * @param solution
+ * @param pngFile target path with filename.
+ * @see VehicleRoutingProblem, VehicleRoutingProblemSolution
+ */
+ public static void plotSolutionAsPNG(VehicleRoutingProblem vrp, VehicleRoutingProblemSolution solution, String pngFile, String plotTitle){
+ final Map locs = new HashMap();
+ boolean locationsRetrieved = retrieveLocations(locs,vrp);
+ if(!locationsRetrieved) return;
+ Locations locations = new Locations(){
+
+ @Override
+ public Coordinate getCoord(String id) {
+ return locs.get(id);
+ }
+
+ };
+ plotRoutesAsPNG(solution.getRoutes(), locations, pngFile, plotTitle);
+
+ }
+
+
+ /**
+ * Plots the a collection of routes to pngFile.
+ *
+ *
+ * @param routes
+ * @param locations indicating the locations for the tour-activities.
+ * @param pngFile target path with filename.
+ * @param plotTitle
+ * @see VehicleRoute
+ */
+ public static void plotRoutesAsPNG(Collection routes, Locations locations, String pngFile, String plotTitle) {
+ log.info("plot routes to " + pngFile);
+ XYSeriesCollection coll = new XYSeriesCollection();
+ int counter = 1;
+ double maxX = 0;
+ double minX = Double.MAX_VALUE;
+ double maxY = 0;
+ double minY = Double.MAX_VALUE;
+ for(VehicleRoute route : routes){
+ if(route.isEmpty()) continue;
+ XYSeries series = new XYSeries(counter, false, true);
+
+ Coordinate startCoord = locations.getCoord(route.getStart().getLocationId());
+ series.add(startCoord.getX(), startCoord.getY());
+ if(startCoord.getX() > maxX) maxX = startCoord.getX();
+ if(startCoord.getY() > maxY) maxY = startCoord.getY();
+ if(startCoord.getX() < minX) minX = startCoord.getX();
+ if(startCoord.getY() < minY) minY = startCoord.getY();
+
+ for(TourActivity act : route.getTourActivities().getActivities()){
+ Coordinate coord = locations.getCoord(act.getLocationId());
+ series.add(coord.getX(), coord.getY());
+ if(coord.getX() > maxX) maxX = coord.getX();
+ if(coord.getY() > maxY) maxY = coord.getY();
+ if(coord.getX() < minX) minX = coord.getX();
+ if(coord.getY() < minY) minY = coord.getY();
+ }
+
+ Coordinate endCoord = locations.getCoord(route.getEnd().getLocationId());
+ series.add(endCoord.getX(), endCoord.getY());
+ if(endCoord.getX() > maxX) maxX = endCoord.getX();
+ if(endCoord.getY() > maxY) maxY = endCoord.getY();
+ if(endCoord.getX() < minX) minX = endCoord.getX();
+ if(endCoord.getY() < minY) minY = endCoord.getY();
+
+ coll.addSeries(series);
+ counter++;
+ }
+ JFreeChart chart = ChartFactory.createXYLineChart(plotTitle,"x-coordinate", "y-coordinate",coll, PlotOrientation.VERTICAL,true,true,false);
+
+ XYPlot plot = chart.getXYPlot();
+
+ XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) plot.getRenderer();
+ renderer.setBaseShapesVisible(true);
+
+ NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
+ xAxis.setTickUnit(new NumberTickUnit(10));
+// Range rangeX = new Range(minX - 0.05*minX, maxX+0.05*maxX);
+ Range rangeX = new Range(minX, maxX);
+ xAxis.setRange(rangeX);
+
+ NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
+// yAxis.setTickUnit(new NumberTickUnit(10));
+// Range rangeY = new Range(minY-0.05*minY,maxY + 0.05*maxY);
+ Range rangeY = new Range(minY,maxY);
+ yAxis.setRange(rangeY);
+ try {
+ ChartUtilities.saveChartAsPNG(new File(pngFile), chart, 1000, 600);
+ } catch (IOException e) {
+ log.error("cannot plot");
+ log.error(e);
+ e.printStackTrace();
+
+ }
+ }
+
+ private static boolean retrieveLocations(Map locs, VehicleRoutingProblem vrp) {
+ for(Vehicle v : vrp.getVehicles()){
+ String locationId = v.getLocationId();
+ if(locationId == null){
+ log.warn("cannot plot solution, since vehicle " + v + " has no locationId.");
+ return false;
+ }
+ Coordinate coord = v.getCoord();
+ if(coord == null){
+ log.warn("cannot plot solution, since vehicle " + v + " has no location-coordinate.");
+ return false;
+ }
+ locs.put(locationId, coord);
+ }
+ for(Job j : vrp.getJobs().values()){
+ if(j instanceof Service){
+ String locationId = ((Service) j).getLocationId();
+ if(locationId == null){
+ log.warn("cannot plot solution, since job " + j + " has no locationId.");
+ return false;
+ }
+ Coordinate coord = ((Service) j).getCoord();
+ if(coord == null){
+ log.warn("cannot plot solution, since job " + j + " has no location-coordinate.");
+ return false;
+ }
+ locs.put(locationId, coord);
+ }
+ else{
+ throw new IllegalStateException("job is not a service. this is not supported yet.");
+ }
+ }
+ return true;
+ }
+
+
+}
diff --git a/jsprit-analysis/src/main/java/analysis/SolutionPrinter.java b/jsprit-analysis/src/main/java/analysis/SolutionPrinter.java
new file mode 100644
index 00000000..01476de8
--- /dev/null
+++ b/jsprit-analysis/src/main/java/analysis/SolutionPrinter.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package analysis;
+
+import basics.VehicleRoutingProblemSolution;
+import basics.route.DefaultVehicleRouteCostCalculator;
+import basics.route.VehicleRoute;
+
+/**
+ * Printer to print the details of a vehicle-routing-problem solution.
+ *
+ * @author stefan schroeder
+ *
+ */
+public class SolutionPrinter {
+
+ /**
+ * Enum to indicate verbose-level.
+ *
+ * Print.CONCISE and Print.VERBOSE are available.
+ *
+ * @author stefan schroeder
+ *
+ */
+ public enum Print {
+
+ CONCISE,VERBOSE
+ }
+
+ /**
+ * Prints costs and #vehicles to stdout (System.out.println).
+ *
+ * @param solution
+ */
+ public static void print(VehicleRoutingProblemSolution solution){
+ System.out.println("[costs="+solution.getCost() + "]");
+ System.out.println("[#vehicles="+solution.getRoutes().size() + "]");
+
+ }
+
+ /**
+ * Prints the details of the solution according to a print-level, i.e. Print.CONCISE or PRINT.VERBOSE.
+ *
+ *
CONCISE prints total-costs and #vehicles.
+ *
VERBOSE prints the route-details additionally. If the DefaultVehicleRouteCostCalculator (which is the standard-calculator)
+ * is used in VehicleRoute, then route-costs are differentiated further between transport, activity, vehicle, driver and other-costs.
+ *
+ * @param solution
+ * @param level
+ */
+ public static void print(VehicleRoutingProblemSolution solution, Print level){
+ if(level.equals(Print.CONCISE)){
+ print(solution);
+ }
+ else{
+ print(solution);
+ System.out.println("routes");
+ int routeCount = 1;
+ for(VehicleRoute route : solution.getRoutes()){
+ System.out.println("[route="+routeCount+"][departureTime="+route.getStart().getEndTime()+"[total=" + route.getCost() + "]");
+ if(route.getVehicleRouteCostCalculator() instanceof DefaultVehicleRouteCostCalculator){
+ DefaultVehicleRouteCostCalculator defaultCalc = (DefaultVehicleRouteCostCalculator) route.getVehicleRouteCostCalculator();
+ System.out.println("[transport=" + defaultCalc.getTpCosts() + "][activity=" + defaultCalc.getActCosts() +
+ "][vehicle=" + defaultCalc.getVehicleCosts() + "][driver=" + defaultCalc.getDriverCosts() + "][other=" + defaultCalc.getOther() + "]");
+ }
+ routeCount++;
+ }
+ }
+
+
+ }
+
+}
diff --git a/jsprit-analysis/src/main/java/analysis/StopWatch.java b/jsprit-analysis/src/main/java/analysis/StopWatch.java
new file mode 100644
index 00000000..bbc45356
--- /dev/null
+++ b/jsprit-analysis/src/main/java/analysis/StopWatch.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package analysis;
+
+import java.util.Collection;
+
+import org.apache.log4j.Logger;
+
+import basics.VehicleRoutingAlgorithm;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.algo.AlgorithmEndsListener;
+import basics.algo.AlgorithmStartsListener;
+
+public class StopWatch implements AlgorithmStartsListener, AlgorithmEndsListener{
+
+ private static Logger log = Logger.getLogger(StopWatch.class);
+
+ private double ran;
+
+ private double startTime;
+
+
+ @Override
+ public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection solutions) {
+ reset();
+ start();
+ }
+
+ public double getCompTimeInSeconds(){
+ return (ran)/1000.0;
+ }
+
+ @Override
+ public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection solutions) {
+ stop();
+ log.info("computation time [in sec]: " + getCompTimeInSeconds());
+ }
+
+ public void stop(){
+ ran += System.currentTimeMillis() - startTime;
+ }
+
+ public void start(){
+ startTime = System.currentTimeMillis();
+ }
+
+ public void reset(){
+ startTime = 0;
+ ran = 0;
+ }
+
+ @Override
+ public String toString() {
+ return "stopWatch: " + getCompTimeInSeconds() + " sec";
+ }
+
+ public double getCurrTimeInSeconds() {
+ return (System.currentTimeMillis()-startTime)/1000.0;
+ }
+
+}
diff --git a/jsprit-core/.DS_Store b/jsprit-core/.DS_Store
new file mode 100644
index 00000000..b365a10f
Binary files /dev/null and b/jsprit-core/.DS_Store differ
diff --git a/jsprit-core/.classpath b/jsprit-core/.classpath
new file mode 100644
index 00000000..7fcefca3
--- /dev/null
+++ b/jsprit-core/.classpath
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jsprit-core/.project b/jsprit-core/.project
new file mode 100644
index 00000000..b663793f
--- /dev/null
+++ b/jsprit-core/.project
@@ -0,0 +1,29 @@
+
+
+ jsprit-core
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.maven.ide.eclipse.maven2Builder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.jdt.core.javanature
+ org.maven.ide.eclipse.maven2Nature
+
+
diff --git a/jsprit-core/.settings/org.eclipse.core.resources.prefs b/jsprit-core/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 00000000..afad9066
--- /dev/null
+++ b/jsprit-core/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,7 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding//src/test/resources=UTF-8
+encoding/=UTF-8
+encoding/src=UTF-8
diff --git a/jsprit-core/.settings/org.eclipse.jdt.core.prefs b/jsprit-core/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 00000000..69c31cd4
--- /dev/null
+++ b/jsprit-core/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/jsprit-core/.settings/org.eclipse.m2e.core.prefs b/jsprit-core/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 00000000..f897a7f1
--- /dev/null
+++ b/jsprit-core/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/jsprit-core/.settings/org.maven.ide.eclipse.prefs b/jsprit-core/.settings/org.maven.ide.eclipse.prefs
new file mode 100644
index 00000000..fee42859
--- /dev/null
+++ b/jsprit-core/.settings/org.maven.ide.eclipse.prefs
@@ -0,0 +1,9 @@
+#Fri Mar 15 09:09:12 CET 2013
+activeProfiles=
+eclipse.preferences.version=1
+fullBuildGoals=process-test-resources
+includeModules=false
+resolveWorkspaceProjects=true
+resourceFilterGoals=process-resources resources\:testResources
+skipCompilerPlugin=true
+version=1
diff --git a/jsprit-core/license.txt b/jsprit-core/license.txt
new file mode 100644
index 00000000..f258a711
--- /dev/null
+++ b/jsprit-core/license.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
\ No newline at end of file
diff --git a/jsprit-core/pom.xml b/jsprit-core/pom.xml
new file mode 100644
index 00000000..7b0fee12
--- /dev/null
+++ b/jsprit-core/pom.xml
@@ -0,0 +1,60 @@
+
+
+
+ jsprit
+ jsprit-project
+ 0.0.1
+ ../jsprit-project/pom.xml
+
+ 4.0.0
+ jsprit-core
+ jsprit-core
+
+
+
+
+ org.apache.commons
+ commons-math
+ 2.2
+ jar
+ compile
+
+
+
+ commons-configuration
+ commons-configuration
+ 1.9
+ jar
+ compile
+
+
+
+ xerces
+ xerces
+ 2.4.0
+
+
+
+
+
+
+
diff --git a/jsprit-core/src/main/java/algorithms/AbstractInsertionStrategy.java b/jsprit-core/src/main/java/algorithms/AbstractInsertionStrategy.java
new file mode 100644
index 00000000..f2dfca54
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/AbstractInsertionStrategy.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import basics.Job;
+import basics.algo.InsertionEndsListener;
+import basics.algo.InsertionListener;
+import basics.algo.InsertionStartsListener;
+import basics.algo.JobInsertedListener;
+import basics.route.VehicleRoute;
+
+
+
+
+abstract class AbstractInsertionStrategy implements InsertionStrategy{
+
+ private static Logger log = Logger.getLogger(AbstractInsertionStrategy.class);
+
+ private Collection listener = new ArrayList();
+
+ public abstract RouteAlgorithm getRouteAlgorithm();
+
+ public void informJobInserted(int nOfJobs2Recreate, Job insertedJob, VehicleRoute insertedIn){
+ for(InsertionListener l : listener){
+ if(l instanceof JobInsertedListener){
+ ((JobInsertedListener)l).informJobInserted(nOfJobs2Recreate, insertedJob, insertedIn);
+ }
+ }
+ }
+
+ public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route){
+ for(InsertionListener l : listener){
+ if(l instanceof BeforeJobInsertionListener){
+ ((BeforeJobInsertionListener)l).informBeforeJobInsertion(job, data, route);
+ }
+ }
+ }
+
+ public void informInsertionStarts(Collection vehicleRoutes, int nOfJobs2Recreate){
+ for(InsertionListener l : listener){
+ if(l instanceof InsertionStartsListener){
+ ((InsertionStartsListener)l).informInsertionStarts(vehicleRoutes,nOfJobs2Recreate);
+ }
+ }
+ }
+
+ public void informInsertionEndsListeners(Collection vehicleRoutes) {
+ for(InsertionListener l : listener){
+ if(l instanceof InsertionEndsListener){
+ ((InsertionEndsListener)l).informInsertionEnds(vehicleRoutes);
+ }
+ }
+ }
+
+ public Collection getListener() {
+ return Collections.unmodifiableCollection(listener);
+ }
+
+ public void addListener(InsertionListener l){
+ log.info("add insertion-listener " + l);
+ listener.add(l);
+ }
+
+ public void addAllListener(List list) {
+ for(InsertionListener l : list) addListener(l);
+ }
+
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/AuxilliaryCostCalculator.java b/jsprit-core/src/main/java/algorithms/AuxilliaryCostCalculator.java
new file mode 100644
index 00000000..43c2dc04
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/AuxilliaryCostCalculator.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Iterator;
+import java.util.List;
+
+import basics.costs.VehicleRoutingActivityCosts;
+import basics.costs.VehicleRoutingTransportCosts;
+import basics.route.Driver;
+import basics.route.TourActivity;
+import basics.route.Vehicle;
+
+
+final class AuxilliaryCostCalculator {
+
+ private final VehicleRoutingTransportCosts routingCosts;
+
+ private final VehicleRoutingActivityCosts activityCosts;
+
+ public AuxilliaryCostCalculator(final VehicleRoutingTransportCosts routingCosts, final VehicleRoutingActivityCosts costFunction) {
+ super();
+ this.routingCosts = routingCosts;
+ this.activityCosts = costFunction;
+ }
+
+ /**
+ *
+ * @param path
+ * @param depTime
+ * @param driver
+ * @param vehicle
+ * @return
+ */
+ public double costOfPath(final List path, final double depTime, final Driver driver, final Vehicle vehicle){
+ if(path.isEmpty()){
+ return 0.0;
+ }
+ double cost = 0.0;
+ Iterator actIter = path.iterator();
+ TourActivity prevAct = actIter.next();
+ double startCost = 0.0;
+ cost += startCost;
+ double departureTimePrevAct = depTime;
+ while(actIter.hasNext()){
+ TourActivity act = actIter.next();
+ double transportCost = routingCosts.getTransportCost(prevAct.getLocationId(), act.getLocationId(), departureTimePrevAct, driver, vehicle);
+ double transportTime = routingCosts.getTransportTime(prevAct.getLocationId(), act.getLocationId(), departureTimePrevAct, driver, vehicle);
+ cost += transportCost;
+ double actStartTime = departureTimePrevAct + transportTime;
+ double earliestOperationStartTime = Math.max(actStartTime, act.getTheoreticalEarliestOperationStartTime());
+ double actEndTime = earliestOperationStartTime + act.getOperationTime();
+ departureTimePrevAct = actEndTime;
+ cost += activityCosts.getActivityCost(act, actStartTime, driver, vehicle);
+ prevAct = act;
+ }
+ return cost;
+ }
+
+ public double costOfPath(String startLocationId, final double startTime, final List path, String endLocationId, final Driver driver, final Vehicle vehicle){
+ if(path.isEmpty()){
+ return 0.0;
+ }
+ double cost = 0.0;
+// Iterator actIter = path.iterator();
+ String prevActLocation = startLocationId;
+// TourActivity prevAct = actIter.next();
+ double startCost = 0.0;
+ cost += startCost;
+ double departureTimePrevAct = startTime;
+ for(TourActivity act : path){
+// TourActivity act = actIter.next();
+ double transportCost = routingCosts.getTransportCost(prevActLocation, act.getLocationId(), departureTimePrevAct, driver, vehicle);
+ double transportTime = routingCosts.getTransportTime(prevActLocation, act.getLocationId(), departureTimePrevAct, driver, vehicle);
+ cost += transportCost;
+ double actStartTime = departureTimePrevAct + transportTime;
+ double earliestOperationStartTime = Math.max(actStartTime, act.getTheoreticalEarliestOperationStartTime());
+ double actEndTime = earliestOperationStartTime + act.getOperationTime();
+ departureTimePrevAct = actEndTime;
+ cost += activityCosts.getActivityCost(act, actStartTime, driver, vehicle);
+ prevActLocation = act.getLocationId();
+ }
+
+ /*
+ *!!! ENDLOCATION
+ => Start u. End können primitiv sein.
+
+ */
+
+ return cost;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/BeforeJobInsertionListener.java b/jsprit-core/src/main/java/algorithms/BeforeJobInsertionListener.java
new file mode 100644
index 00000000..e6c3f8cb
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/BeforeJobInsertionListener.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.Job;
+import basics.algo.InsertionListener;
+import basics.route.VehicleRoute;
+
+interface BeforeJobInsertionListener extends InsertionListener{
+
+ public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/BestInsertion.java b/jsprit-core/src/main/java/algorithms/BestInsertion.java
new file mode 100644
index 00000000..7522cc60
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/BestInsertion.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+import org.apache.log4j.Logger;
+
+import util.RandomNumberGeneration;
+import algorithms.InsertionData.NoInsertionFound;
+import basics.Job;
+import basics.route.VehicleRoute;
+
+
+
+/**
+ * Simplest recreation strategy. All removed customers are inserted where
+ * insertion costs are minimal. I.e. each tour-agent is asked for minimal
+ * marginal insertion costs. The tour-agent offering the lowest marginal
+ * insertion costs gets the customer/shipment.
+ *
+ * @author stefan schroeder
+ *
+ */
+
+final class BestInsertion extends AbstractInsertionStrategy{
+
+ public static BestInsertion newInstance(RouteAlgorithm routeAlgorithm){
+ return new BestInsertion(routeAlgorithm);
+ }
+
+ private static Logger logger = Logger.getLogger(BestInsertion.class);
+
+ private Random random = RandomNumberGeneration.getRandom();
+
+ private RouteAlgorithm routeAlgorithm;
+
+ private Map experimentalPreferredRoute = new HashMap();
+
+ public void setExperimentalPreferredRoute(Map experimentalPreferredRoute) {
+ this.experimentalPreferredRoute = experimentalPreferredRoute;
+ }
+
+ private boolean allowUnassignedJobs = false;
+
+ private boolean fixRouteSet = false;
+
+ private boolean minVehiclesFirst = false;
+
+ public void setFixRouteSet(boolean fixRouteSet) {
+ this.fixRouteSet = fixRouteSet;
+ }
+
+ public void setRandom(Random random) {
+ this.random = random;
+ }
+
+ public BestInsertion(RouteAlgorithm routeAlgorithm) {
+ super();
+ this.routeAlgorithm = routeAlgorithm;
+ logger.info("initialise " + this);
+ }
+
+ public RouteAlgorithm getRouteAlgorithm(){
+ return routeAlgorithm;
+ }
+
+ @Override
+ public String toString() {
+ return "[name=bestInsertion]";
+ }
+
+ @Override
+ public void run(Collection vehicleRoutes, Collection unassignedJobs, double result2beat) {
+ List unassignedJobList = new ArrayList(unassignedJobs);
+ Collections.shuffle(unassignedJobList, random);
+ informInsertionStarts(vehicleRoutes,unassignedJobs.size());
+ int inserted = 0;
+ List reasons = new ArrayList();
+ for(Job unassignedJob : unassignedJobList){
+
+ VehicleRoute insertIn = null;
+ Insertion bestInsertion = null;
+ double bestInsertionCost = Double.MAX_VALUE;
+ for(VehicleRoute vehicleRoute : vehicleRoutes){
+ InsertionData iData = routeAlgorithm.calculateBestInsertion(vehicleRoute, unassignedJob, bestInsertionCost);
+ if(iData instanceof NoInsertionFound) {
+ continue;
+ }
+ if(iData.getInsertionCost() < bestInsertionCost){
+ bestInsertion = new Insertion(vehicleRoute,iData);
+ bestInsertionCost = iData.getInsertionCost();
+ }
+ }
+ if(!minVehiclesFirst){
+ VehicleRoute newRoute = VehicleRoute.emptyRoute();
+ InsertionData newIData = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
+ if(newIData.getInsertionCost() < bestInsertionCost){
+ bestInsertion = new Insertion(newRoute,newIData);
+ bestInsertionCost = newIData.getInsertionCost();
+ vehicleRoutes.add(newRoute);
+ }
+ }
+ if(bestInsertion != null){
+ informBeforeJobInsertion(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
+ insertIn = bestInsertion.getRoute();
+// logger.debug("insert job="+unassignedJob+" at index=" + bestInsertion.getInsertionData().getInsertionIndex() + " delta cost=" + bestInsertion.getInsertionData().getInsertionCost());
+ routeAlgorithm.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
+ }
+ else {
+ if(fixRouteSet){
+ if(allowUnassignedJobs) logger.warn("cannot insert job yet " + unassignedJob);
+ else throw new IllegalStateException("given the vehicles, could not insert job\n");
+ }
+ else{
+ VehicleRoute newRoute = VehicleRoute.emptyRoute();
+ InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
+ if(bestI instanceof InsertionData.NoInsertionFound){
+ if(allowUnassignedJobs){
+ logger.warn("cannot insert job yet " + unassignedJob);
+ }
+ else {
+ for(String s : reasons){
+ System.out.println("reason="+s);
+ }
+ throw new IllegalStateException("given the vehicles, could not insert job\n" +
+ "\t" + unassignedJob + "\n\t");
+ }
+ }
+ else{
+ insertIn = newRoute;
+ informBeforeJobInsertion(unassignedJob,bestI,newRoute);
+ routeAlgorithm.insertJob(unassignedJob,bestI,newRoute);
+ vehicleRoutes.add(newRoute);
+ }
+ }
+ }
+ inserted++;
+ informJobInserted((unassignedJobList.size()-inserted), unassignedJob, insertIn);
+ }
+ informInsertionEndsListeners(vehicleRoutes);
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java b/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java
new file mode 100644
index 00000000..61cd1d27
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.apache.log4j.Logger;
+
+import util.RandomNumberGeneration;
+import algorithms.InsertionData.NoInsertionFound;
+import basics.Job;
+import basics.route.VehicleRoute;
+
+
+
+/**
+ * Simplest recreation strategy. All removed customers are inserted where
+ * insertion costs are minimal. I.e. each tour-agent is asked for minimal
+ * marginal insertion costs. The tour-agent offering the lowest marginal
+ * insertion costs gets the customer/shipment.
+ *
+ * @author stefan schroeder
+ *
+ */
+
+final class BestInsertionConcurrent extends AbstractInsertionStrategy{
+
+
+ static class Batch {
+ List routes = new ArrayList();
+
+ }
+
+ private static Logger logger = Logger.getLogger(BestInsertionConcurrent.class);
+
+ private Random random = RandomNumberGeneration.getRandom();
+
+ private RouteAlgorithm routeAlgorithm;
+
+// private ExecutorService executor;
+
+ private int nuOfBatches;
+
+ private ExecutorCompletionService completionService;
+
+ public void setRandom(Random random) {
+ this.random = random;
+ }
+
+ public BestInsertionConcurrent(RouteAlgorithm routeAlgorithm, ExecutorService executor, int nuOfThreads) {
+ super();
+ this.routeAlgorithm = routeAlgorithm;
+// this.executor = executor;
+ logger.info("initialise " + this);
+ this.nuOfBatches = nuOfThreads;
+ completionService = new ExecutorCompletionService(executor);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=concurrentBestInsertion]";
+ }
+
+ @Override
+ public void run(Collection vehicleRoutes, Collection unassignedJobs, double result2beat) {
+ List unassignedJobList = new ArrayList(unassignedJobs);
+ Collections.shuffle(unassignedJobList, random);
+ informInsertionStarts(vehicleRoutes,unassignedJobs.size());
+
+ int inserted = 0;
+ for(final Job unassignedJob : unassignedJobList){
+ VehicleRoute insertIn = null;
+ Insertion bestInsertion = null;
+ double bestInsertionCost = Double.MAX_VALUE;
+
+ List batches = distributeRoutes(vehicleRoutes,nuOfBatches);
+
+ for(final Batch batch : batches){
+ completionService.submit(new Callable() {
+
+ @Override
+ public Insertion call() throws Exception {
+ return getBestInsertion(batch,unassignedJob);
+ }
+
+ });
+
+ }
+
+ try{
+ for(int i=0;i futureIData = completionService.take();
+ Insertion insertion = futureIData.get();
+ if(insertion == null) continue;
+ if(insertion.getInsertionData().getInsertionCost() < bestInsertionCost){
+ bestInsertion = insertion;
+ bestInsertionCost = insertion.getInsertionData().getInsertionCost();
+ }
+ }
+ }
+ catch(InterruptedException e){
+ Thread.currentThread().interrupt();
+ }
+ catch (ExecutionException e) {
+ e.printStackTrace();
+ logger.error(e.getCause().toString());
+ System.exit(1);
+ }
+
+ if(bestInsertion != null){
+ informBeforeJobInsertion(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
+ insertIn = bestInsertion.getRoute();
+// logger.debug("insert job="+unassignedJob+" at index=" + bestInsertion.getInsertionData().getInsertionIndex() + " delta cost=" + bestInsertion.getInsertionData().getInsertionCost());
+ routeAlgorithm.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
+ }
+ else {
+ VehicleRoute newRoute = VehicleRoute.emptyRoute();
+ InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, unassignedJob, Double.MAX_VALUE);
+ if(bestI instanceof InsertionData.NoInsertionFound)
+ throw new IllegalStateException("given the vehicles, could not create a valid solution.\n\tthe reason might be" +
+ " inappropriate vehicle capacity.\n\tthe job that does not fit in any vehicle anymore is \n\t" + unassignedJob);
+ insertIn = newRoute;
+ informBeforeJobInsertion(unassignedJob,bestI,newRoute);
+ routeAlgorithm.insertJob(unassignedJob,bestI,newRoute);
+ vehicleRoutes.add(newRoute);
+ }
+ inserted++;
+ informJobInserted((unassignedJobList.size()-inserted), unassignedJob, insertIn);
+ }
+
+ }
+
+ private Insertion getBestInsertion(Batch batch, Job unassignedJob) {
+ Insertion bestInsertion = null;
+ double bestInsertionCost = Double.MAX_VALUE;
+ for(VehicleRoute vehicleRoute : batch.routes){
+ InsertionData iData = routeAlgorithm.calculateBestInsertion(vehicleRoute, unassignedJob, bestInsertionCost);
+ if(iData instanceof NoInsertionFound) continue;
+ if(iData.getInsertionCost() < bestInsertionCost){
+ bestInsertion = new Insertion(vehicleRoute,iData);
+ bestInsertionCost = iData.getInsertionCost();
+ }
+ }
+ return bestInsertion;
+ }
+
+ private List distributeRoutes(Collection vehicleRoutes, int nuOfBatches) {
+ List batches = new ArrayList();
+ for(int i=0;i getLatestOperationStart(nextAct)){
+ activityInsertionCosts = Double.MAX_VALUE;
+ }
+ else if(vehicleRoute.isEmpty()){
+ activityInsertionCosts = totalCosts;
+ }
+ else{
+ double oldCostOfPrevAct;
+ if(prevIsStart) oldCostOfPrevAct = 0.0;
+ else oldCostOfPrevAct = state(prevAct).getCurrentCost();
+ double oldCostOfNextAct;
+ if(nextAct instanceof End) oldCostOfNextAct = routeStates.getRouteState(vehicleRoute).getCosts();
+ else oldCostOfNextAct = state(nextAct).getCurrentCost();
+ activityInsertionCosts = (totalCosts) - (oldCostOfNextAct-oldCostOfPrevAct);
+ }
+ return activityInsertionCosts;
+ }
+
+ private ActivityState state(TourActivity act) {
+ return routeStates.getState(act);
+ }
+
+ private double getLatestOperationStart(TourActivity act) {
+ if(state(act) != null){
+ return state(act).getLatestOperationStart();
+ }
+ return act.getTheoreticalLatestOperationStartTime();
+ }
+
+ @Override
+ public String toString() {
+ return "[name=calculatesHardTimeWindowActivityInsertion]";
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertion.java b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertion.java
new file mode 100644
index 00000000..c2c133a2
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertion.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import org.apache.log4j.Logger;
+
+import util.Neighborhood;
+
+import algorithms.RouteStates.ActivityState;
+import basics.Job;
+import basics.Service;
+import basics.costs.VehicleRoutingActivityCosts;
+import basics.costs.VehicleRoutingTransportCosts;
+import basics.route.Driver;
+import basics.route.End;
+import basics.route.ServiceActivity;
+import basics.route.Start;
+import basics.route.TourActivities;
+import basics.route.TourActivity;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+import basics.route.VehicleImpl.NoVehicle;
+
+
+
+final class CalculatesServiceInsertion implements JobInsertionCalculator{
+
+ private static final Logger logger = Logger.getLogger(CalculatesServiceInsertion.class);
+
+ private RouteStates routeStates;
+
+ private VehicleRoutingTransportCosts routingCosts;
+
+ private VehicleRoutingActivityCosts activityCosts;
+
+ private Start start;
+
+ private End end;
+
+ private Neighborhood neighborhood = new Neighborhood() {
+
+ @Override
+ public boolean areNeighbors(String location1, String location2) {
+ return true;
+ }
+ };
+
+
+
+ public void setNeighborhood(Neighborhood neighborhood) {
+ this.neighborhood = neighborhood;
+ logger.info("initialise neighborhood " + neighborhood);
+ }
+
+ public void setActivityStates(RouteStates actStates){
+ this.routeStates = actStates;
+ }
+
+ public ActivityState state(TourActivity act){
+ return routeStates.getState(act);
+ }
+
+ public CalculatesServiceInsertion(VehicleRoutingTransportCosts vehicleRoutingTransportCosts, VehicleRoutingActivityCosts vehicleRoutingActivityCosts) {
+ super();
+ this.routingCosts = vehicleRoutingTransportCosts;
+ this.activityCosts = vehicleRoutingActivityCosts;
+ logger.info("initialise " + this);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=calculatesServiceInsertion]";
+ }
+
+ /**
+ * Calculates the marginal cost of inserting job i locally. This is based on the
+ * assumption that cost changes can entirely covered by only looking at the predecessor i-1 and its successor i+1.
+ *
+ */
+ @Override
+ public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownCosts) {
+ if(jobToInsert == null) throw new IllegalStateException("jobToInsert is missing.");
+ if(newVehicle == null || newVehicle instanceof NoVehicle) throw new IllegalStateException("newVehicle is missing.");
+
+ TourActivities tour = currentRoute.getTourActivities();
+ double bestCost = bestKnownCosts;
+ Service service = (Service)jobToInsert;
+
+ if(routeStates.getRouteState(currentRoute).getLoad() + service.getCapacityDemand() > newVehicle.getCapacity()){
+ return InsertionData.noInsertionFound();
+ }
+ int insertionIndex = InsertionData.NO_INDEX;
+
+ TourActivity deliveryAct2Insert = ServiceActivity.newInstance(service);
+// TourActivity deliveryAct2Insert = actStates.getActivity(service, true);
+
+ initialiseStartAndEnd(newVehicle, newVehicleDepartureTime);
+
+ TourActivity prevAct = start;
+ double prevCostInOriginalTour = 0.0;
+ int actIndex = 0;
+ for(TourActivity nextAct : tour.getActivities()){
+ double nextCostInOriginalTour = state(nextAct).getCurrentCost();
+ if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){
+ double mc = calculate(tour, prevAct, nextAct, deliveryAct2Insert, newDriver, newVehicle, bestCost, nextCostInOriginalTour - prevCostInOriginalTour);
+ if(mc < bestCost){
+ bestCost = mc;
+ insertionIndex = actIndex;
+ }
+ }
+ prevCostInOriginalTour = nextCostInOriginalTour;
+ prevAct = nextAct;
+ actIndex++;
+ }
+ End nextAct = end;
+ if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){
+ double mc = calculate(tour, prevAct, nextAct, deliveryAct2Insert, newDriver, newVehicle, bestCost, routeStates.getRouteState(currentRoute).getCosts() - prevCostInOriginalTour);
+ if(mc < bestCost){
+ bestCost = mc;
+ insertionIndex = actIndex;
+ }
+ }
+
+ if(insertionIndex == InsertionData.NO_INDEX) {
+ return InsertionData.noInsertionFound();
+ }
+ InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver);
+ insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
+ return insertionData;
+ }
+
+ private void initialiseStartAndEnd(final Vehicle newVehicle,
+ double newVehicleDepartureTime) {
+ if(start == null){
+ start = Start.newInstance(newVehicle.getLocationId(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival());
+ start.setEndTime(newVehicleDepartureTime);
+ }
+ else{
+ start.setLocationId(newVehicle.getLocationId());
+ start.setTheoreticalEarliestOperationStartTime(newVehicle.getEarliestDeparture());
+ start.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
+ start.setEndTime(newVehicleDepartureTime);
+ }
+
+ if(end == null){
+ end = End.newInstance(newVehicle.getLocationId(), 0.0, newVehicle.getLatestArrival());
+ }
+ else{
+ end.setLocationId(newVehicle.getLocationId());
+ end.setTheoreticalEarliestOperationStartTime(newVehicleDepartureTime);
+ end.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
+ }
+ }
+
+ public double calculate(TourActivities tour, TourActivity prevAct, TourActivity nextAct, TourActivity newAct, Driver driver, Vehicle vehicle, double bestKnownCosts, double costWithoutNewJob) {
+
+ double tp_costs_prevAct_newAct = routingCosts.getTransportCost(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
+ double tp_time_prevAct_newAct = routingCosts.getTransportTime(prevAct.getLocationId(), newAct.getLocationId(), prevAct.getEndTime(), driver, vehicle);
+
+ double newAct_arrTime = prevAct.getEndTime() + tp_time_prevAct_newAct;
+ double newAct_operationStartTime = Math.max(newAct_arrTime, newAct.getTheoreticalEarliestOperationStartTime());
+
+ double newAct_endTime = newAct_operationStartTime + newAct.getOperationTime();
+
+ double act_costs_newAct = activityCosts.getActivityCost(newAct, newAct_arrTime, driver, vehicle);
+
+ if((tp_costs_prevAct_newAct + act_costs_newAct - costWithoutNewJob) > bestKnownCosts){
+ return Double.MAX_VALUE;
+ }
+
+ double tp_costs_newAct_nextAct = routingCosts.getTransportCost(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
+ double tp_time_newAct_nextAct = routingCosts.getTransportTime(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, driver, vehicle);
+
+ double nextAct_arrTime = newAct_endTime + tp_time_newAct_nextAct;
+ double act_costs_nextAct = activityCosts.getActivityCost(nextAct, nextAct_arrTime, driver, vehicle);
+
+ double activityInsertionCosts;
+
+ double totalCosts = tp_costs_prevAct_newAct + tp_costs_newAct_nextAct + act_costs_newAct + act_costs_nextAct;
+
+ if(totalCosts - costWithoutNewJob > bestKnownCosts){
+ activityInsertionCosts = Double.MAX_VALUE;
+ }
+ if(nextAct_arrTime > getLatestOperationStart(nextAct)){
+ activityInsertionCosts = Double.MAX_VALUE;
+ }
+ else{
+ activityInsertionCosts = totalCosts - costWithoutNewJob;
+ }
+ return activityInsertionCosts;
+ }
+
+ private double getLatestOperationStart(TourActivity act) {
+ if(state(act) != null){
+ return state(act).getLatestOperationStart();
+ }
+ return act.getTheoreticalLatestOperationStartTime();
+ }
+}
diff --git a/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionConsideringFixCost.java b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionConsideringFixCost.java
new file mode 100644
index 00000000..3660103e
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionConsideringFixCost.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import org.apache.log4j.Logger;
+
+import algorithms.InsertionData.NoInsertionFound;
+import basics.Job;
+import basics.route.Driver;
+import basics.route.Vehicle;
+import basics.route.VehicleImpl.NoVehicle;
+import basics.route.VehicleRoute;
+
+
+
+final class CalculatesServiceInsertionConsideringFixCost implements JobInsertionCalculator{
+
+ private static final Logger logger = Logger.getLogger(CalculatesServiceInsertionConsideringFixCost.class);
+
+ private final JobInsertionCalculator standardServiceInsertion;
+
+ private double weight_deltaFixCost = 0.5;
+
+ private double solution_completeness_ratio = 0.5;
+
+ private RouteStates routeStates;
+
+ public CalculatesServiceInsertionConsideringFixCost(final JobInsertionCalculator standardInsertionCalculator, RouteStates routeStates) {
+ super();
+ this.standardServiceInsertion = standardInsertionCalculator;
+ this.routeStates = routeStates;
+ logger.info("inialise " + this);
+ }
+
+ @Override
+ public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownPrice) {
+ double relFixCost = getDeltaRelativeFixCost(currentRoute, newVehicle, jobToInsert);
+ double absFixCost = getDeltaAbsoluteFixCost(currentRoute, newVehicle, jobToInsert);
+ double deltaFixCost = (1-solution_completeness_ratio)*relFixCost + solution_completeness_ratio*absFixCost;
+ double fixcost_contribution = weight_deltaFixCost*solution_completeness_ratio*deltaFixCost;
+ if(fixcost_contribution > bestKnownPrice){
+ return InsertionData.noInsertionFound();
+ }
+ InsertionData iData = standardServiceInsertion.calculate(currentRoute, jobToInsert, newVehicle, newVehicleDepartureTime, newDriver, bestKnownPrice);
+ if(iData instanceof NoInsertionFound){
+ return iData;
+ }
+ double totalInsertionCost = iData.getInsertionCost() + fixcost_contribution;
+ InsertionData insertionData = new InsertionData(totalInsertionCost, iData.getPickupInsertionIndex(), iData.getDeliveryInsertionIndex(), newVehicle, newDriver);
+ insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
+ return insertionData;
+ }
+
+ public void setWeightOfFixCost(double weight){
+ weight_deltaFixCost = weight;
+ logger.info("set weightOfFixCostSaving to " + weight);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=calculatesServiceInsertionConsideringFixCost][weightOfFixedCostSavings="+weight_deltaFixCost+"]";
+ }
+
+ public void setSolutionCompletenessRatio(double ratio){
+ solution_completeness_ratio = ratio;
+ }
+
+ private double getDeltaAbsoluteFixCost(VehicleRoute route, Vehicle newVehicle, Job job) {
+ double load = routeStates.getRouteState(route).getLoad() + job.getCapacityDemand();
+ double currentFix = 0.0;
+ if(route.getVehicle() != null){
+ if(!(route.getVehicle() instanceof NoVehicle)){
+ currentFix += route.getVehicle().getType().vehicleCostParams.fix;
+ }
+ }
+ if(newVehicle.getCapacity() < load){
+ return Double.MAX_VALUE;
+ }
+ return newVehicle.getType().vehicleCostParams.fix - currentFix;
+ }
+
+ private double getDeltaRelativeFixCost(VehicleRoute route, Vehicle newVehicle, Job job) {
+ int currentLoad = routeStates.getRouteState(route).getLoad();
+ double load = currentLoad + job.getCapacityDemand();
+ double currentRelFix = 0.0;
+ if(route.getVehicle() != null){
+ if(!(route.getVehicle() instanceof NoVehicle)){
+ currentRelFix += route.getVehicle().getType().vehicleCostParams.fix*currentLoad/route.getVehicle().getCapacity();
+ }
+ }
+ if(newVehicle.getCapacity() < load){
+ return Double.MAX_VALUE;
+ }
+ double relativeFixCost = newVehicle.getType().vehicleCostParams.fix*(load/newVehicle.getCapacity()) - currentRelFix;
+ return relativeFixCost;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionOnRouteLevel.java b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionOnRouteLevel.java
new file mode 100644
index 00000000..366d5885
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionOnRouteLevel.java
@@ -0,0 +1,356 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.PriorityQueue;
+
+import org.apache.log4j.Logger;
+
+import util.Neighborhood;
+
+import algorithms.RouteStates.ActivityState;
+import basics.Job;
+import basics.Service;
+import basics.costs.VehicleRoutingActivityCosts;
+import basics.costs.VehicleRoutingTransportCosts;
+import basics.route.Driver;
+import basics.route.End;
+import basics.route.Start;
+import basics.route.TourActivities;
+import basics.route.TourActivity;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+import basics.route.VehicleImpl.NoVehicle;
+
+
+
+final class CalculatesServiceInsertionOnRouteLevel implements JobInsertionCalculator{
+
+ private static final Logger logger = Logger.getLogger(CalculatesServiceInsertionOnRouteLevel.class);
+
+ private final VehicleRoutingTransportCosts transportCosts;
+
+ private final VehicleRoutingActivityCosts activityCosts;
+
+ private AuxilliaryCostCalculator auxilliaryPathCostCalculator;
+
+ private RouteStates routeStates;
+
+ private int nuOfActsForwardLooking = 0;
+
+ private int memorySize = 2;
+
+ private Start start;
+
+ private End end;
+
+ private Neighborhood neighborhood = new Neighborhood() {
+
+ @Override
+ public boolean areNeighbors(String location1, String location2) {
+ return true;
+ }
+
+ };
+
+
+
+ public void setNeighborhood(Neighborhood neighborhood) {
+ this.neighborhood = neighborhood;
+ logger.info("initialise neighborhood " + neighborhood);
+ }
+
+ public void setMemorySize(int memorySize) {
+ this.memorySize = memorySize;
+ logger.info("set [solutionMemory="+memorySize+"]");
+ }
+
+ public CalculatesServiceInsertionOnRouteLevel(VehicleRoutingTransportCosts vehicleRoutingCosts, VehicleRoutingActivityCosts costFunc) {
+ super();
+ this.transportCosts = vehicleRoutingCosts;
+ this.activityCosts = costFunc;
+ auxilliaryPathCostCalculator = new AuxilliaryCostCalculator(transportCosts, activityCosts);
+ logger.info("initialise " + this);
+ }
+
+ public void setActivityStates(RouteStates actStates){
+ this.routeStates = actStates;
+ }
+
+ public ActivityState state(TourActivity act){
+ return routeStates.getState(act);
+ }
+
+
+ void setNuOfActsForwardLooking(int nOfActsForwardLooking) {
+ this.nuOfActsForwardLooking = nOfActsForwardLooking;
+ logger.info("set [forwardLooking="+nOfActsForwardLooking+"]");
+ }
+
+ @Override
+ public String toString() {
+ return "[name=calculatesServiceInsertionOnRouteLevel][solutionMemory="+memorySize+"][forwardLooking="+nuOfActsForwardLooking+"]";
+ }
+
+ /**
+ * Calculates the insertion costs of job i on route level (which is based on the assumption that inserting job i does not only
+ * have local effects but affects the entire route).
+ * Calculation is conducted by two steps. In the first step, promising insertion positions are identified by appromiximating their
+ * marginal insertion cost. In the second step, marginal cost of the best M positions are calculated exactly.
+ *
+ *
+ */
+ @Override
+ public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double best_known_insertion_costs) {
+ if(jobToInsert == null) throw new IllegalStateException("job is null. cannot calculate the insertion of a null-job.");
+ if(newVehicle == null || newVehicle instanceof NoVehicle) throw new IllegalStateException("no vehicle given. set para vehicle!");
+
+ /**
+ * map that memorizes the costs with newVehicle, which is a cost-snapshot at tour-activities.
+ */
+ Map activity2costWithNewVehicle = new HashMap();
+
+ /**
+ * priority queue that stores insertion-data by insertion-costs in ascending order.
+ */
+ PriorityQueue bestInsertionsQueue = new PriorityQueue(Math.max(2, currentRoute.getTourActivities().getActivities().size()), getComparator());
+
+ TourActivities tour = currentRoute.getTourActivities();
+ double best_insertion_costs = best_known_insertion_costs;
+ Service service = (Service)jobToInsert;
+
+ /**
+ * pre-check whether vehicle-capacity of new vehicle is sufficient to load service.
+ */
+ if(routeStates.getRouteState(currentRoute).getLoad() + service.getCapacityDemand() > newVehicle.getCapacity()){
+ return InsertionData.noInsertionFound();
+ }
+
+ /**
+ * some inis
+ */
+ TourActivity serviceAct2Insert = routeStates.getActivity(service, true);
+ int best_insertion_index = InsertionData.NO_INDEX;
+
+ initialiseStartAndEnd(newVehicle, newVehicleDepartureTime);
+
+ TourActivity prevAct = start;
+ int actIndex = 0;
+ double sumOf_prevCosts_newVehicle = 0.0;
+ double prevActDepTime_newVehicle = start.getEndTime();
+
+ /**
+ * inserting serviceAct2Insert in route r={0,1,...,i-1,i,j,j+1,...,n(r),n(r)+1}
+ * i=prevAct
+ * j=nextAct
+ * k=serviceAct2Insert
+ */
+ for(TourActivity nextAct : tour.getActivities()){
+ if(neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), nextAct.getLocationId())){
+ /**
+ * builds a path on this route forwardPath={i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking}
+ */
+ List path = new ArrayList();
+ path.add(prevAct); path.add(serviceAct2Insert); path.add(nextAct);
+ if(nuOfActsForwardLooking > 0){ path.addAll(getForwardLookingPath(currentRoute,actIndex)); }
+
+ /**
+ * calculates the path costs with new vehicle, c(forwardPath,newVehicle).
+ */
+ double forwardPathCost_newVehicle = auxilliaryPathCostCalculator.costOfPath(path, prevActDepTime_newVehicle, newDriver, newVehicle);
+
+ /**
+ * insertion_cost_approximation = c({0,1,...,i},newVehicle) + c({i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking},newVehicle) - c({0,1,...,i,j,j+1,...,j+nuOfActsForwardLooking},oldVehicle)
+ */
+ double insertion_cost_approximation = sumOf_prevCosts_newVehicle + forwardPathCost_newVehicle - pathCost_oldVehicle(currentRoute,path);
+
+ /**
+ * memorize it in insertion-queue
+ */
+ if(insertion_cost_approximation < best_known_insertion_costs){
+ bestInsertionsQueue.add(new InsertionData(insertion_cost_approximation, InsertionData.NO_INDEX, actIndex, newVehicle, newDriver));
+ }
+
+ }
+
+ /**
+ * calculate transport and activity costs with new vehicle (without inserting k)
+ */
+ double transportCost_prevAct_nextAct_newVehicle = transportCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), prevActDepTime_newVehicle, newDriver, newVehicle);
+ double transportTime_prevAct_nextAct_newVehicle = transportCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevActDepTime_newVehicle, newDriver, newVehicle);
+ double arrTime_nextAct_newVehicle = prevActDepTime_newVehicle + transportTime_prevAct_nextAct_newVehicle;
+ double activityCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct_newVehicle, newDriver, newVehicle);
+
+ /**
+ * memorize transport and activity costs with new vehicle without inserting k
+ */
+ sumOf_prevCosts_newVehicle += transportCost_prevAct_nextAct_newVehicle + activityCost_nextAct;
+ activity2costWithNewVehicle.put(nextAct, sumOf_prevCosts_newVehicle);
+
+ /**
+ * departure time at nextAct with new vehicle
+ */
+ double depTime_nextAct_newVehicle = Math.max(arrTime_nextAct_newVehicle, nextAct.getTheoreticalEarliestOperationStartTime()) + nextAct.getOperationTime();
+
+ /**
+ * set previous to next
+ */
+ prevAct = nextAct;
+ prevActDepTime_newVehicle = depTime_nextAct_newVehicle;
+
+ actIndex++;
+ }
+ End nextAct = end;
+ if(neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(serviceAct2Insert.getLocationId(), nextAct.getLocationId())){
+
+ /**
+ * calculates the path costs with new vehicle, c(forwardPath,newVehicle).
+ */
+ List path = Arrays.asList(prevAct,serviceAct2Insert,end);
+ double forwardPathCost_newVehicle = auxilliaryPathCostCalculator.costOfPath(path, prevActDepTime_newVehicle, newDriver, newVehicle);
+
+ /**
+ * insertion_cost_approximation = c({0,1,...,i},newVehicle) + c({i,k,j,j+1,j+2,...,j+nuOfActsForwardLooking},newVehicle) - c({0,1,...,i,j,j+1,...,j+nuOfActsForwardLooking},oldVehicle)
+ */
+ double insertion_cost_approximation = sumOf_prevCosts_newVehicle + forwardPathCost_newVehicle - pathCost_oldVehicle(currentRoute,path);
+
+ /**
+ * memorize it in insertion-queue
+ */
+ if(insertion_cost_approximation < best_known_insertion_costs){
+ bestInsertionsQueue.add(new InsertionData(insertion_cost_approximation,InsertionData.NO_INDEX, actIndex, newVehicle, newDriver));
+ }
+ }
+
+
+ /**
+ * the above calculations approximate insertion costs. now calculate the exact insertion costs for the most promising (according to the approximation)
+ * insertion positions.
+ *
+ */
+
+ for(int i=0;i wholeTour = new ArrayList();
+ wholeTour.add(start);
+ wholeTour.addAll(currentRoute.getTourActivities().getActivities());
+ wholeTour.add(end);
+ wholeTour.add(data.getDeliveryInsertionIndex()+1, serviceAct2Insert);
+
+ /**
+ * compute cost-diff of tour with and without new activity --> insertion_costs
+ */
+ double insertion_costs = auxilliaryPathCostCalculator.costOfPath(wholeTour, start.getEndTime(), newDriver, newVehicle) - routeStates.getRouteState(currentRoute).getCosts();
+
+ /**
+ * if better than best known, make it the best known
+ */
+ if(insertion_costs < best_insertion_costs){
+ best_insertion_index = data.getDeliveryInsertionIndex();
+ best_insertion_costs = insertion_costs;
+ }
+ }
+ if(best_insertion_index == InsertionData.NO_INDEX) return InsertionData.noInsertionFound();
+ return new InsertionData(best_insertion_costs, InsertionData.NO_INDEX, best_insertion_index, newVehicle, newDriver);
+ }
+
+ /**
+ * initialize start and end of tour.
+ *
+ * @param newVehicle
+ * @param newVehicleDepartureTime
+ */
+ private void initialiseStartAndEnd(final Vehicle newVehicle, double newVehicleDepartureTime) {
+ if(start == null){
+ start = Start.newInstance(newVehicle.getLocationId(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival());
+ start.setEndTime(newVehicleDepartureTime);
+ }
+ else{
+ start.setLocationId(newVehicle.getLocationId());
+ start.setTheoreticalEarliestOperationStartTime(newVehicle.getEarliestDeparture());
+ start.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
+ start.setEndTime(newVehicleDepartureTime);
+ }
+
+ if(end == null){
+ end = End.newInstance(newVehicle.getLocationId(), 0.0, newVehicle.getLatestArrival());
+ }
+ else{
+ end.setLocationId(newVehicle.getLocationId());
+ end.setTheoreticalEarliestOperationStartTime(newVehicleDepartureTime);
+ end.setTheoreticalLatestOperationStartTime(newVehicle.getLatestArrival());
+ }
+ }
+
+ private double pathCost_oldVehicle(VehicleRoute vehicleRoute, List path) {
+ TourActivity act = path.get(path.size()-1);
+ if(act instanceof End){
+ return routeStates.getRouteState(vehicleRoute).getCosts();
+ }
+ return state(act).getCurrentCost();
+ }
+
+ /**
+ * returns the path or the partial route r_partial = {j+1,j+2,...,j+nuOfActsForwardLooking}
+ *
+ * @param route
+ * @param actIndex
+ * @return
+ */
+ private List getForwardLookingPath(VehicleRoute route, int actIndex) {
+ List forwardLookingPath = new ArrayList();
+ int nuOfActsInPath = 0;
+ int index = actIndex + 1;
+ while(index < route.getTourActivities().getActivities().size() && nuOfActsInPath < nuOfActsForwardLooking){
+ forwardLookingPath.add(route.getTourActivities().getActivities().get(index));
+ index++;
+ nuOfActsInPath++;
+ }
+ if(nuOfActsInPath < nuOfActsForwardLooking){
+ forwardLookingPath.add(route.getEnd());
+ }
+ return forwardLookingPath;
+ }
+
+ /**
+ * creates a comparator to sort insertion-data in insertionQueue in ascending order according insertion costs.
+ * @return
+ */
+ private Comparator getComparator() {
+ return new Comparator() {
+
+ @Override
+ public int compare(InsertionData o1, InsertionData o2) {
+ if(o1.getInsertionCost() < o2.getInsertionCost()){
+ return -1;
+ }
+ else {
+ return 1;
+ }
+
+ }
+ };
+ }
+}
diff --git a/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionWithTimeScheduling.java b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionWithTimeScheduling.java
new file mode 100644
index 00000000..e1ce3079
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionWithTimeScheduling.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.log4j.Logger;
+
+import basics.Job;
+import basics.route.Driver;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+
+class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCalculator{
+
+ private static Logger log = Logger.getLogger(CalculatesServiceInsertionWithTimeScheduling.class);
+
+ private JobInsertionCalculator jic;
+
+ private Random random = new Random();
+
+ private int nOfDepartureTimes = 3;
+
+ private double timeSlice = 900.0;
+
+ public CalculatesServiceInsertionWithTimeScheduling(JobInsertionCalculator jic, double timeSlice, int neighbors) {
+ super();
+ this.jic = jic;
+ this.timeSlice = timeSlice;
+ this.nOfDepartureTimes = neighbors;
+ log.info("initialise " + this);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=calculatesServiceInsertionWithTimeScheduling][timeSlice="+timeSlice+"][#timeSlice="+nOfDepartureTimes+"]";
+ }
+
+ @Override
+ public InsertionData calculate(VehicleRoute currentRoute, Job jobToInsert, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownScore) {
+ List vehicleDepartureTimes = new ArrayList();
+ double currentStart;
+ if(currentRoute.getStart() == null){
+ currentStart = newVehicleDepartureTime;
+ }
+ else currentStart = currentRoute.getStart().getEndTime();
+
+ vehicleDepartureTimes.add(currentStart);
+ double earliestDeparture = newVehicle.getEarliestDeparture();
+ double latestEnd = newVehicle.getLatestArrival();
+
+ for(int i=0;i earliestDeparture) vehicleDepartureTimes.add(neighborStartTime_earlier);
+ double neighborStartTime_later = currentStart + (i+1)*timeSlice;
+ if(neighborStartTime_later < latestEnd) vehicleDepartureTimes.add(neighborStartTime_later);
+ }
+
+ InsertionData bestIData = null;
+ for(Double departureTime : vehicleDepartureTimes){
+ InsertionData iData = jic.calculate(currentRoute, jobToInsert, newVehicle, departureTime, newDriver, bestKnownScore);
+ if(bestIData == null) bestIData = iData;
+ else if(iData.getInsertionCost() < bestIData.getInsertionCost()){
+ iData.setVehicleDepartureTime(departureTime);
+ bestIData = iData;
+ }
+ }
+// log.info(bestIData);
+ return bestIData;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/CalculatesVehTypeDepServiceInsertion.java b/jsprit-core/src/main/java/algorithms/CalculatesVehTypeDepServiceInsertion.java
new file mode 100644
index 00000000..91bd5ebe
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/CalculatesVehTypeDepServiceInsertion.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.log4j.Logger;
+
+import algorithms.InsertionData.NoInsertionFound;
+import algorithms.VehicleFleetManager.TypeKey;
+import basics.Job;
+import basics.route.Driver;
+import basics.route.Vehicle;
+import basics.route.VehicleImpl.NoVehicle;
+import basics.route.VehicleImpl.VehicleType;
+import basics.route.VehicleRoute;
+
+
+
+final class CalculatesVehTypeDepServiceInsertion implements JobInsertionCalculator{
+
+ private Logger logger = Logger.getLogger(CalculatesVehTypeDepServiceInsertion.class);
+
+ private final VehicleFleetManager fleetManager;
+
+ private final JobInsertionCalculator insertionCalculator;
+
+ public CalculatesVehTypeDepServiceInsertion(final VehicleFleetManager fleetManager, final JobInsertionCalculator jobInsertionCalc) {
+ this.fleetManager = fleetManager;
+ this.insertionCalculator = jobInsertionCalc;
+ logger.info("inialise " + this);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=vehicleTypeDependentServiceInsertion]";
+ }
+
+ public InsertionData calculate(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle vehicle, double newVehicleDepartureTime, final Driver driver, final double bestKnownCost) {
+ Vehicle selectedVehicle = currentRoute.getVehicle();
+ Driver selectedDriver = currentRoute.getDriver();
+ InsertionData bestIData = InsertionData.noInsertionFound();
+ double bestKnownCost_ = bestKnownCost;
+ Collection relevantVehicles = new ArrayList();
+ if(!(selectedVehicle instanceof NoVehicle)) relevantVehicles.add(selectedVehicle);
+ for(TypeKey typeKey : fleetManager.getAvailableVehicleTypes()){
+ if(!(currentRoute.getVehicle() instanceof NoVehicle)){
+ TypeKey key = makeTypeKey(currentRoute.getVehicle().getType(),currentRoute.getVehicle().getLocationId());
+ if(typeKey.equals(key)){
+ continue;
+ }
+ }
+ relevantVehicles.add(fleetManager.getEmptyVehicle(typeKey));
+ }
+ for(Vehicle v : relevantVehicles){
+ double depTime = v.getEarliestDeparture();
+ InsertionData iData = insertionCalculator.calculate(currentRoute, jobToInsert, v, depTime, selectedDriver, bestKnownCost_);
+ if(iData instanceof NoInsertionFound) {
+ if(bestIData instanceof NoInsertionFound) bestIData = iData;
+ continue;
+ }
+ if(iData.getInsertionCost() < bestKnownCost_){
+ bestIData = iData;
+ bestKnownCost_ = iData.getInsertionCost();
+ }
+ }
+ return bestIData;
+ }
+
+ private TypeKey makeTypeKey(VehicleType type, String locationId) {
+ return new TypeKey(type,locationId);
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/CalculatorBuilder.java b/jsprit-core/src/main/java/algorithms/CalculatorBuilder.java
new file mode 100644
index 00000000..ec278889
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/CalculatorBuilder.java
@@ -0,0 +1,268 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.commons.configuration.XMLConfiguration;
+
+import util.NeighborhoodImpl;
+
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblem.FleetComposition;
+import basics.algo.InsertionListener;
+import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
+import basics.algo.VehicleRoutingAlgorithmListeners.Priority;
+import basics.costs.VehicleRoutingActivityCosts;
+
+
+
+class CalculatorBuilder {
+
+ private static class CalculatorPlusListeners {
+
+ private JobInsertionCalculator calculator;
+
+ public JobInsertionCalculator getCalculator() {
+ return calculator;
+ }
+
+ private List algorithmListener = new ArrayList();
+ private List insertionListener = new ArrayList();
+
+ public CalculatorPlusListeners(JobInsertionCalculator calculator) {
+ super();
+ this.calculator = calculator;
+ }
+
+ public List getAlgorithmListener() {
+ return algorithmListener;
+ }
+
+ public List getInsertionListener() {
+ return insertionListener;
+ }
+ }
+
+ private List insertionListeners;
+
+ private List algorithmListeners;
+
+ private VehicleRoutingProblem vrp;
+
+ private RouteStates activityStates;
+
+ private boolean local = true;
+
+ private int forwardLooking = 0;
+
+ private int memory = 1;
+
+ private boolean considerFixedCost = false;
+
+ private double weightOfFixedCost = 0;
+
+ private VehicleFleetManager fleetManager;
+
+ private boolean timeScheduling;
+
+ private double timeSlice;
+
+ private int neighbors;
+
+ /**
+ * Constructs the builder.
+ *
+ * Some calculators require information from the overall algorithm or the higher-level insertion procedure. Thus listeners inform them.
+ * These listeners are cached in the according list and can thus be added when its time to add them.
+ *
+ * @param insertionListeners
+ * @param algorithmListeners
+ */
+ public CalculatorBuilder(List insertionListeners, List algorithmListeners) {
+ super();
+ this.insertionListeners = insertionListeners;
+ this.algorithmListeners = algorithmListeners;
+ }
+
+ /**
+ * Sets activityStates. MUST be set.
+ *
+ * @param activityStates
+ * @return
+ */
+ public CalculatorBuilder setActivityStates(RouteStates activityStates){
+ this.activityStates = activityStates;
+ return this;
+ }
+
+ /**
+ * Sets routingProblem. MUST be set.
+ *
+ * @param vehicleRoutingProblem
+ * @return
+ */
+ public CalculatorBuilder setVehicleRoutingProblem(VehicleRoutingProblem vehicleRoutingProblem){
+ this.vrp = vehicleRoutingProblem;
+ return this;
+ }
+
+ /**
+ * Sets fleetManager. MUST be set.
+ *
+ * @param fleetManager
+ * @return
+ */
+ public CalculatorBuilder setVehicleFleetManager(VehicleFleetManager fleetManager){
+ this.fleetManager = fleetManager;
+ return this;
+ }
+
+ /**
+ * Sets a flag to build a calculator based on local calculations.
+ *
+ * Insertion of a job and job-activity is evaluated based on the previous and next activity.
+ */
+ public void setLocalLevel(){
+ local = true;
+ }
+
+ /**
+ * Sets a flag to build a calculator that evaluates job insertion on route-level.
+ *
+ * @param forwardLooking
+ * @param memory
+ */
+ public void setRouteLevel(int forwardLooking, int memory){
+ local = false;
+ this.forwardLooking = forwardLooking;
+ this.memory = memory;
+ }
+
+ /**
+ * Sets a flag to consider also fixed-cost when evaluating the insertion of a job. The weight of the fixed-cost can be determined by setting
+ * weightofFixedCosts.
+ *
+ * @param weightOfFixedCosts
+ */
+ public void considerFixedCosts(double weightOfFixedCosts){
+ considerFixedCost = true;
+ this.weightOfFixedCost = weightOfFixedCosts;
+ }
+
+ public void experimentalTimeScheduler(double timeSlice, int neighbors){
+ timeScheduling = true;
+ this.timeSlice = timeSlice;
+ this.neighbors = neighbors;
+ }
+
+ /**
+ * Builds the jobInsertionCalculator.
+ *
+ * @return jobInsertionCalculator.
+ * @throws IllegalStateException if vrp == null or activityStates == null or fleetManager == null.
+ */
+ public JobInsertionCalculator build(){
+ if(vrp == null) throw new IllegalStateException("vehicle-routing-problem is null, but it must be set (this.setVehicleRoutingProblem(vrp))");
+ if(activityStates == null) throw new IllegalStateException("activity states is null, but is must be set (this.setActivityStates(states))");
+ if(fleetManager == null) throw new IllegalStateException("fleetManager is null, but it must be set (this.setVehicleFleetManager(fleetManager))");
+ JobInsertionCalculator baseCalculator = null;
+ CalculatorPlusListeners standardLocal = null;
+ if(local){
+ standardLocal = createStandardLocal(vrp, activityStates);
+ }
+ else{
+ standardLocal = createStandardRoute(vrp, activityStates,forwardLooking,memory);
+ }
+ baseCalculator = standardLocal.getCalculator();
+ addAlgorithmListeners(standardLocal.getAlgorithmListener());
+ addInsertionListeners(standardLocal.getInsertionListener());
+ if(considerFixedCost){
+ CalculatorPlusListeners withFixed = createCalculatorConsideringFixedCosts(vrp, baseCalculator, activityStates, weightOfFixedCost);
+ baseCalculator = withFixed.getCalculator();
+ addAlgorithmListeners(withFixed.getAlgorithmListener());
+ addInsertionListeners(withFixed.getInsertionListener());
+ }
+ if(timeScheduling){
+ baseCalculator = new CalculatesServiceInsertionWithTimeScheduling(baseCalculator,timeSlice,neighbors);
+ }
+ return createFinalInsertion(fleetManager, baseCalculator, activityStates);
+ }
+
+ private void addInsertionListeners(List list) {
+ for(InsertionListener iL : list){
+ insertionListeners.add(iL);
+ }
+ }
+
+ private void addAlgorithmListeners(List list) {
+ for(PrioritizedVRAListener aL : list){
+ algorithmListeners.add(aL);
+ }
+ }
+
+ private CalculatorPlusListeners createStandardLocal(VehicleRoutingProblem vrp, RouteStates activityStates){
+ JobInsertionCalculator standardServiceInsertion = new CalculatesServiceInsertion(vrp.getTransportCosts(), vrp.getActivityCosts());
+ ((CalculatesServiceInsertion) standardServiceInsertion).setActivityStates(activityStates);
+ ((CalculatesServiceInsertion) standardServiceInsertion).setNeighborhood(vrp.getNeighborhood());
+ CalculatorPlusListeners calcPlusListeners = new CalculatorPlusListeners(standardServiceInsertion);
+
+ return calcPlusListeners;
+ }
+
+ private CalculatorPlusListeners createCalculatorConsideringFixedCosts(VehicleRoutingProblem vrp, JobInsertionCalculator baseCalculator, RouteStates activityStates, double weightOfFixedCosts){
+ final CalculatesServiceInsertionConsideringFixCost withFixCost = new CalculatesServiceInsertionConsideringFixCost(baseCalculator, activityStates);
+ withFixCost.setWeightOfFixCost(weightOfFixedCosts);
+ CalculatorPlusListeners calcPlusListeners = new CalculatorPlusListeners(withFixCost);
+ calcPlusListeners.getInsertionListener().add(new ConfigureFixCostCalculator(vrp, withFixCost));
+ return calcPlusListeners;
+ }
+
+ private CalculatorPlusListeners createStandardRoute(VehicleRoutingProblem vrp, RouteStates activityStates, int forwardLooking, int solutionMemory){
+ int after = forwardLooking;
+ JobInsertionCalculator jobInsertionCalculator = new CalculatesServiceInsertionOnRouteLevel(vrp.getTransportCosts(), vrp.getActivityCosts());
+ ((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setNuOfActsForwardLooking(after);
+ ((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setMemorySize(solutionMemory);
+ ((CalculatesServiceInsertionOnRouteLevel)jobInsertionCalculator).setNeighborhood(vrp.getNeighborhood());
+ ((CalculatesServiceInsertionOnRouteLevel) jobInsertionCalculator).setActivityStates(activityStates);
+ CalculatorPlusListeners calcPlusListener = new CalculatorPlusListeners(jobInsertionCalculator);
+ return calcPlusListener;
+ }
+
+ private JobInsertionCalculator createFinalInsertion(VehicleFleetManager fleetManager, JobInsertionCalculator baseCalc, RouteStates routeStates){
+ return new CalculatesVehTypeDepServiceInsertion(fleetManager, baseCalc);
+ }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jsprit-core/src/main/java/algorithms/ConfigureFixCostCalculator.java b/jsprit-core/src/main/java/algorithms/ConfigureFixCostCalculator.java
new file mode 100644
index 00000000..179164da
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/ConfigureFixCostCalculator.java
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+
+import java.util.Collection;
+
+import org.apache.log4j.Logger;
+
+import basics.Job;
+import basics.VehicleRoutingProblem;
+import basics.algo.InsertionStartsListener;
+import basics.algo.JobInsertedListener;
+import basics.route.VehicleRoute;
+
+
+
+
+
+final class ConfigureFixCostCalculator implements InsertionStartsListener, JobInsertedListener{
+
+ private static Logger log = Logger.getLogger(ConfigureFixCostCalculator.class);
+
+ VehicleRoutingProblem vrp;
+
+ CalculatesServiceInsertionConsideringFixCost calcConsideringFix;
+
+ public ConfigureFixCostCalculator(VehicleRoutingProblem vrp, CalculatesServiceInsertionConsideringFixCost calcConsideringFix) {
+ super();
+ this.vrp = vrp;
+ this.calcConsideringFix = calcConsideringFix;
+ }
+
+ @Override
+ public String toString() {
+ return "[name=configureFixCostCalculator]";
+ }
+
+ @Override
+ public void informInsertionStarts(Collection routes, int nOfJobs2Recreate) {
+ double completenessRatio = (1-((double)nOfJobs2Recreate/(double)vrp.getJobs().values().size()));
+ calcConsideringFix.setSolutionCompletenessRatio(completenessRatio);
+// log.debug("initialise completenessRatio to " + completenessRatio);
+ }
+
+ @Override
+ public void informJobInserted(int nOfJobsStill2Recreate, Job job2insert, VehicleRoute insertedIn) {
+ double completenessRatio = (1-((double)nOfJobsStill2Recreate/(double)vrp.getJobs().values().size()));
+ calcConsideringFix.setSolutionCompletenessRatio(completenessRatio);
+// log.debug("set completenessRatio to " + completenessRatio);
+ }
+}
diff --git a/jsprit-core/src/main/java/algorithms/CreateInitialSolution.java b/jsprit-core/src/main/java/algorithms/CreateInitialSolution.java
new file mode 100644
index 00000000..dcaee915
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/CreateInitialSolution.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+/* *********************************************************************** *
+ * project: org.matsim.*
+ * IniSolution.java
+ * *
+ * *********************************************************************** *
+ * *
+ * copyright : (C) 2011 by the members listed in the COPYING, *
+ * LICENSE and WARRANTY file. *
+ * email : info at matsim dot org *
+ * *
+ * *********************************************************************** *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * See also COPYING, LICENSE and WARRANTY file *
+ * *
+ * *********************************************************************** */
+
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import basics.Job;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.route.DriverImpl;
+import basics.route.TourActivities;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+
+
+
+final class CreateInitialSolution implements InitialSolutionFactory {
+
+ private static final Logger logger = Logger.getLogger(CreateInitialSolution.class);
+
+ private final AbstractInsertionStrategy insertion;
+
+ private boolean generateAsMuchAsRoutesAsVehiclesExist = false;
+
+ public void setGenerateAsMuchAsRoutesAsVehiclesExist(boolean generateAsMuchAsRoutesAsVehiclesExist) {
+ this.generateAsMuchAsRoutesAsVehiclesExist = generateAsMuchAsRoutesAsVehiclesExist;
+ }
+
+ public CreateInitialSolution(AbstractInsertionStrategy insertion) {
+ super();
+ this.insertion = insertion;
+ }
+
+ @Override
+ public VehicleRoutingProblemSolution createInitialSolution(final VehicleRoutingProblem vrp) {
+ logger.info("create initial solution.");
+ List vehicleRoutes = new ArrayList();
+ if(generateAsMuchAsRoutesAsVehiclesExist){
+ for(Vehicle vehicle : vrp.getVehicles()){
+ vehicleRoutes.add(VehicleRoute.newInstance(TourActivities.emptyTour(), DriverImpl.noDriver(), vehicle));
+ }
+ }
+ insertion.run(vehicleRoutes, getUnassignedJobs(vrp), Double.MAX_VALUE);
+ double totalCost = getTotalCost(vehicleRoutes);
+ logger.info("creation done");
+ return new VehicleRoutingProblemSolution(vehicleRoutes, totalCost);
+ }
+
+ private double getTotalCost(List serviceProviders) {
+ double c = 0.0;
+ for(VehicleRoute a : serviceProviders){
+ c += a.getCost();
+ }
+ return c;
+ }
+
+ private List getUnassignedJobs(VehicleRoutingProblem vrp) {
+ List jobs = new ArrayList(vrp.getJobs().values());
+ return jobs;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/FindCheaperVehicle.java b/jsprit-core/src/main/java/algorithms/FindCheaperVehicle.java
new file mode 100644
index 00000000..9a19f579
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/FindCheaperVehicle.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import basics.algo.InsertionStartsListener;
+import basics.route.VehicleRoute;
+
+class FindCheaperVehicle implements InsertionStartsListener{
+
+ FindCheaperVehicleAlgo findCheaperVehicle;
+
+ public FindCheaperVehicle(FindCheaperVehicleAlgo findCheaperVehicle) {
+ super();
+ this.findCheaperVehicle = findCheaperVehicle;
+ }
+
+ @Override
+ public void informInsertionStarts(Collection vehicleRoutes, int nOfJobs2Recreate) {
+ List newRoutes = new ArrayList();
+ for(VehicleRoute route : vehicleRoutes){
+ if(route.isEmpty()) continue;
+ VehicleRoute cheaperRoute = findCheaperVehicle.runAndGetVehicleRoute(route);
+ newRoutes.add(cheaperRoute);
+ }
+ vehicleRoutes.clear();
+ vehicleRoutes.addAll(newRoutes);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=findCheaperVehicle]";
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/FindCheaperVehicleAlgo.java b/jsprit-core/src/main/java/algorithms/FindCheaperVehicleAlgo.java
new file mode 100644
index 00000000..ca3415ef
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/FindCheaperVehicleAlgo.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import algorithms.VehicleFleetManager.TypeKey;
+import basics.route.TourActivities;
+import basics.route.TourActivity;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+import basics.route.VehicleImpl.NoVehicle;
+
+
+
+final class FindCheaperVehicleAlgo {
+
+ private static Logger log = Logger.getLogger(FindCheaperVehicleAlgo.class);
+
+ private VehicleFleetManager fleetManager;
+
+ private VehicleRouteUpdater tourStateCalculator;
+
+ private AuxilliaryCostCalculator auxilliaryCostCalculator;
+
+ private double weightFixCosts = 1.0;
+
+ private RouteStates states;
+
+ public void setWeightFixCosts(double weightFixCosts) {
+ this.weightFixCosts = weightFixCosts;
+ }
+
+ public void setStates(RouteStates states) {
+ this.states = states;
+ }
+
+ public FindCheaperVehicleAlgo(VehicleFleetManager fleetManager, VehicleRouteUpdater tourStateCalculator, AuxilliaryCostCalculator auxilliaryCostCalculator) {
+ super();
+ this.fleetManager = fleetManager;
+ this.tourStateCalculator = tourStateCalculator;
+ this.auxilliaryCostCalculator = auxilliaryCostCalculator;
+ }
+
+
+ public VehicleRoute runAndGetVehicleRoute(VehicleRoute vehicleRoute) {
+ if(vehicleRoute.getVehicle() instanceof NoVehicle){
+ return vehicleRoute;
+ }
+ if(vehicleRoute.getTourActivities() == null || vehicleRoute.getVehicle() == null){
+ return vehicleRoute;
+ }
+ Collection availableVehicleTypes = fleetManager.getAvailableVehicleTypes(new TypeKey(vehicleRoute.getVehicle().getType(),vehicleRoute.getVehicle().getLocationId()));
+ double bestSaving = 0.0;
+ Vehicle bestVehicle = null;
+ List path = new ArrayList();
+ path.add(vehicleRoute.getStart());
+ path.addAll(vehicleRoute.getTourActivities().getActivities());
+ path.add(vehicleRoute.getEnd());
+
+ for(TypeKey vehicleType : availableVehicleTypes){
+ Vehicle vehicle = fleetManager.getEmptyVehicle(vehicleType);
+ if(vehicle.getType().typeId.equals(vehicleRoute.getVehicle().getType().typeId)){
+ continue;
+ }
+ if(states.getRouteState(vehicleRoute).getLoad() <= vehicle.getCapacity()){
+ double fixCostSaving = vehicleRoute.getVehicle().getType().vehicleCostParams.fix - vehicle.getType().vehicleCostParams.fix;
+ double departureTime = vehicleRoute.getStart().getEndTime();
+ double newCost = auxilliaryCostCalculator.costOfPath(path, departureTime, vehicleRoute.getDriver(), vehicle);
+ double varCostSaving = states.getRouteState(vehicleRoute).getCosts() - newCost;
+ double totalCostSaving = varCostSaving + weightFixCosts*fixCostSaving;
+ if(totalCostSaving > bestSaving){
+ bestSaving = totalCostSaving;
+ bestVehicle = vehicle;
+ }
+ }
+ }
+ if(bestVehicle != null){
+ try{
+ fleetManager.unlock(vehicleRoute.getVehicle());
+ fleetManager.lock(bestVehicle);
+ }
+ catch(IllegalStateException e){
+ throw new IllegalStateException(e);
+ }
+ TourActivities newTour = TourActivities.copyOf(vehicleRoute.getTourActivities());
+ tourStateCalculator.updateRoute(vehicleRoute);
+ return VehicleRoute.newInstance(newTour,vehicleRoute.getDriver(),bestVehicle);
+ }
+ return vehicleRoute;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/GendreauPostOpt.java b/jsprit-core/src/main/java/algorithms/GendreauPostOpt.java
new file mode 100644
index 00000000..c7560cee
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/GendreauPostOpt.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+import basics.Job;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.algo.SearchStrategyModule;
+import basics.algo.SearchStrategyModuleListener;
+import basics.route.TourActivity;
+import basics.route.VehicleRoute;
+import basics.route.TourActivity.JobActivity;
+
+import util.RandomNumberGeneration;
+
+final class GendreauPostOpt implements SearchStrategyModule{
+
+ private final static Logger log = Logger.getLogger(GendreauPostOpt.class);
+
+ private final static String NAME = "gendreauPostOpt";
+
+ private final RuinStrategy ruin;
+
+ private final VehicleRoutingProblem vrp;
+
+ private final AbstractInsertionStrategy insertionStrategy;
+
+ private final RouteAlgorithm routeAlgorithm;
+
+ private VehicleFleetManager fleetManager;
+
+ private Random random = RandomNumberGeneration.getRandom();
+
+ private int nOfIterations = 10;
+
+ private double shareOfJobsToRuin = 0.15;
+
+ public void setShareOfJobsToRuin(double shareOfJobsToRuin) {
+ this.shareOfJobsToRuin = shareOfJobsToRuin;
+ }
+
+ public GendreauPostOpt(VehicleRoutingProblem vrp, RuinStrategy ruin, AbstractInsertionStrategy insertionStrategy) {
+ super();
+ this.routeAlgorithm = insertionStrategy.getRouteAlgorithm();
+ this.ruin = ruin;
+ this.vrp = vrp;
+ this.insertionStrategy = insertionStrategy;
+ }
+
+ @Override
+ public String toString() {
+ return "[name=gendreauPostOpt][iterations="+nOfIterations+"][share2ruin="+shareOfJobsToRuin+"]";
+ }
+
+ public void setRandom(Random random) {
+ this.random = random;
+ }
+
+
+ public void setNuOfIterations(int nOfIterations) {
+ this.nOfIterations = nOfIterations;
+ }
+
+ public void setFleetManager(VehicleFleetManager vehicleFleetManager) {
+ this.fleetManager = vehicleFleetManager;
+
+ }
+
+ @Override
+ public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
+// log.info("run gendreau postopt");
+ VehicleRoutingProblemSolution bestSolution = vrpSolution;
+ int itersWithoutImprovement = 0;
+
+ for(int i=0;i copiedRoutes = copyRoutes(bestSolution.getRoutes());
+ iniFleet(copiedRoutes);
+
+ VehicleRoute route2split = pickRouteThatHasAtLeastTwoJobs(copiedRoutes);
+ if(route2split == null) continue;
+ List jobsInRoute = getJobs(route2split);
+ Set unassignedJobs = new HashSet();
+ unassignedJobs.addAll(jobsInRoute);
+ copiedRoutes.remove(route2split);
+
+ Collections.shuffle(jobsInRoute,random);
+ Job targetJob = jobsInRoute.get(0);
+ int nOfJobs2BeRemovedAdditionally = (int) (shareOfJobsToRuin*(double)vrp.getJobs().size());
+ Collection unassignedJobsList = ruin.ruin(copiedRoutes, targetJob, nOfJobs2BeRemovedAdditionally);
+ unassignedJobs.addAll(unassignedJobsList);
+
+ VehicleRoute emptyRoute1 = VehicleRoute.emptyRoute();
+ copiedRoutes.add(emptyRoute1);
+ routeAlgorithm.insertJob(targetJob, routeAlgorithm.calculateBestInsertion(emptyRoute1, targetJob, Double.MAX_VALUE), emptyRoute1);
+ unassignedJobs.remove(targetJob);
+
+ VehicleRoute emptyRoute2 = VehicleRoute.emptyRoute();
+ copiedRoutes.add(emptyRoute2);
+ Job job2 = jobsInRoute.get(1);
+ routeAlgorithm.insertJob(job2, routeAlgorithm.calculateBestInsertion(emptyRoute2, job2, Double.MAX_VALUE), emptyRoute2);
+ unassignedJobs.remove(job2);
+
+ insertionStrategy.run(copiedRoutes, unassignedJobs, Double.MAX_VALUE);
+ double cost = getCost(copiedRoutes);
+
+ if(cost < bestSolution.getCost()){
+// log.info("BING - new: " + cost + " old: " + bestSolution.getCost());
+ bestSolution = new VehicleRoutingProblemSolution(copiedRoutes, cost);
+ itersWithoutImprovement=0;
+ }
+ else{
+ itersWithoutImprovement++;
+ if(itersWithoutImprovement > 200){
+// log.info("BREAK i="+i);
+ break;
+ }
+ }
+ }
+ return bestSolution;
+ }
+
+ private List copyRoutes(Collection routes) {
+ List routeList = new ArrayList();
+ for(VehicleRoute r : routes){
+ routeList.add(VehicleRoute.copyOf(r));
+ }
+ return routeList;
+ }
+
+ private void iniFleet(Collection routes) {
+ fleetManager.unlockAll();
+ for(VehicleRoute route : routes){
+ if(!route.isEmpty()){
+ fleetManager.lock(route.getVehicle());
+ }
+ }
+ }
+
+ private double getCost(Collection routes) {
+ double c = 0.0;
+ for(VehicleRoute r : routes){
+ c+=r.getCost();
+ }
+ return c;
+ }
+
+ private List getJobs(VehicleRoute route2split) {
+ Set jobs = new HashSet();
+ for(TourActivity act : route2split.getTourActivities().getActivities()){
+ if(act instanceof JobActivity){
+ jobs.add(((JobActivity) act).getJob());
+ }
+ }
+ return new ArrayList(jobs);
+ }
+
+ private VehicleRoute pickRouteThatHasAtLeastTwoJobs(Collection routeList) {
+ List routes = new ArrayList();
+ for(VehicleRoute r : routeList){
+ if(getJobs(r).size() > 1){
+ routes.add(r);
+ }
+ }
+ if(routes.isEmpty()) return null;
+ Collections.shuffle(routes,random);
+ return routes.get(0);
+ }
+
+ @Override
+ public String getName() {
+ return NAME;
+ }
+
+ @Override
+ public void addModuleListener(SearchStrategyModuleListener moduleListener) {
+ // TODO Auto-generated method stub
+
+ }
+}
diff --git a/jsprit-core/src/main/java/algorithms/InfiniteVehicles.java b/jsprit-core/src/main/java/algorithms/InfiniteVehicles.java
new file mode 100644
index 00000000..b214854d
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/InfiniteVehicles.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+import basics.route.Vehicle;
+import basics.route.VehicleImpl.VehicleType;
+
+class InfiniteVehicles implements VehicleFleetManager{
+
+ static class TypeKeyComparator implements Comparator{
+
+ @Override
+ public int compare(TypeKey k1, TypeKey k2) {
+ double k1_fix = k1.type.getVehicleCostParams().fix;
+ double k2_fix = k2.type.getVehicleCostParams().fix;
+ return (int)(k1_fix - k2_fix);
+ }
+
+ }
+
+ private static Logger logger = Logger.getLogger(InfiniteVehicles.class);
+
+ private Map types = new HashMap();
+
+ private List sortedTypes = new ArrayList();
+
+ public InfiniteVehicles(Collection vehicles){
+ extractTypes(vehicles);
+ logger.info("initialise " + this);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=infiniteVehicle]";
+ }
+
+ private void extractTypes(Collection vehicles) {
+ for(Vehicle v : vehicles){
+ TypeKey typeKey = new TypeKey(v.getType(),v.getLocationId());
+ types.put(typeKey,v);
+ sortedTypes.add(typeKey);
+
+ }
+ Collections.sort(sortedTypes, new TypeKeyComparator());
+ }
+
+
+ @Override
+ public Vehicle getEmptyVehicle(TypeKey typeId) {
+ return types.get(typeId);
+ }
+
+ @Override
+ public Collection getAvailableVehicleTypes() {
+ return sortedTypes;
+ }
+
+ @Override
+ public void lock(Vehicle vehicle) {
+
+ }
+
+ @Override
+ public void unlock(Vehicle vehicle) {
+
+ }
+
+ @Override
+ public Collection getAvailableVehicleTypes(TypeKey withoutThisType) {
+ Set typeSet = new HashSet(types.keySet());
+ typeSet.remove(withoutThisType);
+ return typeSet;
+ }
+
+ @Override
+ public boolean isLocked(Vehicle vehicle) {
+ return false;
+ }
+
+ @Override
+ public void unlockAll() {
+
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/InitialSolutionFactory.java b/jsprit-core/src/main/java/algorithms/InitialSolutionFactory.java
new file mode 100644
index 00000000..ed1a1d8c
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/InitialSolutionFactory.java
@@ -0,0 +1,24 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+
+
+
+interface InitialSolutionFactory {
+
+ public VehicleRoutingProblemSolution createInitialSolution(VehicleRoutingProblem vrp);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/InsertionData.java b/jsprit-core/src/main/java/algorithms/InsertionData.java
new file mode 100644
index 00000000..ddd6507f
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/InsertionData.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.route.Driver;
+import basics.route.Vehicle;
+
+
+
+class InsertionData {
+
+ static class NoInsertionFound extends InsertionData{
+
+ public NoInsertionFound() {
+ super(Double.MAX_VALUE, NO_INDEX, NO_INDEX, null, null);
+ }
+
+ }
+
+ private static InsertionData noInsertion = new NoInsertionFound();
+
+ public static InsertionData noInsertionFound(){
+ return noInsertion;
+ }
+
+ static int NO_INDEX = -1;
+
+ private final double insertionCost;
+
+ private final int pickupInsertionIndex;
+
+ private final int deliveryInsertionIndex;
+
+ private final Vehicle selectedVehicle;
+
+ private final Driver selectedDriver;
+
+ private double departureTime;
+
+ public InsertionData(double insertionCost, int pickupInsertionIndex, int deliveryInsertionIndex, Vehicle vehicle, Driver driver){
+ this.insertionCost = insertionCost;
+ this.pickupInsertionIndex = pickupInsertionIndex;
+ this.deliveryInsertionIndex = deliveryInsertionIndex;
+ this.selectedVehicle = vehicle;
+ this.selectedDriver = driver;
+ }
+
+ @Override
+ public String toString() {
+ return "[iCost="+insertionCost+"][iIndex="+deliveryInsertionIndex+"][depTime="+departureTime+"][vehicle="+selectedVehicle+"][driver="+selectedDriver+"]";
+ }
+
+ public int getDeliveryInsertionIndex(){
+ return deliveryInsertionIndex;
+ }
+
+ public int getPickupInsertionIndex(){
+ return pickupInsertionIndex;
+ }
+
+ public double getInsertionCost() {
+ return insertionCost;
+ }
+
+ public Vehicle getSelectedVehicle() {
+ return selectedVehicle;
+ }
+
+ public Driver getSelectedDriver(){
+ return selectedDriver;
+ }
+
+ /**
+ * @return the departureTime
+ */
+ public double getVehicleDepartureTime() {
+ return departureTime;
+ }
+
+ /**
+ * @param departureTime the departureTime to set
+ */
+ public void setVehicleDepartureTime(double departureTime) {
+ this.departureTime = departureTime;
+ }
+
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/InsertionFactory.java b/jsprit-core/src/main/java/algorithms/InsertionFactory.java
new file mode 100644
index 00000000..5de8a1f4
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/InsertionFactory.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.log4j.Logger;
+
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblem.FleetComposition;
+import basics.algo.InsertionListener;
+import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
+
+class InsertionFactory {
+
+ private static Logger log = Logger.getLogger(InsertionFactory.class);
+
+ public static AbstractInsertionStrategy createInsertion(VehicleRoutingProblem vrp, HierarchicalConfiguration config,
+ VehicleFleetManager vehicleFleetManager, RouteStates activityStates, List algorithmListeners){
+
+ if(config.containsKey("[@name]")){
+ String insertionName = config.getString("[@name]");
+ if(!insertionName.equals("bestInsertion") && !insertionName.equals("regretInsertion")){
+ new IllegalStateException(insertionName + " is not supported. use either \"bestInsertion\" or \"regretInsertion\"");
+ }
+ AbstractInsertionStrategy insertionStrategy = null;
+ List insertionListeners = new ArrayList();
+ List algoListeners = new ArrayList();
+
+ CalculatorBuilder calcBuilder = new CalculatorBuilder(insertionListeners, algorithmListeners);
+ calcBuilder.setActivityStates(activityStates);
+ calcBuilder.setVehicleRoutingProblem(vrp);
+ calcBuilder.setVehicleFleetManager(vehicleFleetManager);
+
+ if(config.containsKey("level")){
+ String level = config.getString("level");
+ if(level.equals("local")){
+ calcBuilder.setLocalLevel();
+ }
+ else if(level.equals("route")){
+ int forwardLooking = 0;
+ int memory = 1;
+ String forward = config.getString("level[@forwardLooking]");
+ String mem = config.getString("level[@memory]");
+ if(forward != null) forwardLooking = Integer.parseInt(forward);
+ else log.warn("parameter route[@forwardLooking] is missing. by default it is 0 which equals to local level");
+ if(mem != null) memory = Integer.parseInt(mem);
+ else log.warn("parameter route[@memory] is missing. by default it is 1");
+ calcBuilder.setRouteLevel(forwardLooking, memory);
+ }
+ else throw new IllegalStateException("level " + level + " is not known. currently it only knows \"local\" or \"route\"");
+ }
+ else calcBuilder.setLocalLevel();
+
+ if(config.containsKey("considerFixedCosts") || config.containsKey("considerFixedCost")){
+ String val = config.getString("considerFixedCosts");
+ if(val == null) val = config.getString("considerFixedCost");
+ if(val.equals("true")){
+ double fixedCostWeight = 0.5;
+ String weight = config.getString("considerFixedCosts[@weight]");
+ if(weight == null) weight = config.getString("considerFixedCost[@weight]");
+ if(weight != null) fixedCostWeight = Double.parseDouble(weight);
+ else log.warn("parameter considerFixedCosts[@weight] is missing. by default, it is 0.5.");
+ calcBuilder.considerFixedCosts(fixedCostWeight);
+ }
+ }
+ String timeSliceString = config.getString("experimental[@timeSlice]");
+ String neighbors = config.getString("experimental[@neighboringSlices]");
+ if(timeSliceString != null && neighbors != null){
+ calcBuilder.experimentalTimeScheduler(Double.parseDouble(timeSliceString),Integer.parseInt(neighbors));
+ }
+
+ JobInsertionCalculator jic = calcBuilder.build();
+ TourStateUpdater tourStateCalculator = new TourStateUpdater(activityStates, vrp.getTransportCosts(), vrp.getActivityCosts());
+ RouteAlgorithm routeAlgorithm = RouteAlgorithmImpl.newInstance(jic, tourStateCalculator);
+ routeAlgorithm.getListeners().add(new VehicleSwitched(vehicleFleetManager));
+ ((RouteAlgorithmImpl) routeAlgorithm).setActivityStates(activityStates);
+
+ if(insertionName.equals("bestInsertion")){
+ insertionStrategy = BestInsertion.newInstance(routeAlgorithm);
+ }
+ else if(insertionName.equals("regretInsertion")){
+ insertionStrategy = RegretInsertion.newInstance(routeAlgorithm);
+ }
+// else if(insertionName.equals("concurrentBestInsertion")){
+// String processorsString = config.getString("[@processors]");
+// int processors = 1;
+// if(processorsString != null) processors = Integer.parseInt(processorsString);
+//// BestInsertionConcurrent.newInstance(routeAlgorithm,)
+//
+//
+// }
+
+ insertionStrategy.addListener(new RemoveEmptyVehicles());
+ insertionStrategy.addListener(new ResetAndIniFleetManager(vehicleFleetManager));
+ insertionStrategy.addAllListener(insertionListeners);
+// insertionStrategy.addListener(new FindCheaperVehicle(
+// new FindCheaperVehicleAlgoNew(vehicleFleetManager, tourStateCalculator, auxCalculator)));
+
+ algorithmListeners.addAll(algoListeners);
+
+ return insertionStrategy;
+ }
+ else throw new IllegalStateException("cannot create insertionStrategy, since it has no name.");
+ }
+
+
+}
+
+
+
diff --git a/jsprit-core/src/main/java/algorithms/InsertionStrategy.java b/jsprit-core/src/main/java/algorithms/InsertionStrategy.java
new file mode 100644
index 00000000..7c56995b
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/InsertionStrategy.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Collection;
+
+import basics.Job;
+import basics.route.VehicleRoute;
+
+
+
+
+/**
+ *
+ * @author stefan schroeder
+ *
+ */
+
+interface InsertionStrategy {
+
+ class Insertion {
+
+ private final VehicleRoute route;
+
+ private final InsertionData insertionData;
+
+ public Insertion(VehicleRoute vehicleRoute, InsertionData insertionData) {
+ super();
+ this.route = vehicleRoute;
+ this.insertionData = insertionData;
+ }
+
+ public VehicleRoute getRoute() {
+ return route;
+ }
+
+ public InsertionData getInsertionData() {
+ return insertionData;
+ }
+
+ }
+
+
+
+
+
+ /**
+ * Assigns the unassigned jobs to service-providers
+ *
+ * @param vehicleRoutes
+ * @param unassignedJobs
+ * @param result2beat
+ */
+ public void run(Collection vehicleRoutes, Collection unassignedJobs, double result2beat);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/InsertionStrategyFactory.java b/jsprit-core/src/main/java/algorithms/InsertionStrategyFactory.java
new file mode 100644
index 00000000..856512b6
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/InsertionStrategyFactory.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.VehicleRoutingProblem;
+
+interface InsertionStrategyFactory {
+
+ public InsertionStrategy createStrategy(VehicleRoutingProblem vrp);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/JobDistance.java b/jsprit-core/src/main/java/algorithms/JobDistance.java
new file mode 100644
index 00000000..82464989
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/JobDistance.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.Job;
+
+
+
+interface JobDistance {
+
+ public double calculateDistance(Job i, Job j);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/JobDistanceAvgCosts.java b/jsprit-core/src/main/java/algorithms/JobDistanceAvgCosts.java
new file mode 100644
index 00000000..74c742e8
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/JobDistanceAvgCosts.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.Job;
+import basics.Service;
+import basics.costs.VehicleRoutingTransportCosts;
+
+
+
+class JobDistanceAvgCosts implements JobDistance {
+
+ private VehicleRoutingTransportCosts costs;
+
+ public JobDistanceAvgCosts(VehicleRoutingTransportCosts costs) {
+ super();
+ this.costs = costs;
+
+ }
+
+ @Override
+ public double calculateDistance(Job i, Job j) {
+ double avgCost = 0.0;
+// if (i instanceof Shipment && j instanceof Shipment) {
+// if (i.equals(j)) {
+// avgCost = 0.0;
+// } else {
+// Shipment s_i = (Shipment) i;
+// Shipment s_j = (Shipment) j;
+// double cost_i1_j1 = calcDist(s_i.getFromId(), s_j.getFromId());
+// double cost_i1_j2 = calcDist(s_i.getFromId(), s_j.getToId());
+// double cost_i2_j1 = calcDist(s_i.getToId(), s_j.getFromId());
+// double cost_i2_j2 = calcDist(s_i.getToId(), s_j.getToId());
+// avgCost = (cost_i1_j1 + cost_i1_j2 + cost_i2_j1 + cost_i2_j2) / 4;
+// }
+// } else
+ if (i instanceof Service && j instanceof Service) {
+ if (i.equals(j)) {
+ avgCost = 0.0;
+ } else {
+ Service s_i = (Service) i;
+ Service s_j = (Service) j;
+ avgCost = calcDist(s_i.getLocationId(), s_j.getLocationId());
+ }
+ } else {
+ throw new UnsupportedOperationException(
+ "currently, this class just works with shipments and services.");
+ }
+ return avgCost;
+ }
+
+ private double calcDist(String from, String to) {
+ return costs.getTransportCost(from, to, 0.0, null, null);
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/JobDistanceBeeline.java b/jsprit-core/src/main/java/algorithms/JobDistanceBeeline.java
new file mode 100644
index 00000000..b0fe0f5c
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/JobDistanceBeeline.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import util.CrowFlyCosts;
+import util.Locations;
+import basics.Job;
+import basics.Service;
+
+
+
+class JobDistanceBeeline implements JobDistance {
+
+ private Locations locations;
+
+ public JobDistanceBeeline(Locations locations) {
+ super();
+ this.locations = locations;
+ }
+
+ @Override
+ public double calculateDistance(Job i, Job j) {
+ double avgCost = 0.0;
+// if (i instanceof Shipment && j instanceof Shipment) {
+// if (i.equals(j)) {
+// avgCost = 0.0;
+// } else {
+// Shipment s_i = (Shipment) i;
+// Shipment s_j = (Shipment) j;
+// double cost_i1_j1 = calcDist(s_i.getFromId(), s_j.getFromId());
+// double cost_i1_j2 = calcDist(s_i.getFromId(), s_j.getToId());
+// double cost_i2_j1 = calcDist(s_i.getToId(), s_j.getFromId());
+// double cost_i2_j2 = calcDist(s_i.getToId(), s_j.getToId());
+// avgCost = (cost_i1_j1 + cost_i1_j2 + cost_i2_j1 + cost_i2_j2) / 4;
+// }
+// } else
+ if (i instanceof Service && j instanceof Service) {
+ if (i.equals(j)) {
+ avgCost = 0.0;
+ } else {
+ Service s_i = (Service) i;
+ Service s_j = (Service) j;
+ avgCost = calcDist(s_i.getLocationId(), s_j.getLocationId());
+ }
+ } else {
+ throw new UnsupportedOperationException(
+ "currently, this class just works with shipments and services.");
+ }
+ return avgCost;
+ }
+
+ private double calcDist(String from, String to) {
+ return new CrowFlyCosts(locations).getTransportCost(from, to, 0.0,null, null);
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/JobInsertionCalculator.java b/jsprit-core/src/main/java/algorithms/JobInsertionCalculator.java
new file mode 100644
index 00000000..88b8b7fd
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/JobInsertionCalculator.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.Job;
+import basics.route.Driver;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+
+
+ interface JobInsertionCalculator {
+
+ public InsertionData calculate(VehicleRoute currentRoute, Job jobToInsert, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownScore);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/JobObserver.java b/jsprit-core/src/main/java/algorithms/JobObserver.java
new file mode 100644
index 00000000..43641b04
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/JobObserver.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import algorithms.RouteStates.ActivityState;
+import basics.algo.AlgorithmEndsListener;
+import basics.algo.JobInsertedListener;
+import basics.route.TourActivity;
+import basics.route.VehicleRoute;
+import basics.route.TourActivity.JobActivity;
+import basics.Job;
+import basics.Service;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+
+class JobObserver implements JobInsertedListener, BeforeJobInsertionListener, AlgorithmEndsListener{
+
+ private static class Info {
+ double depTime;
+ double tourSize;
+ double insertionIndex;
+ double error;
+ public Info(double depTime, double tourSize, double insertionIndex,
+ double error) {
+ super();
+ this.depTime = depTime;
+ this.tourSize = tourSize;
+ this.insertionIndex = insertionIndex;
+ this.error = error;
+ }
+
+ }
+
+ private String locationId = "70";
+
+ private double routeCostBefore;
+ private double estimatedMC;
+ private boolean beforeFirst = false;
+
+ private RouteStates actStates;
+
+ public void setActivityStates(RouteStates actStates){
+ this.actStates = actStates;
+ }
+
+ public ActivityState state(TourActivity act){
+ return actStates.getState(act);
+ }
+
+
+ Collection infos = new ArrayList();
+
+ @Override
+ public void informJobInserted(int nOfJobsStill2Recreate, Job job2insert, VehicleRoute insertedIn) {
+ if(job2insert instanceof Service){
+ if(((Service) job2insert).getLocationId().equals(locationId)){
+ double actualMC = insertedIn.getCost()-routeCostBefore;
+ TourActivity act = getAct(job2insert,insertedIn);
+ double error = (estimatedMC-actualMC);
+ int tourSize = insertedIn.getTourActivities().getActivities().size();
+ int insertionIndex = getIndexOf(job2insert, insertedIn);
+// infos.add(new Info())
+ double depTime = state(act).getEarliestOperationStart()+act.getOperationTime();
+ infos.add(new Info(depTime,tourSize,insertionIndex,error));
+// System.out.println("[id=1][tourSize="+tourSize+"][index="+insertionIndex+
+// "][earliestDeparture="+depTime+
+// "][tourCostBefore="+routeCostBefore+"][routeCostAfter="+insertedIn.getCost()+"]" +
+// "[estimated="+Math.round(estimatedMC)+"][actual="+Math.round(actualMC)+"][error(abs)="+error +
+// "][errorPerNextCustomer="+ (error/(double)(tourSize-insertionIndex)) + "]");
+ routeCostBefore = 0.0;
+ estimatedMC = 0.0;
+ if(!beforeFirst) throw new IllegalStateException("ähhh");
+ beforeFirst = false;
+ }
+ }
+ }
+
+ private TourActivity getAct(Job job2insert, VehicleRoute insertedIn) {
+ for(TourActivity act : insertedIn.getTourActivities().getActivities()){
+ if(act instanceof JobActivity){
+ if(((JobActivity) act).getJob().getId().equals(job2insert.getId())){
+ return act;
+ }
+ }
+ }
+ return null;
+ }
+
+ private int getIndexOf(Job job2insert, VehicleRoute insertedIn) {
+ int index=0;
+ for(TourActivity act : insertedIn.getTourActivities().getActivities()){
+ if(act instanceof JobActivity){
+ if(((JobActivity) act).getJob().getId().equals(job2insert.getId())){
+ return index;
+ }
+ }
+ index++;
+ }
+ return -1;
+ }
+
+ @Override
+ public void informBeforeJobInsertion(Job job, InsertionData data, VehicleRoute route) {
+ if(job instanceof Service){
+ if(((Service) job).getLocationId().equals(locationId)){
+// System.out.println("[id=1][tourSize="+route.getTour().getActivities().size()+"][tourCost="+route.getCost()+"]" +
+// "[estimatedMarginalInsertionCost="+data.getInsertionCost()+"]");
+ routeCostBefore = route.getCost();
+ estimatedMC = data.getInsertionCost();
+ beforeFirst = true;
+ }
+ }
+ }
+
+ @Override
+ public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection solutions) {
+ try {
+ BufferedWriter writer = new BufferedWriter(new FileWriter("output/errorAna.txt"));
+ for(Info info : infos){
+ writer.write(new StringBuilder().append(info.depTime).append(";").append(info.tourSize).append(";").append(info.insertionIndex).append(";")
+ .append(info.error).append("\n").toString());
+ }
+ writer.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/JobRemover.java b/jsprit-core/src/main/java/algorithms/JobRemover.java
new file mode 100644
index 00000000..c8d34d15
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/JobRemover.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.Job;
+import basics.route.VehicleRoute;
+
+interface JobRemover {
+
+ /**
+ * Removes jobs from vehicRoute and return true if job has been successfully removed.
+ *
+ * @return true if job removed successfully, otherwise false
+ */
+ public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute);
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/JobRemoverImpl.java b/jsprit-core/src/main/java/algorithms/JobRemoverImpl.java
new file mode 100644
index 00000000..737fca0a
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/JobRemoverImpl.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import basics.Job;
+import basics.route.VehicleRoute;
+
+class JobRemoverImpl implements JobRemover{
+
+ interface RemoverListener {
+ public void informRemovedJob(Job j, VehicleRoute r);
+ }
+
+ private List remListeners = new ArrayList();
+
+ @Override
+ public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute) {
+ boolean jobRemoved = vehicleRoute.getTourActivities().removeJob(job);
+ if(jobRemoved) informRemovedJob(job,vehicleRoute);
+ return jobRemoved;
+ }
+
+ private void informRemovedJob(Job job, VehicleRoute vehicleRoute) {
+ for(RemoverListener l : remListeners) l.informRemovedJob(job, vehicleRoute);
+ }
+
+ public List getRemListeners() {
+ return remListeners;
+ }
+
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/NeighborhoodThresholdInitialiser.java b/jsprit-core/src/main/java/algorithms/NeighborhoodThresholdInitialiser.java
new file mode 100644
index 00000000..efd39118
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/NeighborhoodThresholdInitialiser.java
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Collection;
+
+import org.apache.commons.math.stat.descriptive.moment.Mean;
+import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
+import org.apache.log4j.Logger;
+
+import util.CrowFlyCosts;
+import util.EuclideanDistanceCalculator;
+import util.Locations;
+import util.NeighborhoodImpl;
+import util.Solutions;
+import algorithms.selectors.SelectBest;
+import basics.VehicleRoutingAlgorithm;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.algo.AlgorithmStartsListener;
+import basics.algo.VehicleRoutingAlgorithmFactory;
+import basics.route.TourActivity;
+import basics.route.VehicleRoute;
+
+public class NeighborhoodThresholdInitialiser implements AlgorithmStartsListener{
+
+ private static Logger log = Logger.getLogger(NeighborhoodThresholdInitialiser.class);
+
+ private NeighborhoodImpl neighborhood;
+
+ private VehicleRoutingAlgorithmFactory routingAlgorithmFactory = new VehicleRoutingAlgorithmFactory() {
+
+ @Override
+ public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp) {
+ VehicleRoutingAlgorithm algorithm = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, "resources/config.xml");
+ return algorithm;
+ }
+ };
+
+ private int crowFlySpeed = 20;
+
+ public NeighborhoodThresholdInitialiser(NeighborhoodImpl neighborhood) {
+ this.neighborhood = neighborhood;
+ }
+
+
+ /**
+ * @param crowFlySpeed the crowFlySpeed to set
+ */
+ public void setCrowFlySpeed(int crowFlySpeed) {
+ this.crowFlySpeed = crowFlySpeed;
+ }
+
+
+ /**
+ * @param routingAlgorithmFactory the routingAlgorithm to set
+ */
+ public void setRoutingAlgorithmFactory(VehicleRoutingAlgorithmFactory routingAlgorithmFactory) {
+ this.routingAlgorithmFactory = routingAlgorithmFactory;
+ }
+
+ public void initialise(VehicleRoutingProblem problem){
+ informAlgorithmStarts(problem, null, null);
+ }
+
+ @Override
+ public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection solutions) {
+ VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance();
+ builder.addAllJobs(problem.getJobs().values());
+ builder.addAllVehicles(problem.getVehicles());
+ VehicleRoutingProblem pblm = builder.build();
+ CrowFlyCosts crowFly = new CrowFlyCosts(builder.getLocations());
+ crowFly.speed = crowFlySpeed;
+ pblm.setTransportCosts(crowFly);
+
+ VehicleRoutingAlgorithm algo = routingAlgorithmFactory.createAlgorithm(pblm);
+ Collection mySolutions = algo.searchSolutions();
+
+ double threshold = determineThreshold(pblm,builder.getLocations(), mySolutions);
+ neighborhood.setThreshold(threshold);
+ neighborhood.initialise();
+ }
+
+ private double determineThreshold(VehicleRoutingProblem pblm, Locations locations, Collection mySolutions) {
+ VehicleRoutingProblemSolution bestSolution = Solutions.getBest(mySolutions);
+ double[] distances = new double[bestSolution.getRoutes().size()+pblm.getJobs().size()];
+ getDistances(distances,bestSolution,locations);
+ Mean mean = new Mean();
+ double meanValue = mean.evaluate(distances);
+ StandardDeviation dev = new StandardDeviation();
+ double devValue = dev.evaluate(distances, meanValue);
+ log.info("mean="+meanValue+", dev="+devValue);
+ return meanValue + devValue;
+// + 2*devValue;
+// return Double.MAX_VALUE;
+ }
+
+ private void getDistances(double[] distances, VehicleRoutingProblemSolution bestSolution, Locations locations) {
+ int index = 0;
+ for(VehicleRoute route : bestSolution.getRoutes()){
+ TourActivity prev = null;
+ for(TourActivity act : route.getTourActivities().getActivities()){
+ if(prev == null){ prev = act; continue; }
+ double dist = EuclideanDistanceCalculator.calculateDistance(locations.getCoord(prev.getLocationId()), locations.getCoord(act.getLocationId()));
+// log.info("dist="+dist);
+ distances[index] = dist;
+ index++;
+ prev = act;
+ }
+// double dist = EuclideanDistanceCalculator.calculateDistance(locations.getCoord(prev.getLocationId()), locations.getCoord(route.getEnd().getLocationId()));
+// distances[index] = dist;
+// index++;
+ }
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/ParRegretInsertion.java b/jsprit-core/src/main/java/algorithms/ParRegretInsertion.java
new file mode 100644
index 00000000..d894e7f7
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/ParRegretInsertion.java
@@ -0,0 +1,229 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+///*******************************************************************************
+// * Copyright (c) 2011 Stefan Schroeder.
+// * eMail: stefan.schroeder@kit.edu
+// *
+// * All rights reserved. This program and the accompanying materials
+// * are made available under the terms of the GNU Public License v2.0
+// * which accompanies this distribution, and is available at
+// * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+// *
+// * Contributors:
+// * Stefan Schroeder - initial API and implementation
+// ******************************************************************************/
+//package algorithms;
+//
+//import java.util.ArrayList;
+//import java.util.Collection;
+//import java.util.List;
+//import java.util.concurrent.Callable;
+//import java.util.concurrent.CompletionService;
+//import java.util.concurrent.ExecutionException;
+//import java.util.concurrent.ExecutorCompletionService;
+//import java.util.concurrent.ExecutorService;
+//import java.util.concurrent.Future;
+//
+//import org.apache.log4j.Logger;
+//
+//import basics.InsertionData;
+//import basics.Job;
+//import basics.Service;
+//import basics.Shipment;
+//import basics.VehicleRoute;
+//import basics.InsertionData.NoInsertionFound;
+//
+//
+//
+//
+//
+//
+//
+///**
+// * Simplest recreation strategy. All removed customers are inserted where insertion costs are minimal. I.e. each tour-agent is asked for
+// * minimal marginal insertion costs. The tour-agent offering the lowest marginal insertion costs gets the customer/shipment.
+// *
+// * @author stefan schroeder
+// *
+// */
+//
+//final class ParRegretInsertion extends AbstractRecreationStrategy{
+//
+//
+// private Logger logger = Logger.getLogger(ParRegretInsertion.class);
+//
+//
+// public static double scoreParam_of_timeWindowLegth = 0.0;
+//
+// private ExecutorService executor;
+//
+// public static double scoreParam_of_distance = 0.5;
+//
+// private DepotDistance depotDistance;
+//
+// private RouteAlgorithm routeAlgorithm;
+//
+// private VehicleRouteFactory vehicleRouteFactory;
+//
+// public ParRegretInsertion(ExecutorService executor, RouteAlgorithm routeAlgorithm, VehicleRouteFactory routeFactory, DepotDistance depotDistance) {
+// super();
+// this.executor = executor;
+// this.routeAlgorithm = routeAlgorithm;
+// this.depotDistance = depotDistance;
+// this.vehicleRouteFactory = routeFactory;
+// }
+//
+//
+// @Override
+// public void recreate(final Collection vehicleRoutes, Collection unassignedJobs, double result2beat) {
+// List jobs = new ArrayList(unassignedJobs);
+// informRecreationStart(unassignedJobs.size());
+//
+// while(!jobs.isEmpty()){
+// List unassignedJobList = new ArrayList(jobs);
+// ScoredJob bestScoredJob = null;
+// double bestScore = -1*Double.MAX_VALUE;
+// CompletionService completionService = new ExecutorCompletionService(executor);
+//
+// for(final Job unassignedJob : unassignedJobList){
+// completionService.submit(new Callable(){
+//
+// @Override
+// public ScoredJob call() throws Exception {
+// return getScoredJob(vehicleRoutes, unassignedJob);
+// }
+//
+// });
+//
+// }
+// try{
+// for(int i=0;i fsj = completionService.take();
+// ScoredJob scoredJob = fsj.get();
+// if(scoredJob == null){
+// continue;
+// }
+// if(scoredJob.getScore() > bestScore){
+// bestScoredJob = scoredJob;
+// bestScore = scoredJob.getScore();
+// }
+// }
+// }
+// catch(InterruptedException e){
+// Thread.currentThread().interrupt();
+// }
+// catch (ExecutionException e) {
+// e.printStackTrace();
+// logger.error(e.getCause().toString());
+// System.exit(1);
+// }
+// if(bestScoredJob == null){
+// Job job = unassignedJobList.get(0);
+// VehicleRoute newRoute = vehicleRouteFactory.createVehicleRoute();
+// InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, job, Double.MAX_VALUE);
+// if(bestI instanceof InsertionData.NoInsertionFound) throw new IllegalStateException("given the vehicles, could not create a valid solution");
+// routeAlgorithm.insertJob(job,bestI,newRoute);
+// vehicleRoutes.add(newRoute);
+// jobs.remove(job);
+//
+// }
+// else{
+// routeAlgorithm.insertJob(bestScoredJob.getJob(),bestScoredJob.getInsertionData(),bestScoredJob.getRoute());
+// jobs.remove(bestScoredJob.getJob());
+// }
+// informJobInsertion(null, (unassignedJobList.size()-1), null);
+// }
+// }
+//
+// private ScoredJob getScoredJob(Collection vehicleRoutes, Job job){
+// InsertionData best = null;
+// InsertionData secondBest = null;
+// VehicleRoute bestRoute = null;
+// double benchmark = Double.MAX_VALUE;
+// for(VehicleRoute route : vehicleRoutes){
+// if(secondBest != null){
+// benchmark = secondBest.getInsertionCost();
+// }
+// InsertionData iData = routeAlgorithm.calculateBestInsertion(route, job, benchmark);
+// if(iData instanceof NoInsertionFound) continue;
+// if(best == null) {
+// best = iData;
+// bestRoute = route;
+// }
+// else if(iData.getInsertionCost() < best.getInsertionCost()){
+// secondBest = best;
+// best = iData;
+// bestRoute = route;
+// }
+// else if(secondBest == null) secondBest = iData;
+// else if(iData.getInsertionCost() < secondBest.getInsertionCost()) secondBest = iData;
+// }
+// if(best == null){
+// return null;
+// }
+// double score = score(job,best,secondBest);
+// return new ScoredJob(job, score, best, bestRoute);
+// }
+//
+// private double score(Job unassignedJob, InsertionData best, InsertionData secondBest) {
+// /*
+// * wieder so eine bescheuerte fallunterscheidung. hier will ich
+// * doch einfach nur das maßgebende zeitfenster des jobs
+// * job.getTimeWindow()
+// * Problem: eine Shipment hat zwei TWs, sowohl ein PickupTW als auch
+// * ein DeliveryTW
+// */
+// double twStart = 0.0;
+// double twEnd = 0.0;
+// if(unassignedJob instanceof Shipment){
+// twStart = ((Shipment) unassignedJob).getDeliveryTW().getStart();
+// twEnd = ((Shipment) unassignedJob).getDeliveryTW().getEnd();
+// }
+// else if(unassignedJob instanceof Service){
+// twStart = ((Service) unassignedJob).getTimeWindow().getStart();
+// twEnd = ((Service) unassignedJob).getTimeWindow().getEnd();
+// }
+// if(best == null){
+// throw new IllegalStateException("cannot insert job " + unassignedJob.getId());
+// }
+// if(secondBest == null){
+// return Double.MAX_VALUE;
+// }
+//// double score = (secondBest.getInsertionCost()-best.getInsertionCost()) + scoreParam_of_distance*getDistance(unassignedJob) - scoreParam_of_timeWindowLegth*(twEnd-twStart);
+//// logger.info("priceDiff="+ (secondBest.getPrice()-best.getPrice()) + "; param*dist="
+// double timeWindowInfluence = scoreParam_of_timeWindowLegth*(twEnd-twStart);
+// double distanceInfluence = scoreParam_of_distance*getDistance(unassignedJob);
+// double score = (secondBest.getInsertionCost()-best.getInsertionCost()) - timeWindowInfluence
+// + distanceInfluence;
+// return score;
+// }
+//
+// private double getDistance(Job unassignedJob) {
+// return depotDistance.calcDistance(unassignedJob);
+// }
+//
+//
+// public double getTimeParam() {
+// return scoreParam_of_timeWindowLegth;
+// }
+//
+//
+//}
diff --git a/jsprit-core/src/main/java/algorithms/RegretInsertion.java b/jsprit-core/src/main/java/algorithms/RegretInsertion.java
new file mode 100644
index 00000000..46ad780b
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RegretInsertion.java
@@ -0,0 +1,210 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import algorithms.InsertionData.NoInsertionFound;
+import basics.Job;
+import basics.Service;
+import basics.route.VehicleRoute;
+
+
+/**
+ * Insertion based an regret approach.
+ *
+ * Basically calculates the insertion cost of the firstBest and the secondBest alternative. The score is then calculated as difference
+ * between secondBest and firstBest, plus additional scoring variables that can defined in this.ScoringFunction.
+ * The idea is that if the cost of the secondBest alternative is way higher than the first best, it seems to be important to insert this
+ * customer immediatedly. If difference is not that high, it might not impact solution if this customer is inserted later.
+ *
+ * @author stefan schroeder
+ *
+ */
+final class RegretInsertion extends AbstractInsertionStrategy{
+
+ /**
+ * Scorer to include other impacts on score such as time-window length or distance to depot.
+ *
+ * @author schroeder
+ *
+ */
+ static interface ScoringFunction {
+
+ public double score(Job job);
+
+ }
+
+ /**
+ * Scorer that includes the length of the time-window when scoring a job. The wider the time-window, the lower the score.
+ *
+ *
This is the default scorer, i.e.: score = (secondBest - firstBest) + this.TimeWindowScorer.score(job)
+ *
+ * @author schroeder
+ *
+ */
+ static class TimeWindowScorer implements ScoringFunction {
+
+ private double tw_scoringParam = - 0.1;
+
+ @Override
+ public double score(Job job) {
+ double twStart = 0.0;
+ double twEnd = 0.0;
+// if(job instanceof Shipment){
+// twStart = ((Shipment) job).getDeliveryTW().getStart();
+// twEnd = ((Shipment) job).getDeliveryTW().getEnd();
+// }
+// else
+ if(job instanceof Service){
+ twStart = ((Service) job).getTimeWindow().getStart();
+ twEnd = ((Service) job).getTimeWindow().getEnd();
+ }
+ return (twEnd-twStart)*tw_scoringParam;
+ }
+
+ @Override
+ public String toString() {
+ return "[name=timeWindowScorer][scoringParam="+tw_scoringParam+"]";
+ }
+
+ }
+
+ public static RegretInsertion newInstance(RouteAlgorithm routeAlgorithm) {
+ return new RegretInsertion(routeAlgorithm);
+ }
+
+ private Logger logger = Logger.getLogger(RegretInsertion.class);
+
+ private RouteAlgorithm routeAlgorithm;
+
+ private ScoringFunction scoringFunction = new TimeWindowScorer();
+
+ /**
+ * Sets the scoring function.
+ *
+ *
By default, the this.TimeWindowScorer is used.
+ *
+ * @param scoringFunction
+ */
+ public void setScoringFunction(ScoringFunction scoringFunction) {
+ this.scoringFunction = scoringFunction;
+ }
+
+ public RegretInsertion(RouteAlgorithm routeAlgorithm) {
+ super();
+ this.routeAlgorithm = routeAlgorithm;
+ logger.info("initialise " + this);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=regretInsertion][additionalScorer="+scoringFunction+"]";
+ }
+
+ public RouteAlgorithm getRouteAlgorithm(){
+ return routeAlgorithm;
+ }
+
+ /**
+ * Runs insertion.
+ *
+ *
Before inserting a job, all unassigned jobs are scored according to its best- and secondBest-insertion plus additional scoring variables.
+ *
+ */
+ @Override
+ public void run(Collection routes, Collection unassignedJobs, double resultToBeat) {
+ List jobs = new ArrayList(unassignedJobs);
+ informInsertionStarts(routes,unassignedJobs.size());
+ int inserted = 0;
+ while(!jobs.isEmpty()){
+ List unassignedJobList = new ArrayList(jobs);
+ ScoredJob bestScoredJob = null;
+ double bestScore = -1*Double.MAX_VALUE;
+ VehicleRoute insertIn = null;
+
+ for(Job unassignedJob : unassignedJobList){
+ InsertionData best = null;
+ InsertionData secondBest = null;
+ VehicleRoute bestRoute = null;
+
+ double benchmark = Double.MAX_VALUE;
+ for(VehicleRoute route : routes){
+ if(secondBest != null){
+ benchmark = secondBest.getInsertionCost();
+ }
+ InsertionData iData = routeAlgorithm.calculateBestInsertion(route, unassignedJob, benchmark);
+ if(iData instanceof NoInsertionFound) continue;
+ if(best == null){
+ best = iData;
+ bestRoute = route;
+ }
+ else if(iData.getInsertionCost() < best.getInsertionCost()){
+ secondBest = best;
+ best = iData;
+ bestRoute = route;
+ }
+ else if(secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())){
+ secondBest = iData;
+ }
+ }
+ if(best == null){
+ break;
+ }
+ double score = score(unassignedJob,best,secondBest);
+ if(score > bestScore){
+ bestScoredJob = new ScoredJob(unassignedJob,score,best,bestRoute);
+ bestScore = score;
+ }
+ }
+ Job assignedJob;
+ if(bestScoredJob == null){
+ Job job = unassignedJobList.get(0);
+ VehicleRoute newRoute = VehicleRoute.emptyRoute();
+ InsertionData bestI = routeAlgorithm.calculateBestInsertion(newRoute, job, Double.MAX_VALUE);
+ if(bestI instanceof InsertionData.NoInsertionFound) throw new IllegalStateException("given the vehicles, could not create a valid solution");
+ insertIn=newRoute;
+ assignedJob=job;
+ routeAlgorithm.insertJob(job,bestI,newRoute);
+ routes.add(newRoute);
+ jobs.remove(job);
+
+ }
+ else{
+ routeAlgorithm.insertJob(bestScoredJob.getJob(),bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
+ insertIn=bestScoredJob.getRoute();
+ assignedJob=bestScoredJob.getJob();
+ jobs.remove(bestScoredJob.getJob());
+ }
+ inserted++;
+ informJobInserted((unassignedJobList.size()-inserted), assignedJob, insertIn);
+
+ }
+ }
+
+ private double score(Job unassignedJob, InsertionData best, InsertionData secondBest) {
+ if(best == null){
+ throw new IllegalStateException("cannot insert job " + unassignedJob.getId());
+ }
+ if(secondBest == null){
+ return Double.MAX_VALUE;
+ }
+ return (secondBest.getInsertionCost()-best.getInsertionCost()) + scoringFunction.score(unassignedJob);
+
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/RemoveEmptyVehicles.java b/jsprit-core/src/main/java/algorithms/RemoveEmptyVehicles.java
new file mode 100644
index 00000000..16796f87
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RemoveEmptyVehicles.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.apache.log4j.Logger;
+
+
+import basics.algo.InsertionEndsListener;
+import basics.algo.InsertionStartsListener;
+import basics.route.VehicleRoute;
+
+class RemoveEmptyVehicles implements InsertionStartsListener, InsertionEndsListener{
+
+ private static Logger log = Logger.getLogger(RemoveEmptyVehicles.class);
+
+ @Override
+ public void informInsertionStarts(Collection vehicleRoutes, int nOfJobs2Recreate) {
+// List routes = new ArrayList(vehicleRoutes);
+// for(VehicleRoute route : routes){
+// if(route.isEmpty()) { vehicleRoutes.remove(route); }
+// }
+ }
+
+ @Override
+ public String toString() {
+ return "[name=removeEmptyVehicles]";
+ }
+
+ @Override
+ public void informInsertionEnds(Collection vehicleRoutes) {
+ List routes = new ArrayList(vehicleRoutes);
+ for(VehicleRoute route : routes){
+ if(route.isEmpty()) { vehicleRoutes.remove(route); }
+ }
+ }
+}
diff --git a/jsprit-core/src/main/java/algorithms/ResetAndIniFleetManager.java b/jsprit-core/src/main/java/algorithms/ResetAndIniFleetManager.java
new file mode 100644
index 00000000..98e2a669
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/ResetAndIniFleetManager.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Collection;
+
+import org.apache.log4j.Logger;
+
+import basics.algo.InsertionStartsListener;
+import basics.route.VehicleRoute;
+
+class ResetAndIniFleetManager implements InsertionStartsListener{
+
+ private static Logger log = Logger.getLogger(ResetAndIniFleetManager.class);
+
+ private VehicleFleetManager vehicleFleetManager;
+
+ ResetAndIniFleetManager(VehicleFleetManager vehicleFleetManager) {
+ super();
+ this.vehicleFleetManager = vehicleFleetManager;
+ }
+
+ @Override
+ public void informInsertionStarts(Collection vehicleRoutes, int nOfJobs2Recreate) {
+ vehicleFleetManager.unlockAll();
+ for(VehicleRoute route : vehicleRoutes){
+ if(!route.isEmpty()){
+ vehicleFleetManager.lock(route.getVehicle());
+ }
+ }
+// log.info("#lockedVehicles=" + ((VehicleFleetManagerImpl)vehicleFleetManager).sizeOfLockedVehicles() + " #routes="+vehicleRoutes.size());
+ }
+
+ @Override
+ public String toString() {
+ return "[name=resetAndIniFleetManager]";
+ }
+}
diff --git a/jsprit-core/src/main/java/algorithms/RouteAlgorithm.java b/jsprit-core/src/main/java/algorithms/RouteAlgorithm.java
new file mode 100644
index 00000000..351a9fa6
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RouteAlgorithm.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Collection;
+
+import basics.Job;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+
+
+interface RouteAlgorithm {
+
+ interface RouteAlgorithmListener {
+
+ }
+
+ interface JobRemovedListener extends RouteAlgorithmListener{
+ public void removed(VehicleRoute route, Job job);
+ }
+
+ interface JobInsertedListener extends RouteAlgorithmListener{
+ public void inserted(VehicleRoute route, Job job);
+ }
+
+ interface VehicleSwitchedListener extends RouteAlgorithmListener{
+ public void vehicleSwitched(Vehicle oldVehicle, Vehicle newVehicle);
+ }
+
+ /**
+ * Calculates the best insertion position and the corresponding marginal costs of inserting the job (according to the insertionCostCalculator).
+ * This does not affect any input parameter, thus the vehicleRoute and its data will not be changed/affected.
+ *
+ * @param VehicleRoute, Job, double
+ * @return InsertionData
+ */
+ public InsertionData calculateBestInsertion(VehicleRoute vehicleRoute, Job job, double bestKnownPrice);
+
+ /**
+ * Removes job from vehicleRoute and does not update the resulting tour. Thus the tour state might not be valid anymore.
+ * Note that this changes vehicleRoute!
+ *
+ * @return true if job removed successfully, otherwise false
+ */
+ public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute);
+
+ /**
+ * Removes job from input parameter vehicleRoute AND updates the state of the resulting tour with tourStateCalc.
+ * Note that this changes para vehicleRoute!
+ */
+ public boolean removeJob(Job job, VehicleRoute vehicleRoute);
+
+ /**
+ * Inserts job into vehicleRoute.getTour().
+ * Please note, that this changes the parameter vehicleRoute!
+ */
+ public void insertJobWithoutTourUpdate(VehicleRoute vehicleRoute, Job job, InsertionData insertionData);
+
+ /**
+ * Inserts job into vehicleRoute.getTour().
+ * Please note, that this changes the parameter vehicleRoute!
+ */
+ public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute);
+
+ /**
+ * Updates vehicleRoute, i.e. uses the tourStateCalculator to update for example timeWindows and loads on vehicleRoute.getTour()
+ * Note that this changes the parameter vehicleRoute!
+ */
+ public void updateTour(VehicleRoute vehicleRoute);
+
+ public Collection getListeners();
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/RouteAlgorithmImpl.java b/jsprit-core/src/main/java/algorithms/RouteAlgorithmImpl.java
new file mode 100644
index 00000000..2d9062d8
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RouteAlgorithmImpl.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.log4j.Logger;
+
+import algorithms.RouteStates.ActivityState;
+import algorithms.InsertionData.NoInsertionFound;
+import basics.Job;
+import basics.Service;
+import basics.route.ServiceActivity;
+import basics.route.TourActivity;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+
+
+/**
+ *
+ * @author stefan schroeder
+ *
+ */
+
+final class RouteAlgorithmImpl implements RouteAlgorithm {
+
+ private static Logger logger = Logger.getLogger(RouteAlgorithmImpl.class);
+
+ static double NO_DEPARTURE_TIME = -12345.12345;
+
+ private String algoDescription = "algorithm to remove and insert jobs from vehicleRoutes. it also calculates marginal costs of the best insertion of a " +
+ "job into the given vehicle route";
+
+ public static RouteAlgorithmImpl newInstance(JobInsertionCalculator jobInsertionCalculator, VehicleRouteUpdater tourStateCalculator){
+ return new RouteAlgorithmImpl(jobInsertionCalculator, tourStateCalculator);
+ }
+
+ private Collection listeners = new ArrayList();
+
+ private VehicleRouteUpdater tourCalculator;
+
+ private JobInsertionCalculator insertionCostCalculator;
+
+ private RouteStates actStates;
+
+ public void setActivityStates(RouteStates actStates){
+ this.actStates = actStates;
+ }
+
+ public ActivityState state(TourActivity act){
+ return actStates.getState(act);
+ }
+
+ private RouteAlgorithmImpl(JobInsertionCalculator insertionCostCalculator, VehicleRouteUpdater tourCalculator){
+ this.tourCalculator = tourCalculator;
+ this.insertionCostCalculator = insertionCostCalculator;
+ }
+
+
+ public InsertionData calculateBestInsertion(VehicleRoute vehicleRoute, Job job, double bestKnownCost) {
+ return insertionCostCalculator.calculate(vehicleRoute, job, null, NO_DEPARTURE_TIME, null, bestKnownCost);
+ }
+
+
+ public boolean removeJobWithoutTourUpdate(Job job, VehicleRoute vehicleRoute) {
+ boolean removed = vehicleRoute.getTourActivities().removeJob(job);
+ if(removed){
+ jobRemoved(vehicleRoute,job);
+ }
+ return removed;
+ }
+
+ private void jobRemoved(VehicleRoute vehicleRoute, Job job) {
+ for(RouteAlgorithmListener l : listeners){
+ if(l instanceof JobRemovedListener){
+ ((JobRemovedListener) l).removed(vehicleRoute, job);
+ }
+ }
+ }
+
+
+ public boolean removeJob(Job job, VehicleRoute vehicleRoute){
+ boolean removed = removeJobWithoutTourUpdate(job, vehicleRoute);
+ if(removed) updateTour(vehicleRoute);
+ return removed;
+ }
+
+
+ public void updateTour(VehicleRoute vehicleRoute){
+ boolean tourIsFeasible = tourCalculator.updateRoute(vehicleRoute);
+ if(!tourIsFeasible){
+ throw new IllegalStateException("At this point tour should be feasible. but it is not. \n currentTour=" + vehicleRoute.getTourActivities() +
+ "\n error sources: check jobInsertionCostCalculators and actInsertionCalculators. somehow an insertion is made, althought a hard constraint is broken. Here, hard constraints refer to \n" +
+ "hard time-window constraints. If you want to deal with such constraints, make sure a violation is penalized properly (such that it can never be the best insertion position). \n" +
+ "If you use CalculatesServiceInsertion and CalculatesActivityInsertion, the only hard constraint is the vehicle-capacity constraints. A violation of time-windows must be penalized in \n" +
+ "the vehicleRouteCostFunction. For example: in handleActivity(....) one can check whether the act start-time is higher than the latestOperationStartTime. If so penalize it with a very high value. \n" +
+ "For example: \n" +
+ "public void handleActivity(TourActivity tourAct, double startTime, double endTime) {\n" +
+ "\tif(startTime > tourAct.getLatestOperationStartTime()){\n" +
+ "\t\tcost += Double.MAX_VALUE;\n" +
+ "\t}\n" +
+ "});");
+ }
+ }
+
+
+ @Override
+ public void insertJobWithoutTourUpdate(VehicleRoute vehicleRoute, Job job, InsertionData insertionData) {
+ if(insertionData == null || (insertionData instanceof NoInsertionFound)) throw new IllegalStateException("insertionData null. cannot insert job.");
+ if(job == null) throw new IllegalStateException("cannot insert null-job");
+ if(!(vehicleRoute.getVehicle().getId().toString().equals(insertionData.getSelectedVehicle().getId().toString()))){
+ vehicleSwitched(vehicleRoute.getVehicle(),insertionData.getSelectedVehicle());
+ vehicleRoute.setVehicle(insertionData.getSelectedVehicle(), insertionData.getVehicleDepartureTime());
+ }
+ if(job instanceof Service) {
+ vehicleRoute.getTourActivities().addActivity(insertionData.getDeliveryInsertionIndex(), ServiceActivity.newInstance((Service)job));
+// if(vehicleRoute.getStart().getEndTime() != insertionData.getVehicleDepartureTime()){
+// logger.info("depTime switched from " + vehicleRoute.getStart().getEndTime() + " to " + insertionData.getVehicleDepartureTime());
+// }
+ vehicleRoute.setDepartureTime(insertionData.getVehicleDepartureTime());
+ }
+ else throw new IllegalStateException("neither service nor shipment. this is not supported.");
+ jobInserted(vehicleRoute,job);
+ }
+
+
+
+ private void vehicleSwitched(Vehicle oldVehicle, Vehicle newVehicle) {
+ for(RouteAlgorithmListener l : listeners){
+ if(l instanceof VehicleSwitchedListener){
+ ((VehicleSwitchedListener) l).vehicleSwitched(oldVehicle,newVehicle);
+ }
+ }
+ }
+
+ private void jobInserted(VehicleRoute vehicleRoute, Job job) {
+ for(RouteAlgorithmListener l : listeners){
+ if(l instanceof JobInsertedListener){
+ ((JobInsertedListener) l).inserted(vehicleRoute, job);
+ }
+ }
+ }
+
+
+ public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute){
+ insertJobWithoutTourUpdate(vehicleRoute, job, insertionData);
+ updateTour(vehicleRoute);
+ }
+
+ @Override
+ public String toString() {
+ return algoDescription;
+ }
+
+ private void insertJob(Service service, int serviceInsertionIndex, VehicleRoute vehicleRoute) {
+// TourActivity del = actStates.getActivity(service,true);
+ vehicleRoute.getTourActivities().addActivity(serviceInsertionIndex, ServiceActivity.newInstance(service));
+ }
+
+
+
+ public Collection getListeners() {
+ return listeners;
+ }
+
+ public void setAlgoDescription(String algoDescription) {
+ this.algoDescription = algoDescription;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/RouteStates.java b/jsprit-core/src/main/java/algorithms/RouteStates.java
new file mode 100644
index 00000000..19c770c7
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RouteStates.java
@@ -0,0 +1,208 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import basics.Job;
+import basics.Service;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.algo.AlgorithmEndsListener;
+import basics.algo.IterationEndsListener;
+import basics.algo.IterationStartsListener;
+import basics.route.ServiceActivity;
+import basics.route.TourActivity;
+import basics.route.VehicleRoute;
+
+class RouteStates implements IterationStartsListener{
+
+ Logger log = Logger.getLogger(RouteStates.class);
+
+ static class RouteState {
+ private double costs;
+ private int load;
+ private VehicleRoute route;
+ public RouteState(VehicleRoute route) {
+ super();
+ this.route = route;
+ }
+ /**
+ * @return the costs
+ */
+ public double getCosts() {
+ return costs;
+ }
+ /**
+ * @param costs the costs to set
+ */
+ public void setCosts(double costs) {
+ this.costs = costs;
+ }
+ /**
+ * @return the load
+ */
+ public int getLoad() {
+ return load;
+ }
+ /**
+ * @param load the load to set
+ */
+ public void setLoad(int load) {
+ this.load = load;
+ }
+
+ }
+
+ static class ActivityState {
+ private double earliestOperationStart;
+ private double latestOperationStart;
+ private double currentLoad;
+ private double currentCost;
+ private TourActivity act;
+
+ public ActivityState(TourActivity activity){
+ this.earliestOperationStart=activity.getTheoreticalEarliestOperationStartTime();
+ this.latestOperationStart=activity.getTheoreticalLatestOperationStartTime();
+ this.act = activity;
+ }
+
+ @Override
+ public String toString() {
+ return "[earliestStart="+earliestOperationStart+"][latestStart="+
+ latestOperationStart+"][currLoad="+currentLoad+"][currCost="+currentCost+"]";
+ }
+
+ public double getEarliestOperationStart() {
+ return earliestOperationStart;
+ }
+
+ void setEarliestOperationStart(double earliestOperationStart) {
+ this.earliestOperationStart = earliestOperationStart;
+ }
+
+ public double getLatestOperationStart() {
+ return latestOperationStart;
+ }
+
+ void setLatestOperationStart(double latestOperationStart) {
+ this.latestOperationStart = latestOperationStart;
+ }
+
+ public double getCurrentLoad() {
+ return currentLoad;
+ }
+
+ void setCurrentLoad(double currentLoad) {
+ this.currentLoad = currentLoad;
+ }
+
+ public double getCurrentCost() {
+ return currentCost;
+ }
+
+ void setCurrentCost(double currentCost) {
+ this.currentCost = currentCost;
+ }
+
+ public void reset() {
+ earliestOperationStart = act.getTheoreticalEarliestOperationStartTime();
+ latestOperationStart = act.getTheoreticalLatestOperationStartTime() ;
+ currentLoad = 0.0;
+ currentCost = 0.0;
+ }
+ }
+
+
+ private Map activityStates;
+
+ private Map tourActivities;
+
+ private Map routeStates;
+
+ public RouteStates() {
+ activityStates = new HashMap();
+ tourActivities = new HashMap();
+ routeStates = new HashMap();
+ }
+
+ ActivityState getState(TourActivity act){
+ if(!activityStates.containsKey(act)) return null;
+ return activityStates.get(act);
+ }
+
+ public void clearStates(){
+ activityStates.clear();
+ }
+
+ public Map getActivityStates() {
+ return activityStates;
+ }
+
+ TourActivity getActivity(Service service, boolean resetState){
+ TourActivity tourActivity = tourActivities.get(service);
+ getState(tourActivity).reset();
+ return tourActivity;
+ }
+
+ public void resetRouteStates(){
+ routeStates.clear();
+ }
+
+ public RouteState getRouteState(VehicleRoute route){
+ RouteState routeState = routeStates.get(route);
+ if(routeState == null){
+ routeState = new RouteState(route);
+ putRouteState(route, routeState);
+ }
+ return routeState;
+ }
+
+ private void putRouteState(VehicleRoute route, RouteState routeState){
+ routeStates.put(route, routeState);
+ }
+
+ void initialiseStateOfJobs(Collection jobs){
+ for(Job job : jobs){
+ if(job instanceof Service){
+ ServiceActivity service = ServiceActivity.newInstance((Service)job);
+ ActivityState state = new ActivityState(service);
+ tourActivities.put((Service) job, service);
+ activityStates.put(service, state);
+ }
+ else{
+ throw new IllegalStateException();
+ }
+ }
+ }
+
+ @Override
+ public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
+ resetRouteStates();
+ }
+
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/RuinRadial.java b/jsprit-core/src/main/java/algorithms/RuinRadial.java
new file mode 100644
index 00000000..82cb3ed6
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RuinRadial.java
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.TreeSet;
+
+import org.apache.log4j.Logger;
+
+import util.RandomNumberGeneration;
+import util.StopWatch;
+import basics.Job;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.algo.SearchStrategyModule;
+import basics.route.VehicleRoute;
+
+
+
+final class RuinRadial implements RuinStrategy {
+
+ private final static String NAME = "radialRuin";
+
+ /**
+ * returns a new creation of instance of ruinRadial
+ * @param vrp
+ * @param fraction TODO
+ * @param jobDistance
+ * @param jobRemover TODO
+ * @param routeUpdater TODO
+ * @return
+ */
+ static RuinRadial newInstance(VehicleRoutingProblem vrp, double fraction, JobDistance jobDistance, JobRemover jobRemover, VehicleRouteUpdater routeUpdater){
+ return new RuinRadial(vrp, fraction, jobDistance, jobRemover, routeUpdater);
+ }
+
+
+ static class ReferencedJob {
+ private Job job;
+ private double distance;
+
+ public ReferencedJob(Job job, double distance) {
+ super();
+ this.job = job;
+ this.distance = distance;
+ }
+
+ public Job getJob() {
+ return job;
+ }
+
+ public double getDistance() {
+ return distance;
+ }
+ }
+
+ private Logger logger = Logger.getLogger(RuinRadial.class);
+
+ private VehicleRoutingProblem vrp;
+
+ private double fractionOfAllNodes2beRuined;
+
+ private Map> distanceNodeTree = new HashMap>();
+
+ private Random random = RandomNumberGeneration.getRandom();
+
+ private JobDistance jobDistance;
+
+ private JobRemover jobRemover;
+
+ private VehicleRouteUpdater routeUpdater;
+
+ public void setRandom(Random random) {
+ this.random = random;
+ }
+
+ public RuinRadial(VehicleRoutingProblem vrp, double fraction, JobDistance jobDistance, JobRemover jobRemover, VehicleRouteUpdater routeUpdater) {
+ super();
+ this.vrp = vrp;
+ this.jobDistance = jobDistance;
+ this.jobRemover = jobRemover;
+ this.routeUpdater = routeUpdater;
+ this.fractionOfAllNodes2beRuined = fraction;
+ calculateDistancesFromJob2Job();
+ logger.info("intialise " + this);
+ }
+
+ public void setRuinFraction(double fractionOfAllNodes) {
+ this.fractionOfAllNodes2beRuined = fractionOfAllNodes;
+ logger.info("fraction set " + this);
+ }
+
+ private void calculateDistancesFromJob2Job() {
+ logger.info("preprocess distances between locations ...");
+ StopWatch stopWatch = new StopWatch();
+ stopWatch.start();
+ int nuOfDistancesStored = 0;
+ for (Job i : vrp.getJobs().values()) {
+ TreeSet treeSet = new TreeSet(
+ new Comparator() {
+ @Override
+ public int compare(ReferencedJob o1, ReferencedJob o2) {
+ if (o1.getDistance() <= o2.getDistance()) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ });
+ distanceNodeTree.put(i.getId(), treeSet);
+ for (Job j : vrp.getJobs().values()) {
+ double distance = jobDistance.calculateDistance(i, j);
+ ReferencedJob refNode = new ReferencedJob(j, distance);
+ treeSet.add(refNode);
+ nuOfDistancesStored++;
+ }
+ }
+ stopWatch.stop();
+ logger.info("preprocessing comp-time: " + stopWatch + "; nuOfDistances stored: " + nuOfDistancesStored + "; estimated memory: " +
+ (distanceNodeTree.keySet().size()*64+nuOfDistancesStored*92) + " bytes");
+ }
+
+ @Override
+ public String toString() {
+ return "[name=radialRuin][fraction="+fractionOfAllNodes2beRuined+"]";
+ }
+
+ @Override
+ public Collection ruin(Collection vehicleRoutes) {
+ if(vehicleRoutes.isEmpty()){
+ return Collections.EMPTY_LIST;
+ }
+ int nOfJobs2BeRemoved = getNuOfJobs2BeRemoved();
+ if (nOfJobs2BeRemoved == 0) {
+ return Collections.EMPTY_LIST;
+ }
+ Job randomJob = pickRandomJob();
+ Collection unassignedJobs = ruin(vehicleRoutes,randomJob,nOfJobs2BeRemoved);
+ return unassignedJobs;
+ }
+
+ public Collection ruin(Collection vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved){
+ List unassignedJobs = new ArrayList();
+ TreeSet tree = distanceNodeTree.get(targetJob.getId());
+ Iterator descendingIterator = tree.descendingIterator();
+ int counter = 0;
+ while (descendingIterator.hasNext() && counter < nOfJobs2BeRemoved) {
+ ReferencedJob refJob = descendingIterator.next();
+ Job job = refJob.getJob();
+ unassignedJobs.add(job);
+ counter++;
+ boolean removed = false;
+ for (VehicleRoute route : vehicleRoutes) {
+ removed = jobRemover.removeJobWithoutTourUpdate(job, route);
+ if (removed) {
+ break;
+ }
+ }
+ }
+ for(VehicleRoute route : vehicleRoutes){
+ routeUpdater.updateRoute(route);
+ }
+ return unassignedJobs;
+ }
+
+ private Job pickRandomJob() {
+ int totNuOfJobs = vrp.getJobs().values().size();
+ int randomIndex = random.nextInt(totNuOfJobs);
+ Job job = new ArrayList(vrp.getJobs().values()).get(randomIndex);
+ return job;
+ }
+
+ private int getNuOfJobs2BeRemoved() {
+ return (int) Math.ceil(vrp.getJobs().values().size()
+ * fractionOfAllNodes2beRuined);
+ }
+
+// @Override
+// public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) {
+// ruin(vrpSolution.getRoutes());
+// return vrpSolution;
+// }
+//
+// @Override
+// public String getName() {
+// return NAME;
+// }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/RuinRandom.java b/jsprit-core/src/main/java/algorithms/RuinRandom.java
new file mode 100644
index 00000000..9180f13a
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RuinRandom.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.log4j.Logger;
+
+import util.RandomNumberGeneration;
+import basics.Job;
+import basics.VehicleRoutingProblem;
+import basics.route.VehicleRoute;
+
+
+/**
+ * Ruin strategy that ruins current solution randomly. I.e.
+ * customer are removed randomly from current solution.
+ *
+ * @author stefan schroeder
+ *
+ */
+
+final class RuinRandom implements RuinStrategy {
+
+ public static RuinRandom newInstance(VehicleRoutingProblem vrp, double fraction, JobRemover jobRemover, VehicleRouteUpdater routeUpdater){
+ return new RuinRandom(vrp, fraction, jobRemover, routeUpdater);
+ }
+
+ private Logger logger = Logger.getLogger(RuinRandom.class);
+
+ private VehicleRoutingProblem vrp;
+
+ private double fractionOfAllNodes2beRuined;
+
+ private Random random = RandomNumberGeneration.getRandom();
+
+ private JobRemover jobRemover;
+
+ private VehicleRouteUpdater vehicleRouteUpdater;
+
+ public void setRandom(Random random) {
+ this.random = random;
+ }
+
+ /**
+ * Constructs ruinRandom.
+ *
+ * @param vrp
+ * @param fraction which is the fraction of total c
+ * @param jobRemover
+ * @param vehicleRouteUpdater
+ */
+ public RuinRandom(VehicleRoutingProblem vrp, double fraction, JobRemover jobRemover, VehicleRouteUpdater vehicleRouteUpdater) {
+ super();
+ this.vrp = vrp;
+ this.jobRemover = jobRemover;
+ this.vehicleRouteUpdater = vehicleRouteUpdater;
+ this.fractionOfAllNodes2beRuined = fraction;
+ logger.info("initialise " + this);
+ logger.info("done");
+ }
+
+ /**
+ * Removes a fraction of jobs from vehicleRoutes.
+ *
+ * The number of jobs is calculated as follows: Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined).
+ */
+ @Override
+ public Collection ruin(Collection vehicleRoutes) {
+ List unassignedJobs = new ArrayList();
+ int nOfJobs2BeRemoved = selectNuOfJobs2BeRemoved();
+ ruin(vehicleRoutes, nOfJobs2BeRemoved, unassignedJobs);
+ return unassignedJobs;
+ }
+
+ /**
+ * Removes nOfJobs2BeRemoved from vehicleRoutes, including targetJob.
+ */
+ @Override
+ public Collection ruin(Collection vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved) {
+ List unassignedJobs = new ArrayList();
+ if(targetJob != null){
+ boolean removed = false;
+ for (VehicleRoute route : vehicleRoutes) {
+ removed = jobRemover.removeJobWithoutTourUpdate(targetJob, route);
+ if (removed) {
+ nOfJobs2BeRemoved--;
+ unassignedJobs.add(targetJob);
+ break;
+ }
+ }
+ }
+ ruin(vehicleRoutes, nOfJobs2BeRemoved, unassignedJobs);
+ return unassignedJobs;
+ }
+
+ public void setRuinFraction(double fractionOfAllNodes2beRuined) {
+ this.fractionOfAllNodes2beRuined = fractionOfAllNodes2beRuined;
+ logger.info("fraction set " + this);
+ }
+
+ private void ruin(Collection vehicleRoutes,int nOfJobs2BeRemoved, List unassignedJobs) {
+ LinkedList availableJobs = new LinkedList(vrp.getJobs().values());
+ for (int i = 0; i < nOfJobs2BeRemoved; i++) {
+ Job job = pickRandomJob(availableJobs);
+ unassignedJobs.add(job);
+ availableJobs.remove(job);
+ for (VehicleRoute route : vehicleRoutes) {
+ boolean removed = jobRemover.removeJobWithoutTourUpdate(job, route);
+ if (removed) break;
+ }
+ }
+ updateRoutes(vehicleRoutes);
+ }
+
+ private void updateRoutes(Collection vehicleRoutes) {
+ for(VehicleRoute route : vehicleRoutes){
+ vehicleRouteUpdater.updateRoute(route);
+ }
+ }
+
+
+ @Override
+ public String toString() {
+ return "[name=randomRuin][fraction="+fractionOfAllNodes2beRuined+"]";
+ }
+
+ private Job pickRandomJob(LinkedList availableJobs) {
+ int randomIndex = random.nextInt(availableJobs.size());
+ return availableJobs.get(randomIndex);
+ }
+
+ private int selectNuOfJobs2BeRemoved() {
+ return (int) Math.ceil(vrp.getJobs().values().size() * fractionOfAllNodes2beRuined);
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/RuinStrategy.java b/jsprit-core/src/main/java/algorithms/RuinStrategy.java
new file mode 100644
index 00000000..67cf868a
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RuinStrategy.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Collection;
+import java.util.List;
+
+import basics.Job;
+import basics.route.VehicleRoute;
+
+
+
+
+/**
+ *
+ * @author stefan schroeder
+ *
+ */
+
+interface RuinStrategy {
+
+ public static interface RuinListener {
+
+ }
+
+ /**
+ * Ruins a current solution, i.e. removes jobs from service providers and
+ * returns a collection of these removed, and thus unassigned, jobs.
+ *
+ * @param vehicleRoutes
+ * @return
+ */
+ public Collection ruin(Collection vehicleRoutes);
+
+ public Collection ruin(Collection vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/RuinStrategyFactory.java b/jsprit-core/src/main/java/algorithms/RuinStrategyFactory.java
new file mode 100644
index 00000000..3d7bb19a
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/RuinStrategyFactory.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.VehicleRoutingProblem;
+
+interface RuinStrategyFactory {
+
+ public RuinStrategy createStrategy(VehicleRoutingProblem vrp);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/SchrimpfFactory.java b/jsprit-core/src/main/java/algorithms/SchrimpfFactory.java
new file mode 100644
index 00000000..3142c4f4
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/SchrimpfFactory.java
@@ -0,0 +1,65 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.net.URL;
+
+import basics.VehicleRoutingAlgorithm;
+import basics.VehicleRoutingProblem;
+import basics.io.AlgorithmConfig;
+import basics.io.AlgorithmConfigXmlReader;
+
+
+/**
+ * Factory that creates the {@link VehicleRoutingAlgorithm} as proposed by Schrimpf et al., 2000 with the following parameters:
+ *
+ *
+ * R&R_random (prob=0.5, F=0.5);
+ * R&R_radial (prob=0.5, F=0.3);
+ * threshold-accepting with exponentialDecayFunction (alpha=0.1, warmup-iterations=100);
+ * nuOfIterations=2000
+ *
+ *
Gerhard Schrimpf, Johannes Schneider, Hermann Stamm- Wilbrandt, and Gunter Dueck.
+ * Record breaking optimization results using the ruin and recreate principle.
+ * Journal of Computational Physics, 159(2):139 – 171, 2000. ISSN 0021-9991. doi: 10.1006/jcph.1999. 6413.
+ * URL http://www.sciencedirect.com/science/article/ pii/S0021999199964136
+ *
+ *
algorithm-xml-config is available at src/main/resources/schrimpf.xml.
+ *
+ * @author stefan schroeder
+ *
+ */
+public class SchrimpfFactory {
+
+ /**
+ * Creates the {@link VehicleRoutingAlgorithm}.
+ *
+ * @param vrp
+ * @return algorithm
+ */
+ public VehicleRoutingAlgorithm createAlgorithm(VehicleRoutingProblem vrp){
+ AlgorithmConfig algorithmConfig = new AlgorithmConfig();
+ URL resource = this.getClass().getClassLoader().getResource("schrimpf.xml");
+ new AlgorithmConfigXmlReader(algorithmConfig).read(resource.getPath());
+ return VehicleRoutingAlgorithms.createAlgorithm(vrp, algorithmConfig);
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/ScoredJob.java b/jsprit-core/src/main/java/algorithms/ScoredJob.java
new file mode 100644
index 00000000..b4da2991
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/ScoredJob.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.Job;
+import basics.route.VehicleRoute;
+
+
+
+class ScoredJob {
+
+ private final Job job;
+
+ private final double score;
+
+ private final InsertionData insertionData;
+
+ private final VehicleRoute route;
+
+ public ScoredJob(final Job job, final double score, final InsertionData insertionData, final VehicleRoute route) {
+ super();
+ this.job = job;
+ this.score = score;
+ this.insertionData = insertionData;
+ this.route = route;
+ }
+
+ public InsertionData getInsertionData() {
+ return insertionData;
+ }
+
+ public VehicleRoute getRoute() {
+ return route;
+ }
+
+ public Job getJob() {
+ return job;
+ }
+
+ public double getScore() {
+ return score;
+ }
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/SolutionVerifier.java b/jsprit-core/src/main/java/algorithms/SolutionVerifier.java
new file mode 100644
index 00000000..69566285
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/SolutionVerifier.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import basics.Job;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblemSolution;
+import basics.algo.AlgorithmEndsListener;
+import basics.route.VehicleRoute;
+
+class SolutionVerifier implements AlgorithmEndsListener{
+
+ @Override
+ public void informAlgorithmEnds(VehicleRoutingProblem problem, Collection solutions) {
+
+ for(VehicleRoutingProblemSolution solution : solutions){
+ Set jobsInSolution = new HashSet();
+ for(VehicleRoute route : solution.getRoutes()){
+ jobsInSolution.addAll(route.getTourActivities().getJobs());
+ }
+ if(jobsInSolution.size() != problem.getJobs().size()){
+ throw new IllegalStateException("we are at the end of the algorithm and still have not found a valid solution." +
+ "This cannot be.");
+ }
+ }
+
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/TourConstraintEngine.java b/jsprit-core/src/main/java/algorithms/TourConstraintEngine.java
new file mode 100644
index 00000000..6cd3e758
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/TourConstraintEngine.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.Job;
+import basics.route.VehicleRoute;
+
+class TourConstraintEngine {
+
+// void ini(VehicleRoute route, Job job){
+//
+// }
+//
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/TourStateUpdater.java b/jsprit-core/src/main/java/algorithms/TourStateUpdater.java
new file mode 100644
index 00000000..b46af2ec
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/TourStateUpdater.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import org.apache.log4j.Logger;
+
+import basics.costs.VehicleRoutingActivityCosts;
+import basics.costs.VehicleRoutingTransportCosts;
+import basics.route.VehicleRoute;
+
+
+
+
+
+/**
+ * Updates tour state, i.e. the tour as well as each activity in that tour has a state such as currentLoad, currentCost, earliestOperationTime and
+ * latestOperationTime. Each time the tour is changed (for instance by removing or adding an activity), tour and activity states
+ * might change, thus this updater updates activity states.
+ * This includes:
+ * - update load and totalCost at tour-level
+ * - update currentLoad and currentCost at activity-level
+ * - update earliest- and latestOperationStart values at activity-level
+ *
+ * If ensureFeasibility is true, then it additionally checks whether the earliestOperationStartTime is higher than the latestOperationStartTime.
+ * If it is, it returns a false value to indicate that the tour is not feasible. This makes only sense for hard-timewindows.
+ *
+ * If softTimeWindow is set to true, latestOperationStartTimes are not updated and the tour is always feasible.
+ *
+ * @author stefan schroeder
+ *
+ */
+
+class TourStateUpdater implements VehicleRouteUpdater{
+
+// public final static Counter counter = new Counter("#updateTWProcesses: ");
+
+ private static Logger logger = Logger.getLogger(TourStateUpdater.class);
+
+ private boolean ensureFeasibility = true;
+
+ private UpdateTourStatesForwardInTime forwardUpdate;
+
+ private UpdateTourStatesBackwardInTime backwardUpdate;
+
+ private boolean updateTimeWindows = true;
+
+ private RouteStates actStates;
+
+ public TourStateUpdater(RouteStates activityStates, VehicleRoutingTransportCosts costs, VehicleRoutingActivityCosts costFunction) {
+ super();
+ forwardUpdate = new UpdateTourStatesForwardInTime(costs, costs, costFunction);
+ backwardUpdate = new UpdateTourStatesBackwardInTime(costs);
+ actStates=activityStates;
+ forwardUpdate.setActivityStates(actStates);
+ backwardUpdate.setActivityStates(actStates);
+ }
+
+ /*
+ *
+ */
+ public boolean updateRoute(VehicleRoute vehicleRoute) {
+ if(updateTimeWindows){
+ backwardUpdate.checkFeasibility = ensureFeasibility;
+ backwardUpdate.updateRoute(vehicleRoute);
+ }
+ forwardUpdate.updateRoute(vehicleRoute);
+ boolean tourIsFeasible = true;
+
+ return tourIsFeasible;
+ }
+
+ public void setTimeWindowUpdate(boolean updateTimeWindows) {
+ this.updateTimeWindows = updateTimeWindows;
+ logger.info("set timeWindowUpdate to " + updateTimeWindows);
+ }
+
+ public void setEnsureFeasibility(boolean ensureFeasibility) {
+ this.ensureFeasibility = ensureFeasibility;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/UpdateTourStatesBackwardInTime.java b/jsprit-core/src/main/java/algorithms/UpdateTourStatesBackwardInTime.java
new file mode 100644
index 00000000..19a8d5e3
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/UpdateTourStatesBackwardInTime.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Iterator;
+
+import org.apache.log4j.Logger;
+
+import algorithms.RouteStates.ActivityState;
+import basics.costs.BackwardTransportTime;
+import basics.route.Start;
+import basics.route.TourActivities;
+import basics.route.TourActivity;
+import basics.route.VehicleRoute;
+
+
+
+
+
+
+/**
+ *
+ * @author stefan schroeder
+ *
+ */
+
+class UpdateTourStatesBackwardInTime implements VehicleRouteUpdater{
+
+// public static Counter counter = new Counter("#updateTWProcesses: ");
+
+ private static Logger log = Logger.getLogger(UpdateTourStatesBackwardInTime.class);
+
+ public boolean checkFeasibility = true;
+
+ private BackwardTransportTime transportTime;
+
+ private RouteStates actStates;
+
+ public void setActivityStates(RouteStates actStates){
+ this.actStates = actStates;
+ }
+
+ public ActivityState state(TourActivity act){
+ return actStates.getState(act);
+ }
+
+ public UpdateTourStatesBackwardInTime(BackwardTransportTime transportTime) {
+ super();
+ this.transportTime = transportTime;
+ }
+
+ /*
+ *
+ */
+ public boolean updateRoute(VehicleRoute vehicleRoute) {
+ boolean ok = update(vehicleRoute);
+ return ok;
+ }
+
+
+
+ private boolean update(VehicleRoute vehicleRoute) {
+ TourActivities tour = vehicleRoute.getTourActivities();
+ int tourSize = tour.getActivities().size();
+ Iterator reverseActIter = vehicleRoute.getTourActivities().reverseActivityIterator();
+ TourActivity prevAct;
+ boolean feasible = true;
+ prevAct = vehicleRoute.getEnd();
+ double startAtPrevAct = prevAct.getTheoreticalLatestOperationStartTime();
+ int count = 0;
+ while(reverseActIter.hasNext()){
+ TourActivity currAct = reverseActIter.next();
+
+ double latestOperationStartTime = latestOperationStartTime(vehicleRoute, prevAct, currAct, startAtPrevAct);
+ ActivityState state = state(currAct);
+ state.setLatestOperationStart(latestOperationStartTime);
+ prevAct = currAct;
+ startAtPrevAct = latestOperationStartTime;
+ count++;
+ }
+// Start start = vehicleRoute.getStart();
+// double latestOperationStartTime = latestOperationStartTime(vehicleRoute, prevAct, start, startAtPrevAct);
+ assert count == tourSize;
+ return feasible;
+ }
+
+ private double latestOperationStartTime(VehicleRoute vehicleRoute,
+ TourActivity prevAct, TourActivity currAct, double startAtPrevAct) {
+ double latestDepTimeAtCurrAct = startAtPrevAct - transportTime.getBackwardTransportTime(currAct.getLocationId(), prevAct.getLocationId(), startAtPrevAct, vehicleRoute.getDriver(),vehicleRoute.getVehicle());
+ double potentialLatestOperationStartTimeAtCurrAct = latestDepTimeAtCurrAct - currAct.getOperationTime();
+ double latestOperationStartTime = Math.min(currAct.getTheoreticalLatestOperationStartTime(), potentialLatestOperationStartTimeAtCurrAct);
+ return latestOperationStartTime;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/UpdateTourStatesForwardInTime.java b/jsprit-core/src/main/java/algorithms/UpdateTourStatesForwardInTime.java
new file mode 100644
index 00000000..96732e5c
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/UpdateTourStatesForwardInTime.java
@@ -0,0 +1,156 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import org.apache.log4j.Logger;
+
+import algorithms.RouteStates.ActivityState;
+import basics.costs.ForwardTransportCost;
+import basics.costs.ForwardTransportTime;
+import basics.costs.VehicleRoutingActivityCosts;
+import basics.route.Driver;
+import basics.route.End;
+import basics.route.ServiceActivity;
+import basics.route.TourActivities;
+import basics.route.TourActivity;
+import basics.route.Vehicle;
+import basics.route.VehicleRoute;
+
+
+/**
+ *
+ * @author sschroeder
+ *
+ */
+
+class UpdateTourStatesForwardInTime implements VehicleRouteUpdater{
+
+// public static Counter counter = new Counter("#updateTWProcesses: ");
+ private static Logger log = Logger.getLogger(UpdateTourStatesForwardInTime.class);
+
+ public boolean checkFeasibility = true;
+
+ private VehicleRoutingActivityCosts activityCost;
+
+ private ForwardTransportTime transportTime;
+
+ private ForwardTransportCost transportCost;
+
+ private RouteStates routeStates;
+
+ private boolean activityStatesSet = false;
+
+ public void setActivityStates(RouteStates actStates){
+ this.routeStates = actStates;
+ activityStatesSet = true;
+ }
+
+ public ActivityState state(TourActivity act){
+ return routeStates.getState(act);
+ }
+
+ public UpdateTourStatesForwardInTime(ForwardTransportTime transportTime, ForwardTransportCost transportCost, VehicleRoutingActivityCosts activityCost) {
+ super();
+ this.transportTime = transportTime;
+ this.transportCost = transportCost;
+ this.activityCost = activityCost;
+ }
+
+ /**
+ *
+ *
+ */
+ public boolean updateRoute(VehicleRoute vehicleRoute) {
+ vehicleRoute.getVehicleRouteCostCalculator().reset();
+
+ Vehicle vehicle = vehicleRoute.getVehicle();
+ Driver driver = vehicleRoute.getDriver();
+
+ TourActivity prevAct = vehicleRoute.getStart();
+
+ double startAtPrevAct = vehicleRoute.getStart().getEndTime();
+
+ double totalOperationCost = 0.0;
+ int totalLoadPicked = 0;
+ int currentLoadState = 0;
+
+ for(TourActivity currentAct : vehicleRoute.getTourActivities().getActivities()){
+ totalLoadPicked += getPickedLoad(currentAct);
+ currentLoadState += getCapDemand(currentAct);
+
+ double transportTime = this.transportTime.getTransportTime(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
+
+ double arrivalTimeAtCurrAct = startAtPrevAct + transportTime;
+ double operationStartTime = Math.max(currentAct.getTheoreticalEarliestOperationStartTime(), arrivalTimeAtCurrAct);
+
+ double operationEndTime = operationStartTime + currentAct.getOperationTime();
+
+ currentAct.setArrTime(arrivalTimeAtCurrAct);
+ currentAct.setEndTime(operationEndTime);
+
+ double transportCost = this.transportCost.getTransportCost(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
+ double actCost = activityCost.getActivityCost(currentAct, arrivalTimeAtCurrAct, driver, vehicle);
+
+ vehicleRoute.getVehicleRouteCostCalculator().addTransportCost(transportCost);
+ vehicleRoute.getVehicleRouteCostCalculator().addActivityCost(actCost);
+
+ totalOperationCost += transportCost;
+ totalOperationCost += actCost;
+
+ if(activityStatesSet){
+ ActivityState currentState = state(currentAct);
+ currentState.setEarliestOperationStart(operationStartTime);
+ currentState.setCurrentLoad(currentLoadState);
+ currentState.setCurrentCost(totalOperationCost);
+ }
+
+ prevAct = currentAct;
+ startAtPrevAct = operationEndTime;
+ }
+
+ End currentAct = vehicleRoute.getEnd();
+ double transportCost = this.transportCost.getTransportCost(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
+ double transportTime = this.transportTime.getTransportTime(prevAct.getLocationId(), currentAct.getLocationId(), startAtPrevAct, driver, vehicle);
+ double arrivalTimeAtCurrAct = startAtPrevAct + transportTime;
+
+ currentAct.setArrTime(arrivalTimeAtCurrAct);
+ currentAct.setEndTime(arrivalTimeAtCurrAct);
+
+ totalOperationCost += transportCost;
+
+ routeStates.getRouteState(vehicleRoute).setCosts(totalOperationCost);
+ routeStates.getRouteState(vehicleRoute).setLoad(totalLoadPicked);
+
+ vehicleRoute.getVehicleRouteCostCalculator().addTransportCost(transportCost);
+
+ vehicleRoute.getVehicleRouteCostCalculator().price(vehicleRoute.getDriver());
+ vehicleRoute.getVehicleRouteCostCalculator().price(vehicleRoute.getVehicle());
+ vehicleRoute.getVehicleRouteCostCalculator().finish();
+ return true;
+ }
+
+ private int getCapDemand(TourActivity currentAct) {
+ return currentAct.getCapacityDemand();
+ }
+
+ private double getPickedLoad(TourActivity currentAct) {
+ if(currentAct instanceof ServiceActivity){
+ return currentAct.getCapacityDemand();
+ }
+// else if(currentAct instanceof Pickup){
+// return currentAct.getCapacityDemand();
+// }
+ return 0.0;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/VehicleFleetManager.java b/jsprit-core/src/main/java/algorithms/VehicleFleetManager.java
new file mode 100644
index 00000000..afe104f0
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/VehicleFleetManager.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.Collection;
+
+import basics.route.Vehicle;
+import basics.route.VehicleImpl;
+import basics.route.VehicleImpl.VehicleType;
+
+interface VehicleFleetManager {
+
+ public static class TypeKey {
+
+ public final VehicleType type;
+ public final String locationId;
+
+ public TypeKey(VehicleType type, String locationId) {
+ super();
+ this.type = type;
+ this.locationId = locationId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((locationId == null) ? 0 : locationId.hashCode());
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ TypeKey other = (TypeKey) obj;
+ if (locationId == null) {
+ if (other.locationId != null)
+ return false;
+ } else if (!locationId.equals(other.locationId))
+ return false;
+ if (type == null) {
+ if (other.type != null)
+ return false;
+ } else if (!type.equals(other.type))
+ return false;
+ return true;
+ }
+
+
+ }
+
+ public abstract Vehicle getEmptyVehicle(TypeKey typeId);
+
+ public abstract Collection getAvailableVehicleTypes();
+
+ public abstract void lock(Vehicle vehicle);
+
+ public abstract void unlock(Vehicle vehicle);
+
+ public abstract Collection getAvailableVehicleTypes(TypeKey withoutThisType);
+
+ public abstract boolean isLocked(Vehicle vehicle);
+
+ public abstract void unlockAll();
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/VehicleFleetManagerImpl.java b/jsprit-core/src/main/java/algorithms/VehicleFleetManagerImpl.java
new file mode 100644
index 00000000..a6f30556
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/VehicleFleetManagerImpl.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+import basics.route.Vehicle;
+import basics.route.VehicleImpl.NoVehicle;
+
+
+
+class VehicleFleetManagerImpl implements VehicleFleetManager {
+
+ public VehicleFleetManagerImpl newInstance(Collection vehicles){
+ return new VehicleFleetManagerImpl(vehicles);
+ }
+
+ public static VehicleFleetManager createDefaultFleetManager() {
+ return new DefaultFleetManager();
+ }
+
+ public static class DefaultFleetManager extends VehicleFleetManagerImpl {
+
+ public DefaultFleetManager() {
+ super(Collections.EMPTY_LIST);
+
+ }
+
+ }
+
+ static class TypeContainer {
+
+ private TypeKey type;
+
+ private ArrayList vehicleList;
+
+ public TypeContainer(TypeKey type) {
+ super();
+ this.type = type;
+ vehicleList = new ArrayList();
+ }
+
+ void add(Vehicle vehicle){
+ if(vehicleList.contains(vehicle)){
+ throw new IllegalStateException("cannot add vehicle twice " + vehicle.getId());
+ }
+ vehicleList.add(vehicle);
+ }
+
+ void remove(Vehicle vehicle){
+ vehicleList.remove(vehicle);
+ }
+
+ public Vehicle getVehicle() {
+ return vehicleList.get(0);
+// return vehicleList.getFirst();
+ }
+
+ public boolean isEmpty() {
+ return vehicleList.isEmpty();
+ }
+
+ }
+
+ private static Logger logger = Logger.getLogger(VehicleFleetManagerImpl.class);
+
+ private Collection vehicles;
+
+ private Set lockedVehicles;
+
+ private Map typeMapOfAvailableVehicles;
+
+ public VehicleFleetManagerImpl(Collection vehicles) {
+ super();
+ this.vehicles = vehicles;
+ this.lockedVehicles = new HashSet();
+ makeMap();
+ logger.info("initialise " + this);
+ }
+
+ public VehicleFleetManagerImpl(Collection vehicles, Collection lockedVehicles) {
+ this.vehicles = vehicles;
+ makeMap();
+ this.lockedVehicles = new HashSet();
+ for(Vehicle v : lockedVehicles){
+ lock(v);
+ }
+ logger.info("initialise " + this);
+ }
+
+ @Override
+ public String toString() {
+ return "[name=finiteVehicles]";
+ }
+
+ private void makeMap() {
+ typeMapOfAvailableVehicles = new HashMap();
+ for(Vehicle v : vehicles){
+ addVehicle(v);
+ }
+ }
+
+ private void addVehicle(Vehicle v) {
+ if(v.getType() == null){
+ throw new IllegalStateException("vehicle needs type");
+ }
+// String typeId = v.getType().typeId;
+ TypeKey typeKey = new TypeKey(v.getType(),v.getLocationId());
+ if(!typeMapOfAvailableVehicles.containsKey(typeKey)){
+ typeMapOfAvailableVehicles.put(typeKey, new TypeContainer(typeKey));
+ }
+ typeMapOfAvailableVehicles.get(typeKey).add(v);
+ }
+
+ private void removeVehicle(Vehicle v){
+ TypeKey key = new TypeKey(v.getType(),v.getLocationId());
+ if(typeMapOfAvailableVehicles.containsKey(key)){
+ typeMapOfAvailableVehicles.get(key).remove(v);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getEmptyVehicle(java.lang.String)
+ */
+ @Override
+ public Vehicle getEmptyVehicle(TypeKey typeId){
+ Vehicle v = null;
+ if(typeMapOfAvailableVehicles.containsKey(typeId)){
+ v = typeMapOfAvailableVehicles.get(typeId).getVehicle();
+ }
+ return v;
+ }
+
+ /* (non-Javadoc)
+ * @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getAvailableVehicleTypes()
+ */
+ @Override
+ public Collection getAvailableVehicleTypes(){
+ List types = new ArrayList();
+ for(TypeKey key : typeMapOfAvailableVehicles.keySet()){
+ if(!typeMapOfAvailableVehicles.get(key).isEmpty()){
+ types.add(key);
+ }
+ }
+ return types;
+ }
+
+ /* (non-Javadoc)
+ * @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#lock(org.matsim.contrib.freight.vrp.basics.Vehicle)
+ */
+ @Override
+ public void lock(Vehicle vehicle){
+ if(vehicles.isEmpty() || vehicle instanceof NoVehicle){
+ return;
+ }
+ boolean locked = lockedVehicles.add(vehicle);
+ removeVehicle(vehicle);
+ if(!locked){
+ throw new IllegalStateException("cannot lock vehicle twice " + vehicle.getId());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#unlock(org.matsim.contrib.freight.vrp.basics.Vehicle)
+ */
+ @Override
+ public void unlock(Vehicle vehicle){
+ if(vehicles.isEmpty() || vehicle instanceof NoVehicle){
+ return;
+ }
+ if(vehicle == null) return;
+ lockedVehicles.remove(vehicle);
+ addVehicle(vehicle);
+ }
+
+ /* (non-Javadoc)
+ * @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#getAvailableVehicleTypes(java.lang.String)
+ */
+ @Override
+ public Collection getAvailableVehicleTypes(TypeKey withoutThisType) {
+ List types = new ArrayList();
+ for(TypeKey typeKey : typeMapOfAvailableVehicles.keySet()){
+ if(typeKey.equals(withoutThisType)){
+ continue;
+ }
+ if(!typeMapOfAvailableVehicles.get(typeKey).isEmpty()){
+ types.add(typeKey);
+ }
+ }
+ return types;
+ }
+
+ /* (non-Javadoc)
+ * @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#isLocked(org.matsim.contrib.freight.vrp.basics.Vehicle)
+ */
+ @Override
+ public boolean isLocked(Vehicle vehicle) {
+ return lockedVehicles.contains(vehicle);
+ }
+
+ /* (non-Javadoc)
+ * @see org.matsim.contrib.freight.vrp.basics.VehicleFleetManager#unlockAll()
+ */
+ @Override
+ public void unlockAll() {
+ Collection locked = new ArrayList(lockedVehicles);
+ for(Vehicle v : locked){
+ unlock(v);
+ }
+ if(!lockedVehicles.isEmpty()){
+ throw new IllegalStateException("no vehicle must be locked");
+ }
+ }
+
+ public int sizeOfLockedVehicles(){
+ return lockedVehicles.size();
+ }
+
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/VehicleRouteUpdater.java b/jsprit-core/src/main/java/algorithms/VehicleRouteUpdater.java
new file mode 100644
index 00000000..ab0efa4e
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/VehicleRouteUpdater.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2011 Stefan Schroeder.
+ * eMail: stefan.schroeder@kit.edu
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the GNU Public License v2.0
+ * which accompanies this distribution, and is available at
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+import basics.route.VehicleRoute;
+
+
+/**
+ * Updater that updates a vehicleRoute, e.g. the total costs or the time-windows.
+ *
+ * @author stefan schroeder
+ *
+ */
+
+interface VehicleRouteUpdater {
+
+ public boolean updateRoute(VehicleRoute vehicleRoute);
+
+}
diff --git a/jsprit-core/src/main/java/algorithms/VehicleRoutingAlgorithms.java b/jsprit-core/src/main/java/algorithms/VehicleRoutingAlgorithms.java
new file mode 100644
index 00000000..302a2ff7
--- /dev/null
+++ b/jsprit-core/src/main/java/algorithms/VehicleRoutingAlgorithms.java
@@ -0,0 +1,870 @@
+/*******************************************************************************
+ * Copyright (C) 2013 Stefan Schroeder
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributors:
+ * Stefan Schroeder - initial API and implementation
+ ******************************************************************************/
+package algorithms;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.log4j.Logger;
+
+import util.RouteUtils;
+import algorithms.VehicleRoutingAlgorithms.TypedMap.AbstractInsertionKey;
+import algorithms.VehicleRoutingAlgorithms.TypedMap.AbstractKey;
+import algorithms.VehicleRoutingAlgorithms.TypedMap.AcceptorKey;
+import algorithms.VehicleRoutingAlgorithms.TypedMap.RuinStrategyKey;
+import algorithms.VehicleRoutingAlgorithms.TypedMap.SelectorKey;
+import algorithms.VehicleRoutingAlgorithms.TypedMap.StrategyModuleKey;
+import algorithms.acceptors.AcceptNewIfBetterThanWorst;
+import algorithms.acceptors.AcceptNewRemoveFirst;
+import algorithms.acceptors.SchrimpfAcceptance;
+import algorithms.acceptors.SolutionAcceptor;
+import algorithms.selectors.SelectBest;
+import algorithms.selectors.SelectRandomly;
+import algorithms.selectors.SolutionSelector;
+import basics.Job;
+import basics.VehicleRoutingAlgorithm;
+import basics.VehicleRoutingProblem;
+import basics.VehicleRoutingProblem.FleetSize;
+import basics.VehicleRoutingProblemSolution;
+import basics.algo.AlgorithmStartsListener;
+import basics.algo.InsertionListener;
+import basics.algo.SearchStrategy;
+import basics.algo.SearchStrategyManager;
+import basics.algo.SearchStrategyModule;
+import basics.algo.SearchStrategyModuleListener;
+import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener;
+import basics.algo.VehicleRoutingAlgorithmListeners.Priority;
+import basics.io.AlgorithmConfig;
+import basics.io.AlgorithmConfigXmlReader;
+import basics.route.VehicleRoute;
+
+
+
+public class VehicleRoutingAlgorithms {
+
+ static class TypedMap {
+
+ static interface AbstractKey {
+
+ Class