From 695d7f3a07af62bb9b4e92ec105089a20f4c89a2 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Thu, 31 Oct 2013 14:31:53 +0100 Subject: [PATCH 01/17] clean actCosts --- .../src/main/java/basics/costs/VehicleRoutingActivityCosts.java | 1 - 1 file changed, 1 deletion(-) diff --git a/jsprit-core/src/main/java/basics/costs/VehicleRoutingActivityCosts.java b/jsprit-core/src/main/java/basics/costs/VehicleRoutingActivityCosts.java index deaefca2..5b14793b 100644 --- a/jsprit-core/src/main/java/basics/costs/VehicleRoutingActivityCosts.java +++ b/jsprit-core/src/main/java/basics/costs/VehicleRoutingActivityCosts.java @@ -62,6 +62,5 @@ public interface VehicleRoutingActivityCosts { */ public double getActivityCost(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle); -// public Parameter getParameter(TourActivity tourAct, Vehicle vehicle, Driver driver); } From 917e4a732a4b0c2c479d94de27f143c914a3d473 Mon Sep 17 00:00:00 2001 From: oblonski <4sschroeder@gmail.com> Date: Sat, 2 Nov 2013 07:46:38 +0100 Subject: [PATCH 02/17] bugfix: issue #51 --- .../algorithms/CalculatesServiceInsertionOnRouteLevel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionOnRouteLevel.java b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionOnRouteLevel.java index 88a848a1..5d5c2e98 100644 --- a/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionOnRouteLevel.java +++ b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionOnRouteLevel.java @@ -283,7 +283,9 @@ final class CalculatesServiceInsertionOnRouteLevel implements JobInsertionCalcul } } if(best_insertion_index == InsertionData.NO_INDEX) return InsertionData.noInsertionFound(); - return new InsertionData(best_insertion_costs, InsertionData.NO_INDEX, best_insertion_index, newVehicle, newDriver); + InsertionData insertionData = new InsertionData(best_insertion_costs, InsertionData.NO_INDEX, best_insertion_index, newVehicle, newDriver); + insertionData.setVehicleDepartureTime(newVehicleDepartureTime); + return insertionData; } /** From d9b5d8ee96abb61495d754da40be1386e4d5a77b Mon Sep 17 00:00:00 2001 From: oblonski <4sschroeder@gmail.com> Date: Sat, 2 Nov 2013 07:47:03 +0100 Subject: [PATCH 03/17] experimental timeScheduler --- ...lculatesServiceInsertionWithTimeScheduling.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionWithTimeScheduling.java b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionWithTimeScheduling.java index b96999ec..d813dfeb 100644 --- a/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionWithTimeScheduling.java +++ b/jsprit-core/src/main/java/algorithms/CalculatesServiceInsertionWithTimeScheduling.java @@ -27,6 +27,12 @@ import basics.route.Driver; import basics.route.Vehicle; import basics.route.VehicleRoute; +/** + * This is experimental. It ignores vehicles' earliestStartTime. + * + * @author schroeder + * + */ class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCalculator{ private static Logger log = Logger.getLogger(CalculatesServiceInsertionWithTimeScheduling.class); @@ -67,9 +73,13 @@ class CalculatesServiceInsertionWithTimeScheduling implements JobInsertionCalcul for(int i=0;i earliestDeparture) vehicleDepartureTimes.add(neighborStartTime_earlier); +// if(neighborStartTime_earlier > earliestDeparture) { + vehicleDepartureTimes.add(neighborStartTime_earlier); +// } double neighborStartTime_later = currentStart + (i+1)*timeSlice; - if(neighborStartTime_later < latestEnd) vehicleDepartureTimes.add(neighborStartTime_later); +// if(neighborStartTime_later < latestEnd) { + vehicleDepartureTimes.add(neighborStartTime_later); +// } } InsertionData bestIData = null; From db888e5c1ef649f2d2eca13dc4d0cdc5bacd7bce Mon Sep 17 00:00:00 2001 From: oblonski <4sschroeder@gmail.com> Date: Sun, 3 Nov 2013 04:26:59 +0100 Subject: [PATCH 04/17] add example 'ConfigureAlgorithInCodeInsteadOfPerXml' - issue #52 --- ...nfigureAlgorithmInCodeInsteadOfPerXml.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 jsprit-examples/src/main/java/examples/ConfigureAlgorithmInCodeInsteadOfPerXml.java diff --git a/jsprit-examples/src/main/java/examples/ConfigureAlgorithmInCodeInsteadOfPerXml.java b/jsprit-examples/src/main/java/examples/ConfigureAlgorithmInCodeInsteadOfPerXml.java new file mode 100644 index 00000000..96497237 --- /dev/null +++ b/jsprit-examples/src/main/java/examples/ConfigureAlgorithmInCodeInsteadOfPerXml.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (C) 2013 Stefan Schroeder + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + ******************************************************************************/ +package examples; + +import java.io.File; +import java.util.Collection; + +import org.apache.commons.configuration.XMLConfiguration; + +import util.Coordinate; +import util.Solutions; +import algorithms.VehicleRoutingAlgorithms; +import analysis.SolutionPlotter; +import analysis.SolutionPrinter; +import analysis.SolutionPrinter.Print; +import basics.Service; +import basics.VehicleRoutingAlgorithm; +import basics.VehicleRoutingProblem; +import basics.VehicleRoutingProblemSolution; +import basics.io.AlgorithmConfig; +import basics.io.VrpXMLWriter; +import basics.route.Vehicle; +import basics.route.VehicleImpl; +import basics.route.VehicleImpl.Builder; +import basics.route.VehicleType; +import basics.route.VehicleTypeImpl; + +public class ConfigureAlgorithmInCodeInsteadOfPerXml { + + public static void main(String[] args) { + /* + * some preparation - create output folder + */ + File dir = new File("output"); + // if the directory does not exist, create it + if (!dir.exists()){ + System.out.println("creating directory ./output"); + boolean result = dir.mkdir(); + if(result) System.out.println("./output created"); + } + + /* + * get a vehicle type-builder and build a type with the typeId "vehicleType" and a capacity of 2 + */ + VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder.newInstance("vehicleType", 2); + VehicleType vehicleType = vehicleTypeBuilder.build(); + + /* + * get a vehicle-builder and build a vehicle located at (10,10) with type "vehicleType" + */ + Builder vehicleBuilder = VehicleImpl.Builder.newInstance("vehicle"); + vehicleBuilder.setLocationCoord(Coordinate.newInstance(10, 10)); + vehicleBuilder.setType(vehicleType); + Vehicle vehicle = vehicleBuilder.build(); + + /* + * build services at the required locations, each with a capacity-demand of 1. + */ + Service service1 = Service.Builder.newInstance("1", 1).setCoord(Coordinate.newInstance(5, 7)).build(); + Service service2 = Service.Builder.newInstance("2", 1).setCoord(Coordinate.newInstance(5, 13)).build(); + + Service service3 = Service.Builder.newInstance("3", 1).setCoord(Coordinate.newInstance(15, 7)).build(); + Service service4 = Service.Builder.newInstance("4", 1).setCoord(Coordinate.newInstance(15, 13)).build(); + + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + vrpBuilder.addVehicle(vehicle); + vrpBuilder.addService(service1).addService(service2).addService(service3).addService(service4); + + VehicleRoutingProblem problem = vrpBuilder.build(); + + /* + * get the algorithm out-of-the-box. + */ + AlgorithmConfig algorithmConfig = getAlgorithmConfig(); + VehicleRoutingAlgorithm algorithm = VehicleRoutingAlgorithms.createAlgorithm(problem,algorithmConfig); + + /* + * and search a solution + */ + Collection solutions = algorithm.searchSolutions(); + + /* + * get the best + */ + VehicleRoutingProblemSolution bestSolution = Solutions.getBest(solutions); + + new VrpXMLWriter(problem, solutions).write("output/problem-with-solution.xml"); + + SolutionPrinter.print(bestSolution,Print.VERBOSE); + + /* + * plot + */ + SolutionPlotter.plotSolutionAsPNG(problem, bestSolution, "output/solution.png", "solution"); + } + + private static AlgorithmConfig getAlgorithmConfig() { + AlgorithmConfig config = new AlgorithmConfig(); + XMLConfiguration xmlConfig = config.getXMLConfiguration(); + xmlConfig.setProperty("iterations", 2000); + xmlConfig.setProperty("construction.insertion[@name]","bestInsertion"); + + xmlConfig.setProperty("strategy.memory", 1); + String searchStrategy = "strategy.searchStrategies.searchStrategy"; + + xmlConfig.setProperty(searchStrategy + "(0).selector[@name]","selectBest"); + xmlConfig.setProperty(searchStrategy + "(0).acceptor[@name]","acceptNewRemoveWorst"); + xmlConfig.setProperty(searchStrategy + "(0).modules.module(0)[@name]","ruin_and_recreate"); + xmlConfig.setProperty(searchStrategy + "(0).modules.module(0).ruin[@name]","randomRuin"); + xmlConfig.setProperty(searchStrategy + "(0).modules.module(0).ruin.share","0.3"); + xmlConfig.setProperty(searchStrategy + "(0).modules.module(0).insertion[@name]","bestInsertion"); + xmlConfig.setProperty(searchStrategy + "(0).probability","0.5"); + + xmlConfig.setProperty(searchStrategy + "(1).selector[@name]","selectBest"); + xmlConfig.setProperty(searchStrategy + "(1).acceptor[@name]","acceptNewRemoveWorst"); + xmlConfig.setProperty(searchStrategy + "(1).modules.module(0)[@name]","ruin_and_recreate"); + xmlConfig.setProperty(searchStrategy + "(1).modules.module(0).ruin[@name]","radialRuin"); + xmlConfig.setProperty(searchStrategy + "(1).modules.module(0).ruin.share","0.15"); + xmlConfig.setProperty(searchStrategy + "(1).modules.module(0).insertion[@name]","bestInsertion"); + xmlConfig.setProperty(searchStrategy + "(1).probability","0.5"); + + return config; + } + +} From 1b1dea71e9e1f2354f468d4c5fb38aa3c7c41071 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Wed, 6 Nov 2013 16:10:15 +0100 Subject: [PATCH 05/17] change visibility to public --- .../src/main/java/algorithms/InsertionStrategyFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsprit-core/src/main/java/algorithms/InsertionStrategyFactory.java b/jsprit-core/src/main/java/algorithms/InsertionStrategyFactory.java index a08f8b79..024a8615 100644 --- a/jsprit-core/src/main/java/algorithms/InsertionStrategyFactory.java +++ b/jsprit-core/src/main/java/algorithms/InsertionStrategyFactory.java @@ -18,7 +18,7 @@ package algorithms; import basics.VehicleRoutingProblem; -interface InsertionStrategyFactory { +public interface InsertionStrategyFactory { public InsertionStrategy createStrategy(VehicleRoutingProblem vrp); From ce4c3acc396e7f3aa3d61437008876b4bf32749a Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Wed, 6 Nov 2013 16:10:43 +0100 Subject: [PATCH 06/17] remove interface InsertionStrategyBuilder --- .../src/main/java/algorithms/BestInsertionBuilder.java | 4 ++-- .../src/main/java/algorithms/InsertionStrategyBuilder.java | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 jsprit-core/src/main/java/algorithms/InsertionStrategyBuilder.java diff --git a/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java b/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java index b13773ff..d2efe07e 100644 --- a/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java +++ b/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java @@ -9,7 +9,7 @@ import basics.algo.InsertionListener; import basics.algo.VehicleRoutingAlgorithmListeners.PrioritizedVRAListener; import basics.route.VehicleFleetManager; -public class BestInsertionBuilder implements InsertionStrategyBuilder{ +public class BestInsertionBuilder { private VehicleRoutingProblem vrp; @@ -101,7 +101,7 @@ public class BestInsertionBuilder implements InsertionStrategyBuilder{ this.actInsertionCostsCalculator = activityInsertionCostsCalculator; }; - @Override + public InsertionStrategy build() { List iListeners = new ArrayList(); List algorithmListeners = new ArrayList(); diff --git a/jsprit-core/src/main/java/algorithms/InsertionStrategyBuilder.java b/jsprit-core/src/main/java/algorithms/InsertionStrategyBuilder.java deleted file mode 100644 index 04e34208..00000000 --- a/jsprit-core/src/main/java/algorithms/InsertionStrategyBuilder.java +++ /dev/null @@ -1,7 +0,0 @@ -package algorithms; - -public interface InsertionStrategyBuilder { - - public InsertionStrategy build(); - -} From 05005d5a0451df14c7b8cb621a70bca127319554 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Wed, 6 Nov 2013 16:11:01 +0100 Subject: [PATCH 07/17] add deprecating doc to Solutions --- jsprit-core/src/main/java/util/Solutions.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jsprit-core/src/main/java/util/Solutions.java b/jsprit-core/src/main/java/util/Solutions.java index a206f05a..9da4b645 100644 --- a/jsprit-core/src/main/java/util/Solutions.java +++ b/jsprit-core/src/main/java/util/Solutions.java @@ -22,6 +22,12 @@ import basics.VehicleRoutingProblemSolution; public class Solutions { + /** + * + * @deprecated use bestOf instead. + * @param solutions + * @return + */ @Deprecated public static VehicleRoutingProblemSolution getBest(Collection solutions){ VehicleRoutingProblemSolution best = null; From 2ea579d6f6f85a4c56576c0e16e70223ff0be2e8 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Thu, 7 Nov 2013 16:45:42 +0100 Subject: [PATCH 08/17] improve Benchmarker - adds new constructor with algoFactory --- .../java/analysis/ConcurrentBenchmarker.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/jsprit-analysis/src/main/java/analysis/ConcurrentBenchmarker.java b/jsprit-analysis/src/main/java/analysis/ConcurrentBenchmarker.java index 8457f5ae..8642074c 100644 --- a/jsprit-analysis/src/main/java/analysis/ConcurrentBenchmarker.java +++ b/jsprit-analysis/src/main/java/analysis/ConcurrentBenchmarker.java @@ -37,6 +37,7 @@ import algorithms.VehicleRoutingAlgorithms; import basics.VehicleRoutingAlgorithm; import basics.VehicleRoutingProblem; import basics.VehicleRoutingProblemSolution; +import basics.algo.VehicleRoutingAlgorithmFactory; import basics.algo.VehicleRoutingAlgorithmListeners.Priority; public class ConcurrentBenchmarker { @@ -47,7 +48,7 @@ public class ConcurrentBenchmarker { - private String algorithmConfig; + private String algorithmConfig = null; private List benchmarkInstances = new ArrayList(); @@ -65,6 +66,8 @@ public class ConcurrentBenchmarker { } }; + + private VehicleRoutingAlgorithmFactory algorithmFactory; public void setCost(Cost cost){ this.cost = cost; } @@ -74,6 +77,10 @@ public class ConcurrentBenchmarker { Logger.getRootLogger().setLevel(Level.ERROR); } + public ConcurrentBenchmarker(VehicleRoutingAlgorithmFactory algorithmFactory){ + this.algorithmFactory = algorithmFactory; + } + public void addBenchmarkWriter(BenchmarkWriter writer){ writers.add(writer); } @@ -94,6 +101,12 @@ public class ConcurrentBenchmarker { benchmarkInstances.add(new BenchmarkInstance(name,problem,bestKnownResult,bestKnownVehicles)); } + /** + * Sets nuOfRuns with same algorithm on same instance. + *

Default is 1 + * + * @param runs + */ public void setNuOfRuns(int runs){ this.runs = runs; } @@ -142,11 +155,11 @@ public class ConcurrentBenchmarker { double[] times = new double[runs]; for(int run=0;run solutions = vra.searchSolutions(); - VehicleRoutingProblemSolution best = Solutions.getBest(solutions); + VehicleRoutingProblemSolution best = Solutions.bestOf(solutions); vehicles[run] = best.getRoutes().size(); results[run] = cost.getCost(best); times[run] = stopwatch.getCompTimeInSeconds(); @@ -155,6 +168,16 @@ public class ConcurrentBenchmarker { return new BenchmarkResult(p, runs, results, times, vehicles); } + private VehicleRoutingAlgorithm createAlgorithm(BenchmarkInstance p) { + if(algorithmConfig != null){ + return VehicleRoutingAlgorithms.readAndCreateAlgorithm(p.vrp, algorithmConfig); + } + else{ + return algorithmFactory.createAlgorithm(p.vrp); + } + + } + private void print(Collection results) { double sumTime=0.0; double sumResult=0.0; From 87d23e85297d3298cf990f781c1bfe92c57a54c0 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Thu, 7 Nov 2013 16:46:19 +0100 Subject: [PATCH 09/17] change visibility of GendreauStrat --- jsprit-core/src/main/java/algorithms/Gendreau.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsprit-core/src/main/java/algorithms/Gendreau.java b/jsprit-core/src/main/java/algorithms/Gendreau.java index 7c46473d..63eb5e93 100644 --- a/jsprit-core/src/main/java/algorithms/Gendreau.java +++ b/jsprit-core/src/main/java/algorithms/Gendreau.java @@ -40,7 +40,7 @@ import basics.route.VehicleFleetManager; import basics.route.TourActivity.JobActivity; import basics.route.VehicleRoute; -final class Gendreau implements SearchStrategyModule{ +public final class Gendreau implements SearchStrategyModule{ private final static Logger log = Logger.getLogger(Gendreau.class); From 96d7b6f9c30fd3ebae11a4c02b0218e29bda851f Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Thu, 7 Nov 2013 16:46:46 +0100 Subject: [PATCH 10/17] premature loop break --- .../src/main/java/algorithms/ServiceInsertionCalculator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jsprit-core/src/main/java/algorithms/ServiceInsertionCalculator.java b/jsprit-core/src/main/java/algorithms/ServiceInsertionCalculator.java index 1c1193e8..6e17dba4 100644 --- a/jsprit-core/src/main/java/algorithms/ServiceInsertionCalculator.java +++ b/jsprit-core/src/main/java/algorithms/ServiceInsertionCalculator.java @@ -122,6 +122,7 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator{ } else if(status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)){ loopBroken = true; + break; } } double nextActArrTime = prevActStartTime + transportCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevActStartTime, newDriver, newVehicle); From 59f0f63e198d03c9697c2663b4aaa9c51d7d87e8 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Thu, 7 Nov 2013 16:47:14 +0100 Subject: [PATCH 11/17] change para-name in constructor to specify time --- .../main/java/basics/algo/TimeBreaker.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/jsprit-core/src/main/java/basics/algo/TimeBreaker.java b/jsprit-core/src/main/java/basics/algo/TimeBreaker.java index 267a6151..447f2f7b 100644 --- a/jsprit-core/src/main/java/basics/algo/TimeBreaker.java +++ b/jsprit-core/src/main/java/basics/algo/TimeBreaker.java @@ -25,6 +25,15 @@ import basics.VehicleRoutingProblem; import basics.VehicleRoutingProblemSolution; import basics.algo.SearchStrategy.DiscoveredSolution; +/** + * Breaks algorithm prematurely based on specified time. + * + *

Note, TimeBreaker must be registered as AlgorithmListener
+ * agorithm.getAlgorithmListeners().addListener(this); + * + * @author stefan + * + */ public class TimeBreaker implements PrematureAlgorithmBreaker, AlgorithmStartsListener{ private static Logger logger = Logger.getLogger(TimeBreaker.class); @@ -33,9 +42,18 @@ public class TimeBreaker implements PrematureAlgorithmBreaker, AlgorithmStartsLi private double startTime; - public TimeBreaker(double time) { + /** + * Constructs TimeBreaker that breaks algorithm prematurely based on specified time. + * + *

Note, TimeBreaker must be registered as AlgorithmListener
+ * agorithm.getAlgorithmListeners().addListener(this); + * + * @author stefan + * + */ + public TimeBreaker(double time_in_seconds) { super(); - this.timeThreshold = time; + this.timeThreshold = time_in_seconds; logger.info("initialise " + this); } From c31949f53869a0bb965ca6eeea5e7c7d8e1bf95a Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Thu, 7 Nov 2013 16:47:27 +0100 Subject: [PATCH 12/17] some internals --- .../algo/VariationCoefficientBreaker.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/jsprit-core/src/main/java/basics/algo/VariationCoefficientBreaker.java b/jsprit-core/src/main/java/basics/algo/VariationCoefficientBreaker.java index e3f271fc..f3c75691 100644 --- a/jsprit-core/src/main/java/basics/algo/VariationCoefficientBreaker.java +++ b/jsprit-core/src/main/java/basics/algo/VariationCoefficientBreaker.java @@ -28,6 +28,16 @@ import basics.VehicleRoutingProblem; import basics.VehicleRoutingProblemSolution; import basics.algo.SearchStrategy.DiscoveredSolution; +/** + * Breaks algorithm prematurely based on variationCoefficient. + * + *

Note that this must be registered in algorithm
+ * algorithm.getAlgorithmListeners().addListener(this); + * + * + * @author stefan + * + */ public class VariationCoefficientBreaker implements PrematureAlgorithmBreaker, IterationStartsListener, AlgorithmStartsListener, IterationEndsListener{ private static Logger logger = Logger.getLogger(VariationCoefficientBreaker.class); @@ -42,6 +52,16 @@ public class VariationCoefficientBreaker implements PrematureAlgorithmBreaker, I private VehicleRoutingProblemSolution lastAccepted = null; + /** + * Breaks algorithm prematurely based on variationCoefficient. + * + *

Note that this must be registered in algorithm
+ * algorithm.getAlgorithmListeners().addListener(this); + * + * + * @author stefan + * + */ public VariationCoefficientBreaker(int nuOfIterations, double variationCoefficientThreshold) { super(); this.nuOfIterations = nuOfIterations; From 0586b9664c916261787f6e1c020ed66ab7792d66 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Tue, 12 Nov 2013 16:23:05 +0100 Subject: [PATCH 13/17] refine Gendreau --- .../src/main/java/algorithms/Gendreau.java | 15 ++++++++------- .../java/algorithms/VehicleRoutingAlgorithms.java | 3 +-- .../test/java/algorithms/GendreauPostOptTest.java | 7 +++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/jsprit-core/src/main/java/algorithms/Gendreau.java b/jsprit-core/src/main/java/algorithms/Gendreau.java index 63eb5e93..b7097200 100644 --- a/jsprit-core/src/main/java/algorithms/Gendreau.java +++ b/jsprit-core/src/main/java/algorithms/Gendreau.java @@ -64,7 +64,7 @@ public final class Gendreau implements SearchStrategyModule{ this.shareOfJobsToRuin = shareOfJobsToRuin; } - public Gendreau(VehicleRoutingProblem vrp, RuinStrategy ruin, InsertionStrategy insertionStrategy) { + public Gendreau(VehicleRoutingProblem vrp, RuinStrategy ruin, InsertionStrategy insertionStrategy, VehicleFleetManager vehicleFleetManager) { super(); InsertionListeners insertionListeners = new InsertionListeners(); insertionListeners.addAllListeners(insertionStrategy.getListeners()); @@ -72,6 +72,7 @@ public final class Gendreau implements SearchStrategyModule{ this.ruin = ruin; this.vrp = vrp; this.insertionStrategy = insertionStrategy; + this.fleetManager = vehicleFleetManager; } @Override @@ -88,10 +89,10 @@ public final class Gendreau implements SearchStrategyModule{ this.nOfIterations = nOfIterations; } - public void setFleetManager(VehicleFleetManager vehicleFleetManager) { - this.fleetManager = vehicleFleetManager; - - } +// public void setFleetManager(VehicleFleetManager vehicleFleetManager) { +// this.fleetManager = vehicleFleetManager; +// +// } @Override public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) { @@ -119,14 +120,14 @@ public final class Gendreau implements SearchStrategyModule{ VehicleRoute emptyRoute1 = VehicleRoute.emptyRoute(); copiedRoutes.add(emptyRoute1); insertionStrategy.insertJobs(Arrays.asList(emptyRoute1), Arrays.asList(targetJob)); -// 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); insertionStrategy.insertJobs(Arrays.asList(emptyRoute2), Arrays.asList(job2)); -// routeAlgorithm.insertJob(job2, routeAlgorithm.calculateBestInsertion(emptyRoute2, job2, Double.MAX_VALUE), emptyRoute2); + unassignedJobs.remove(job2); insertionStrategy.insertJobs(copiedRoutes, unassignedJobs); diff --git a/jsprit-core/src/main/java/algorithms/VehicleRoutingAlgorithms.java b/jsprit-core/src/main/java/algorithms/VehicleRoutingAlgorithms.java index bd8e6db4..85f0d418 100644 --- a/jsprit-core/src/main/java/algorithms/VehicleRoutingAlgorithms.java +++ b/jsprit-core/src/main/java/algorithms/VehicleRoutingAlgorithms.java @@ -809,10 +809,9 @@ public class VehicleRoutingAlgorithms { insertion = createInsertionStrategy(insertionConfigs.get(0), vrp, vehicleFleetManager, routeStates, prioListeners, executorService, nuOfThreads, constraintManager); algorithmListeners.addAll(prioListeners); } - Gendreau gendreau = new Gendreau(vrp, ruin, insertion); + Gendreau gendreau = new Gendreau(vrp, ruin, insertion, vehicleFleetManager); gendreau.setShareOfJobsToRuin(share); gendreau.setNuOfIterations(iterations); - gendreau.setFleetManager(vehicleFleetManager); definedClasses.put(strategyModuleKey, gendreau); return gendreau; } diff --git a/jsprit-core/src/test/java/algorithms/GendreauPostOptTest.java b/jsprit-core/src/test/java/algorithms/GendreauPostOptTest.java index b2ee4691..185e139b 100644 --- a/jsprit-core/src/test/java/algorithms/GendreauPostOptTest.java +++ b/jsprit-core/src/test/java/algorithms/GendreauPostOptTest.java @@ -198,8 +198,7 @@ public class GendreauPostOptTest { InsertionStrategy insertionStrategy = new BestInsertion(insertionCalc); insertionStrategy.addListener(stateUpdater); insertionStrategy.addListener(new VehicleSwitched(fleetManager)); - Gendreau postOpt = new Gendreau(vrp, radialRuin, insertionStrategy); - postOpt.setFleetManager(fleetManager); + Gendreau postOpt = new Gendreau(vrp, radialRuin, insertionStrategy, fleetManager); VehicleRoutingProblemSolution newSolution = postOpt.runAndGetSolution(sol); newSolution.setCost(getCosts(newSolution,states)); @@ -258,10 +257,10 @@ public class GendreauPostOptTest { InsertionStrategy insertionStrategy = new BestInsertion(insertionCalc); insertionStrategy.addListener(stateUpdater); insertionStrategy.addListener(new VehicleSwitched(fleetManager)); - Gendreau postOpt = new Gendreau(vrp, radialRuin, insertionStrategy); + Gendreau postOpt = new Gendreau(vrp, radialRuin, insertionStrategy, fleetManager); postOpt.setShareOfJobsToRuin(1.0); postOpt.setNuOfIterations(1); - postOpt.setFleetManager(fleetManager); + // postOpt.setWithFix(withFixCost); VehicleRoutingProblemSolution newSolution = postOpt.runAndGetSolution(sol); newSolution.setCost(getCosts(newSolution,states)); From 463d87b30bf9ee279ce924d17023f551ac64acd7 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Tue, 12 Nov 2013 16:28:36 +0100 Subject: [PATCH 14/17] clean redundant files --- .../algorithms/BestInsertionConcurrent.java | 198 ------------------ 1 file changed, 198 deletions(-) delete mode 100644 jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java diff --git a/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java b/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java deleted file mode 100644 index 6f343d63..00000000 --- a/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java +++ /dev/null @@ -1,198 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2013 Stefan Schroeder - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3.0 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see . - ******************************************************************************/ -package algorithms; - - -// -// -///** -// * -// * @author stefan schroeder -// * -// */ -// -//final class BestInsertionConcurrent implements InsertionStrategy{ -// -// public static BestInsertionConcurrent newInstance(RouteAlgorithm routeAlgorithm, ExecutorService executor, int nuOfThreads){ -// return new BestInsertionConcurrent(routeAlgorithm, executor, nuOfThreads); -// } -// -// 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; -// } -// -// private 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 insertJobs(Collection vehicleRoutes, Collection unassignedJobs) { -// 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); -// } -//// informInsertionEndsListeners(vehicleRoutes); -// } -// -// 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 getListeners() { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public void addListener(InsertionListener insertionListener) { -// // TODO Auto-generated method stub -// -// } -// -//} From 96ed6a70cdb823541cc5a942b40bff2acf4cfc68 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Tue, 12 Nov 2013 17:15:28 +0100 Subject: [PATCH 15/17] Merge BestInsertionBuilder and BestInsertionConcurrent from PickupAndDelivery-Branch into RelaxAPI-Branch --- .../java/algorithms/BestInsertionBuilder.java | 35 ++- .../algorithms/BestInsertionConcurrent.java | 264 ++++++++++++++++++ .../VehicleRoutingAlgorithmFactoryImpl.java | 1 + 3 files changed, 290 insertions(+), 10 deletions(-) create mode 100644 jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java diff --git a/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java b/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java index 2f8281e9..3278e04a 100644 --- a/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java +++ b/jsprit-core/src/main/java/algorithms/BestInsertionBuilder.java @@ -2,6 +2,7 @@ package algorithms; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ExecutorService; import basics.VehicleRoutingProblem; import basics.algo.InsertionListener; @@ -29,16 +30,21 @@ public class BestInsertionBuilder { private int forwaredLooking; private int memory; + + private ExecutorService executor; + + private int nuOfThreads; - public BestInsertionBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager) { + public BestInsertionBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager, ConstraintManager constraintManager) { super(); this.vrp = vrp; this.stateManager = stateManager; - this.constraintManager = new ConstraintManager(vrp,stateManager); + this.constraintManager = constraintManager; this.fleetManager = vehicleFleetManager; } public BestInsertionBuilder setRouteLevel(int forwardLooking, int memory){ + local = false; this.forwaredLooking = forwardLooking; this.memory = memory; @@ -56,11 +62,17 @@ public class BestInsertionBuilder { return this; } - public void setActivityInsertionCostCalculator(ActivityInsertionCostsCalculator activityInsertionCostsCalculator){ + public BestInsertionBuilder setActivityInsertionCostCalculator(ActivityInsertionCostsCalculator activityInsertionCostsCalculator){ this.actInsertionCostsCalculator = activityInsertionCostsCalculator; + return this; }; - + public BestInsertionBuilder setConcurrentMode(ExecutorService executor, int nuOfThreads){ + this.executor = executor; + this.nuOfThreads = nuOfThreads; + return this; + } + public InsertionStrategy build() { List iListeners = new ArrayList(); List algorithmListeners = new ArrayList(); @@ -80,13 +92,16 @@ public class BestInsertionBuilder { calcBuilder.considerFixedCosts(weightOfFixedCosts); } JobInsertionCostsCalculator jobInsertions = calcBuilder.build(); - BestInsertion bestInsertion = new BestInsertion(jobInsertions); - for(InsertionListener l : iListeners) bestInsertion.addListener(l); + InsertionStrategy bestInsertion; + if(executor == null){ + bestInsertion = new BestInsertion(jobInsertions); + + } + else{ + bestInsertion = new BestInsertionConcurrent(jobInsertions,executor,nuOfThreads); + } + for(InsertionListener l : iListeners) bestInsertion.addListener(l); return bestInsertion; } - public void setConstraintManager(ConstraintManager constraintManager) { - this.constraintManager = constraintManager; - } - } 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..afb37573 --- /dev/null +++ b/jsprit-core/src/main/java/algorithms/BestInsertionConcurrent.java @@ -0,0 +1,264 @@ +/******************************************************************************* + * Copyright (C) 2013 Stefan Schroeder + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + ******************************************************************************/ +package algorithms; + +import java.util.ArrayList; +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.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.algo.InsertionListener; +import basics.route.Driver; +import basics.route.Vehicle; +import basics.route.VehicleRoute; + + + +/** + * + * @author stefan schroeder + * + */ + +final class BestInsertionConcurrent implements InsertionStrategy{ + + static class Batch { + List routes = new ArrayList(); + + } + + 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; + } + + } + + private static Logger logger = Logger.getLogger(BestInsertionConcurrent.class); + + private Random random = RandomNumberGeneration.getRandom(); + + private final static double NO_NEW_DEPARTURE_TIME_YET = -12345.12345; + + private final static Vehicle NO_NEW_VEHICLE_YET = null; + + private final static Driver NO_NEW_DRIVER_YET = null; + + private InsertionListeners insertionsListeners; + + private Inserter inserter; + + private JobInsertionCostsCalculator bestInsertionCostCalculator; + + private boolean minVehiclesFirst = false; + + private int nuOfBatches; + + private ExecutorService executor; + + private ExecutorCompletionService completionService; + + public void setRandom(Random random) { + this.random = random; + } + + public BestInsertionConcurrent(JobInsertionCostsCalculator jobInsertionCalculator, ExecutorService executorService, int nuOfBatches) { + super(); + this.insertionsListeners = new InsertionListeners(); + this.executor = executorService; + this.nuOfBatches = nuOfBatches; + inserter = new Inserter(insertionsListeners); + bestInsertionCostCalculator = jobInsertionCalculator; + completionService = new ExecutorCompletionService(executor); + logger.info("initialise " + this); + } + + @Override + public String toString() { + return "[name=bestInsertion]"; + } + + @Override + public void insertJobs(Collection vehicleRoutes, Collection unassignedJobs) { + insertionsListeners.informInsertionStarts(vehicleRoutes,unassignedJobs); + List unassignedJobList = new ArrayList(unassignedJobs); + Collections.shuffle(unassignedJobList, random); + + List batches = distributeRoutes(vehicleRoutes,nuOfBatches); + + for(final Job unassignedJob : unassignedJobList){ + + Insertion bestInsertion = null; + double bestInsertionCost = Double.MAX_VALUE; + + 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(!minVehiclesFirst){ + VehicleRoute newRoute = VehicleRoute.emptyRoute(); + InsertionData newIData = bestInsertionCostCalculator.getInsertionData(newRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost); + if(newIData.getInsertionCost() < bestInsertionCost){ + bestInsertion = new Insertion(newRoute,newIData); + bestInsertionCost = newIData.getInsertionCost(); + vehicleRoutes.add(newRoute); + batches.get(0).routes.add(newRoute); + } + } + + if(bestInsertion == null){ + VehicleRoute newRoute = VehicleRoute.emptyRoute(); + InsertionData bestI = bestInsertionCostCalculator.getInsertionData(newRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE); + if(bestI instanceof InsertionData.NoInsertionFound){ + throw new IllegalStateException(getErrorMsg(unassignedJob)); + } + else{ + bestInsertion = new Insertion(newRoute,bestI); + vehicleRoutes.add(newRoute); + } + } +// logger.info("insert " + unassignedJob + " pickup@" + bestInsertion.getInsertionData().getPickupInsertionIndex() + " delivery@" + bestInsertion.getInsertionData().getDeliveryInsertionIndex()); + inserter.insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); + } + insertionsListeners.informInsertionEndsListeners(vehicleRoutes); + } + + private String getErrorMsg(Job unassignedJob) { + return "given the vehicles, could not insert job\n" + + "\t" + unassignedJob + + "\n\tthis might have the following reasons:\n" + + "\t- no vehicle has the capacity to transport the job [check whether there is at least one vehicle that is capable to transport the job]\n" + + "\t- the time-window cannot be met, even in a commuter tour the time-window is missed [check whether it is possible to reach the time-window on the shortest path or make hard time-windows soft]\n" + + "\t- if you deal with finite vehicles, and the available vehicles are already fully employed, no vehicle can be found anymore to transport the job [add penalty-vehicles]"; + } + + @Override + public void removeListener(InsertionListener insertionListener) { + insertionsListeners.removeListener(insertionListener); + } + + @Override + public Collection getListeners() { + return Collections.unmodifiableCollection(insertionsListeners.getListeners()); + } + + @Override + public void addListener(InsertionListener insertionListener) { + insertionsListeners.addListener(insertionListener); + + } + + private Insertion getBestInsertion(Batch batch, Job unassignedJob) { + Insertion bestInsertion = null; + double bestInsertionCost = Double.MAX_VALUE; + for(VehicleRoute vehicleRoute : batch.routes){ + InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, 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 Date: Mon, 18 Nov 2013 22:33:10 +0100 Subject: [PATCH 16/17] make RuinRadial more memory efficient and make NeighborhoodCreation independent from RuinRadial --- .../src/main/java/algorithms/RuinRadial.java | 267 ++++++++++++++---- .../algorithms/JobNeighborhoodsImplTest.java | 110 ++++++++ ...ighborhoodsWithCapRestrictionImplTest.java | 111 ++++++++ 3 files changed, 435 insertions(+), 53 deletions(-) create mode 100644 jsprit-core/src/test/java/algorithms/JobNeighborhoodsImplTest.java create mode 100644 jsprit-core/src/test/java/algorithms/JobNeighborhoodsWithCapRestrictionImplTest.java diff --git a/jsprit-core/src/main/java/algorithms/RuinRadial.java b/jsprit-core/src/main/java/algorithms/RuinRadial.java index 7b9c5edf..6f198537 100644 --- a/jsprit-core/src/main/java/algorithms/RuinRadial.java +++ b/jsprit-core/src/main/java/algorithms/RuinRadial.java @@ -47,6 +47,194 @@ import basics.route.VehicleRoute; */ final class RuinRadial implements RuinStrategy { + static interface JobNeighborhoods { + + public Iterator getNearestNeighborsIterator(int nNeighbors, Job neighborTo); + + } + + static class NeighborhoodIterator implements Iterator{ + + private static Logger log = Logger.getLogger(NeighborhoodIterator.class); + + private Iterator jobIter; + + private int nJobs; + + private int jobCount = 0; + + public NeighborhoodIterator(Iterator jobIter, int nJobs) { + super(); + this.jobIter = jobIter; + this.nJobs = nJobs; + } + + @Override + public boolean hasNext() { + if(jobCount < nJobs){ + boolean hasNext = jobIter.hasNext(); + if(!hasNext) log.warn("more jobs are requested then iterator can iterate over. probably the number of neighbors memorized in JobNeighborhoods is too small"); + return hasNext; + } + return false; + } + + @Override + public Job next() { + ReferencedJob next = jobIter.next(); + jobCount++; + return next.getJob(); + } + + @Override + public void remove() { + jobIter.remove(); + } + + } + + static class JobNeighborhoodsImpl implements JobNeighborhoods { + + private static Logger logger = Logger.getLogger(JobNeighborhoodsImpl.class); + + private VehicleRoutingProblem vrp; + + private Map> distanceNodeTree = new HashMap>(); + + private JobDistance jobDistance; + + public JobNeighborhoodsImpl(VehicleRoutingProblem vrp, JobDistance jobDistance) { + super(); + this.vrp = vrp; + this.jobDistance = jobDistance; + logger.info("intialise " + this); + } + + public Iterator getNearestNeighborsIterator(int nNeighbors, Job neighborTo){ + TreeSet tree = distanceNodeTree.get(neighborTo.getId()); + Iterator descendingIterator = tree.iterator(); + return new NeighborhoodIterator(descendingIterator, nNeighbors); + } + + public void initialise(){ + logger.info("calculates and memorizes distances from EACH job to EACH job --> n^2 calculations"); + calculateDistancesFromJob2Job(); + } + + 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()) { + if(i==j) continue; + 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"); + } + + } + + static class JobNeighborhoodsImplWithCapRestriction implements JobNeighborhoods { + + private static Logger logger = Logger.getLogger(JobNeighborhoodsImpl.class); + + private VehicleRoutingProblem vrp; + + private Map> distanceNodeTree = new HashMap>(); + + private JobDistance jobDistance; + + private int capacity; + + public JobNeighborhoodsImplWithCapRestriction(VehicleRoutingProblem vrp, JobDistance jobDistance, int capacity) { + super(); + this.vrp = vrp; + this.jobDistance = jobDistance; + this.capacity = capacity; + logger.info("intialise " + this); + } + + public Iterator getNearestNeighborsIterator(int nNeighbors, Job neighborTo){ + TreeSet tree = distanceNodeTree.get(neighborTo.getId()); + Iterator descendingIterator = tree.iterator(); + return new NeighborhoodIterator(descendingIterator, nNeighbors); + } + + public void initialise(){ + logger.info("calculates distances from EACH job to EACH job --> n^2="+Math.pow(vrp.getJobs().values().size(), 2) + " calculations, but 'only' "+(vrp.getJobs().values().size()*capacity)+ " are cached."); + calculateDistancesFromJob2Job(); + } + + 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()) { + if(i==j) continue; + double distance = jobDistance.calculateDistance(i, j); + ReferencedJob refNode = new ReferencedJob(j, distance); + if(treeSet.size() < capacity){ + treeSet.add(refNode); + nuOfDistancesStored++; + } + else{ + if(treeSet.last().getDistance() > distance){ + treeSet.pollLast(); + treeSet.add(refNode); + } + } + } + assert treeSet.size() <= capacity : "treeSet.size() is bigger than specified capacity"; + + } + 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=neighborhoodWithCapRestriction][capacity="+capacity+"]"; + } + + } + + static class ReferencedJob { private Job job; private double distance; @@ -72,14 +260,12 @@ final class RuinRadial implements RuinStrategy { private double fractionOfAllNodes2beRuined; - private Map> distanceNodeTree = new HashMap>(); - private Random random = RandomNumberGeneration.getRandom(); - private JobDistance jobDistance; - private RuinListeners ruinListeners; + private JobNeighborhoods jobNeighborhoods; + public void setRandom(Random random) { this.random = random; } @@ -94,42 +280,14 @@ final class RuinRadial implements RuinStrategy { public RuinRadial(VehicleRoutingProblem vrp, double fraction2beRemoved, JobDistance jobDistance) { super(); this.vrp = vrp; - this.jobDistance = jobDistance; this.fractionOfAllNodes2beRuined = fraction2beRemoved; ruinListeners = new RuinListeners(); - calculateDistancesFromJob2Job(); + int nJobsToMemorize = (int) Math.ceil(vrp.getJobs().values().size()*fraction2beRemoved); + JobNeighborhoodsImplWithCapRestriction jobNeighborhoodsImpl = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, nJobsToMemorize); + jobNeighborhoodsImpl.initialise(); + jobNeighborhoods = jobNeighborhoodsImpl; logger.info("intialise " + 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() { @@ -143,11 +301,11 @@ final class RuinRadial implements RuinStrategy { @Override public Collection ruin(Collection vehicleRoutes) { if(vehicleRoutes.isEmpty()){ - return Collections.EMPTY_LIST; + return Collections.emptyList(); } int nOfJobs2BeRemoved = getNuOfJobs2BeRemoved(); if (nOfJobs2BeRemoved == 0) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } Job randomJob = pickRandomJob(); Collection unassignedJobs = ruin(vehicleRoutes,randomJob,nOfJobs2BeRemoved); @@ -160,27 +318,30 @@ final class RuinRadial implements RuinStrategy { public Collection ruin(Collection vehicleRoutes, Job targetJob, int nOfJobs2BeRemoved){ ruinListeners.ruinStarts(vehicleRoutes); 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(); + int nNeighbors = nOfJobs2BeRemoved - 1; + removeJob(targetJob,vehicleRoutes); + unassignedJobs.add(targetJob); + Iterator neighborhoodIterator = jobNeighborhoods.getNearestNeighborsIterator(nNeighbors, targetJob); + while(neighborhoodIterator.hasNext()){ + Job job = neighborhoodIterator.next(); + removeJob(job,vehicleRoutes); unassignedJobs.add(job); - counter++; - boolean removed = false; - for (VehicleRoute route : vehicleRoutes) { - removed = route.getTourActivities().removeJob(job);; - if (removed) { - ruinListeners.removed(job,route); - break; - } - } } ruinListeners.ruinEnds(vehicleRoutes, unassignedJobs); return unassignedJobs; } + private void removeJob(Job job, Collection vehicleRoutes) { + boolean removed = false; + for (VehicleRoute route : vehicleRoutes) { + removed = route.getTourActivities().removeJob(job);; + if (removed) { + ruinListeners.removed(job,route); + break; + } + } + } + private Job pickRandomJob() { int totNuOfJobs = vrp.getJobs().values().size(); int randomIndex = random.nextInt(totNuOfJobs); diff --git a/jsprit-core/src/test/java/algorithms/JobNeighborhoodsImplTest.java b/jsprit-core/src/test/java/algorithms/JobNeighborhoodsImplTest.java new file mode 100644 index 00000000..9ba678b6 --- /dev/null +++ b/jsprit-core/src/test/java/algorithms/JobNeighborhoodsImplTest.java @@ -0,0 +1,110 @@ +package algorithms; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import util.Coordinate; +import algorithms.RuinRadial.JobNeighborhoodsImpl; +import basics.Job; +import basics.Service; +import basics.VehicleRoutingProblem; + +public class JobNeighborhoodsImplTest { + + VehicleRoutingProblem vrp; + + JobDistance jobDistance; + + Service target; + Service s2; + Service s3; + Service s4; + Service s5; + Service s6; + Service s7; + + @Before + public void doBefore(){ + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + target = Service.Builder.newInstance("s1", 1).setCoord(Coordinate.newInstance(0, 5)).build(); + s2 = Service.Builder.newInstance("s2", 1).setCoord(Coordinate.newInstance(0, 4)).build(); + s3 = Service.Builder.newInstance("s3", 1).setCoord(Coordinate.newInstance(0, 3)).build(); + s4 = Service.Builder.newInstance("s4", 1).setCoord(Coordinate.newInstance(0, 2)).build(); + + s5 = Service.Builder.newInstance("s5", 1).setCoord(Coordinate.newInstance(0, 6)).build(); + s6 = Service.Builder.newInstance("s6", 1).setCoord(Coordinate.newInstance(0, 7)).build(); + s7 = Service.Builder.newInstance("s7", 1).setCoord(Coordinate.newInstance(0, 8)).build(); + + vrp = builder.addJob(target).addJob(s2).addJob(s3).addJob(s4).addJob(s5).addJob(s6).addJob(s7).build(); + + jobDistance = new EuclideanServiceDistance(); + } + + @Test + public void whenRequestingNeighborhoodOfTargetJob_nNeighborsShouldBeTwo(){ + JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(2, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertEquals(2,services.size()); + } + + @Test + public void whenRequestingNeighborhoodOfTargetJob_s2ShouldBeNeighbor(){ + JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(2, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertTrue(services.contains(s2)); + } + + @Test + public void whenRequestingNeighborhoodOfTargetJob_s4ShouldBeNeighbor(){ + JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(2, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertTrue(services.contains(s5)); + } + + @Test + public void whenRequestingNeighborhoodOfTargetJob_sizeShouldBe4(){ + JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(4, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertEquals(4,services.size()); + } + + @Test + public void whenRequestingMoreNeighborsThanExisting_itShouldReturnMaxNeighbors(){ + JobNeighborhoodsImpl jn = new JobNeighborhoodsImpl(vrp, jobDistance); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(100, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertEquals(6,services.size()); + } + +} diff --git a/jsprit-core/src/test/java/algorithms/JobNeighborhoodsWithCapRestrictionImplTest.java b/jsprit-core/src/test/java/algorithms/JobNeighborhoodsWithCapRestrictionImplTest.java new file mode 100644 index 00000000..8f4bbc28 --- /dev/null +++ b/jsprit-core/src/test/java/algorithms/JobNeighborhoodsWithCapRestrictionImplTest.java @@ -0,0 +1,111 @@ +package algorithms; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import util.Coordinate; +import algorithms.RuinRadial.JobNeighborhoodsImpl; +import algorithms.RuinRadial.JobNeighborhoodsImplWithCapRestriction; +import basics.Job; +import basics.Service; +import basics.VehicleRoutingProblem; + +public class JobNeighborhoodsWithCapRestrictionImplTest { + + VehicleRoutingProblem vrp; + + JobDistance jobDistance; + + Service target; + Service s2; + Service s3; + Service s4; + Service s5; + Service s6; + Service s7; + + @Before + public void doBefore(){ + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + target = Service.Builder.newInstance("s1", 1).setCoord(Coordinate.newInstance(0, 5)).build(); + s2 = Service.Builder.newInstance("s2", 1).setCoord(Coordinate.newInstance(0, 4)).build(); + s3 = Service.Builder.newInstance("s3", 1).setCoord(Coordinate.newInstance(0, 3)).build(); + s4 = Service.Builder.newInstance("s4", 1).setCoord(Coordinate.newInstance(0, 2)).build(); + + s5 = Service.Builder.newInstance("s5", 1).setCoord(Coordinate.newInstance(0, 6)).build(); + s6 = Service.Builder.newInstance("s6", 1).setCoord(Coordinate.newInstance(0, 7)).build(); + s7 = Service.Builder.newInstance("s7", 1).setCoord(Coordinate.newInstance(0, 8)).build(); + + vrp = builder.addJob(target).addJob(s2).addJob(s3).addJob(s4).addJob(s5).addJob(s6).addJob(s7).build(); + + jobDistance = new EuclideanServiceDistance(); + } + + @Test + public void whenRequestingNeighborhoodOfTargetJob_nNeighborsShouldBeTwo(){ + JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, 2); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(2, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertEquals(2,services.size()); + } + + @Test + public void whenRequestingNeighborhoodOfTargetJob_s2ShouldBeNeighbor(){ + JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, 2); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(2, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertTrue(services.contains(s2)); + } + + @Test + public void whenRequestingNeighborhoodOfTargetJob_s4ShouldBeNeighbor(){ + JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, 2); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(2, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertTrue(services.contains(s5)); + } + + @Test + public void whenRequestingNeighborhoodOfTargetJob_sizeShouldBe4(){ + JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, 4); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(4, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertEquals(4,services.size()); + } + + @Test + public void whenRequestingMoreNeighborsThanExisting_itShouldReturnMaxNeighbors(){ + JobNeighborhoodsImplWithCapRestriction jn = new JobNeighborhoodsImplWithCapRestriction(vrp, jobDistance, 2); + jn.initialise(); + Iterator iter = jn.getNearestNeighborsIterator(100, target); + List services = new ArrayList(); + while(iter.hasNext()){ + services.add((Service) iter.next()); + } + assertEquals(2,services.size()); + } + +} From 61406bc70ec4042e9841f05650d8b9474e091009 Mon Sep 17 00:00:00 2001 From: Stefan Schroeder <4sschroeder@gmail.com> Date: Tue, 19 Nov 2013 14:56:27 +0100 Subject: [PATCH 17/17] modify license in pom --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 3b3c5309..4d8a5078 100644 --- a/pom.xml +++ b/pom.xml @@ -34,8 +34,8 @@ - GNU General Public License, version 2 (GPL-2.0) - http://opensource.org/licenses/GPL-2.0 + GNU Lesser General Public Licence, version 3.0 + http://opensource.org/licenses/LGPL-3.0