diff --git a/.travis.yml b/.travis.yml index 26e49a8f..dbca6aee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,8 +51,8 @@ script: notifications: email: - - github@graphhopper.com - + - $EMAIL + cache: directories: - $HOME/.m2 diff --git a/docs/Other-Projects.md b/docs/Other-Projects.md index 20e9a196..aa2dee26 100644 --- a/docs/Other-Projects.md +++ b/docs/Other-Projects.md @@ -1,29 +1,34 @@ +## Other Related Open Source Implementations + ### VRP -#### [Chris Groer's VRPH library](https://sites.google.com/site/vrphlibrary/) -An open source library for solving the capacitated vehicle routing problem written in C++. - -#### [OptaPlanner](https://www.optaplanner.org/) -OptaPlanner is a lightweight, embeddable planning engine written in Java™. It can be used to solve the capacitated vehicle routing problem (with time windows). - -#### [Open-VRP](https://github.com/mck-/Open-VRP) -Open-VRP is a framework to model and solve various vehicle routing problems. - -#### [VROOM](https://github.com/jcoupey/vroom) -VROOM is an optimization engine written in C++14 that aim at providing good solutions to various real-life vehicle routing problems within a small computing time. It is free software, distributed under the term of the GNU General Public License V3. - #### [Hipster4j](http://www.hipster4j.org/) Hipster is an easy to use yet powerful and flexible type-safe library for heuristic search, written in pure Java. It relies on a flexible model with generic operators to define search problems. So you can also model and solve vehicle routing problems. +#### [Open-VRP](https://github.com/mck-/Open-VRP) +Open-VRP is a framework to model and solve various vehicle routing problems. + +#### [OptaPlanner](https://www.optaplanner.org/) +OptaPlanner is a lightweight, embeddable planning engine written in Java™. It can be used to solve the capacitated vehicle routing problem (with time windows). + +#### [OR-Tools](https://developers.google.com/optimization/) +Google Optimization Tools (OR-Tools) is a fast and portable software suite for solving combinatorial optimization problems, including the VRP. Open-source, written in C++ and available through SWIG for Python, Java, and .NET (using Mono on non-Windows platforms). You can compile OR-Tools on Linux, Mac OS X, and Windows (with Visual Studio). + #### [OscaR](https://bitbucket.org/oscarlib/oscar/wiki/Home) OscaR, an Open Source Toolbox for Optimising Logistics and Supply Chain Systems. +#### [VROOM](https://github.com/jcoupey/vroom) +VROOM is an optimization engine written in C++14 that aim at providing good solutions to various real-life vehicle routing problems within a small computing time. It is free software, distributed under the term of the GNU General Public License V3. + +#### [VRPH library](https://sites.google.com/site/vrphlibrary/) +An open source library by Chris Groer for solving the capacitated vehicle routing problem written in C++. +Now hosted by [coin-or](https://projects.coin-or.org/VRPH) + ### Territory Design #### [OpenDoorLogistics](http://www.opendoorlogistics.com) Open Door Logistics Studio is an easy-to-use standalone open source application for performing geographic analysis of your customer base and sales territory design, mapping and management. - - +### Other implementations If you know another promising open source implementation, report it. diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java index 14e33fd3..7225cf1f 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java @@ -20,6 +20,7 @@ package com.graphhopper.jsprit.core.algorithm.box; import com.graphhopper.jsprit.core.algorithm.PrettyAlgorithmBuilder; import com.graphhopper.jsprit.core.algorithm.SearchStrategy; +import com.graphhopper.jsprit.core.algorithm.SearchStrategyModule; import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; import com.graphhopper.jsprit.core.algorithm.acceptor.SchrimpfAcceptance; import com.graphhopper.jsprit.core.algorithm.acceptor.SolutionAcceptor; @@ -124,7 +125,10 @@ public class Jsprit { STRING_K_MIN("string_kmin"), STRING_K_MAX("string_kmax"), STRING_L_MIN("string_lmin"), - STRING_L_MAX("string_lmax"); + STRING_L_MAX("string_lmax"), + MIN_UNASSIGNED("min_unassigned"), + PROPORTION_UNASSIGNED("proportion_unassigned"); + String paraName; @@ -233,6 +237,9 @@ public class Jsprit { defaults.put(Parameter.FAST_REGRET.toString(), String.valueOf(false)); defaults.put(Parameter.BREAK_SCHEDULING.toString(), String.valueOf(true)); defaults.put(Parameter.CONSTRUCTION.toString(), Construction.REGRET_INSERTION.toString()); + + defaults.put(Parameter.MIN_UNASSIGNED.toString(), String.valueOf(Integer.MAX_VALUE)); + defaults.put(Parameter.PROPORTION_UNASSIGNED.toString(), String.valueOf(1.0)); return defaults; } @@ -526,8 +533,8 @@ public class Jsprit { boolean fastRegret = Boolean.parseBoolean(getProperty(Parameter.FAST_REGRET.toString())); if (es != null) { if(fastRegret){ - RegretInsertionConcurrentFast regretInsertion = (RegretInsertionConcurrentFast) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.REGRET) + RegretInsertionConcurrentFast regretInsertion = (RegretInsertionConcurrentFast) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET) .setConcurrentMode(es, noThreads) .setFastRegret(true) .considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()))) @@ -540,8 +547,8 @@ public class Jsprit { regret = regretInsertion; } else { - RegretInsertionConcurrent regretInsertion = (RegretInsertionConcurrent) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.REGRET) + RegretInsertionConcurrent regretInsertion = (RegretInsertionConcurrent) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET) .setConcurrentMode(es, noThreads) .considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()))) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) @@ -553,8 +560,8 @@ public class Jsprit { } } else { if(fastRegret) { - RegretInsertionFast regretInsertion = (RegretInsertionFast) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.REGRET) + RegretInsertionFast regretInsertion = (RegretInsertionFast) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET) .setFastRegret(true) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) .considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()))) @@ -566,8 +573,8 @@ public class Jsprit { regret = regretInsertion; } else{ - RegretInsertion regretInsertion = (RegretInsertion) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.REGRET) + RegretInsertion regretInsertion = (RegretInsertion) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) .considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()))) .setActivityInsertionCostCalculator(activityInsertion) @@ -581,16 +588,16 @@ public class Jsprit { AbstractInsertionStrategy best; if (vrp.getJobs().size() < 250 || es == null) { - BestInsertion bestInsertion = (BestInsertion) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.BEST) + BestInsertion bestInsertion = (BestInsertion) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.BEST) .considerFixedCosts(Double.valueOf(properties.getProperty(Parameter.FIXED_COST_PARAM.toString()))) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) .setActivityInsertionCostCalculator(activityInsertion) .build(); best = bestInsertion; } else { - BestInsertionConcurrent bestInsertion = (BestInsertionConcurrent) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.BEST) + BestInsertionConcurrent bestInsertion = (BestInsertionConcurrent) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.BEST) .considerFixedCosts(Double.valueOf(properties.getProperty(Parameter.FIXED_COST_PARAM.toString()))) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) .setConcurrentMode(es, noThreads) @@ -621,34 +628,34 @@ public class Jsprit { SolutionCostCalculator objectiveFunction = getObjectiveFunction(vrp, maxCosts); SearchStrategy radial_regret = new SearchStrategy(Strategy.RADIAL_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - radial_regret.addModule(new RuinAndRecreateModule(Strategy.RADIAL_REGRET.toString(), regret, radial)); + radial_regret.addModule(configureModule(new RuinAndRecreateModule(Strategy.RADIAL_REGRET.toString(), regret, radial))); SearchStrategy radial_best = new SearchStrategy(Strategy.RADIAL_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - radial_best.addModule(new RuinAndRecreateModule(Strategy.RADIAL_BEST.toString(), best, radial)); + radial_best.addModule(configureModule(new RuinAndRecreateModule(Strategy.RADIAL_BEST.toString(), best, radial))); SearchStrategy random_best = new SearchStrategy(Strategy.RANDOM_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - random_best.addModule(new RuinAndRecreateModule(Strategy.RANDOM_BEST.toString(), best, random_for_best)); + random_best.addModule(configureModule(new RuinAndRecreateModule(Strategy.RANDOM_BEST.toString(), best, random_for_best))); SearchStrategy random_regret = new SearchStrategy(Strategy.RANDOM_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - random_regret.addModule(new RuinAndRecreateModule(Strategy.RANDOM_REGRET.toString(), regret, random_for_regret)); + random_regret.addModule(configureModule(new RuinAndRecreateModule(Strategy.RANDOM_REGRET.toString(), regret, random_for_regret))); SearchStrategy worst_regret = new SearchStrategy(Strategy.WORST_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - worst_regret.addModule(new RuinAndRecreateModule(Strategy.WORST_REGRET.toString(), regret, worst)); + worst_regret.addModule(configureModule(new RuinAndRecreateModule(Strategy.WORST_REGRET.toString(), regret, worst))); SearchStrategy worst_best = new SearchStrategy(Strategy.WORST_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - worst_best.addModule(new RuinAndRecreateModule(Strategy.WORST_BEST.toString(), best, worst)); + worst_best.addModule(configureModule(new RuinAndRecreateModule(Strategy.WORST_BEST.toString(), best, worst))); final SearchStrategy clusters_regret = new SearchStrategy(Strategy.CLUSTER_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - clusters_regret.addModule(new RuinAndRecreateModule(Strategy.CLUSTER_REGRET.toString(), regret, clusters)); + clusters_regret.addModule(configureModule(new RuinAndRecreateModule(Strategy.CLUSTER_REGRET.toString(), regret, clusters))); final SearchStrategy clusters_best = new SearchStrategy(Strategy.CLUSTER_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - clusters_best.addModule(new RuinAndRecreateModule(Strategy.CLUSTER_BEST.toString(), best, clusters)); + clusters_best.addModule(configureModule(new RuinAndRecreateModule(Strategy.CLUSTER_BEST.toString(), best, clusters))); SearchStrategy stringRegret = new SearchStrategy(Strategy.STRING_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - stringRegret.addModule(new RuinAndRecreateModule(Strategy.STRING_REGRET.toString(), regret, stringRuin)); + stringRegret.addModule(configureModule(new RuinAndRecreateModule(Strategy.STRING_REGRET.toString(), regret, stringRuin))); SearchStrategy stringBest = new SearchStrategy(Strategy.STRING_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - stringBest.addModule(new RuinAndRecreateModule(Strategy.STRING_BEST.toString(), best, stringRuin)); + stringBest.addModule(configureModule(new RuinAndRecreateModule(Strategy.STRING_BEST.toString(), best, stringRuin))); PrettyAlgorithmBuilder prettyBuilder = PrettyAlgorithmBuilder.newInstance(vrp, vehicleFleetManager, stateManager, constraintManager); prettyBuilder.setRandom(random); @@ -697,6 +704,13 @@ public class Jsprit { } + private SearchStrategyModule configureModule(RuinAndRecreateModule ruinAndRecreateModule) { + ruinAndRecreateModule.setRandom(random); + ruinAndRecreateModule.setMinUnassignedJobsToBeReinserted(Integer.valueOf(properties.getProperty(Parameter.MIN_UNASSIGNED.toString()))); + ruinAndRecreateModule.setProportionOfUnassignedJobsToBeReinserted(Double.valueOf(properties.getProperty(Parameter.PROPORTION_UNASSIGNED.toString()))); + return ruinAndRecreateModule; + } + private DefaultScorer getRegretScorer(VehicleRoutingProblem vrp) { DefaultScorer scorer = new DefaultScorer(vrp); scorer.setTimeWindowParam(Double.valueOf(properties.getProperty(Parameter.REGRET_TIME_WINDOW_SCORER.toString()))); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModule.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModule.java index 9c6e85cc..71facbc2 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModule.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModule.java @@ -26,9 +26,7 @@ import com.graphhopper.jsprit.core.algorithm.ruin.listener.RuinListener; import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; public class RuinAndRecreateModule implements SearchStrategyModule { @@ -39,6 +37,12 @@ public class RuinAndRecreateModule implements SearchStrategyModule { private String moduleName; + private Random random = new Random(4711); + + private int minUnassignedJobsToBeReinserted = Integer.MAX_VALUE; + + private double proportionOfUnassignedJobsToBeReinserted = 1d; + public RuinAndRecreateModule(String moduleName, InsertionStrategy insertion, RuinStrategy ruin) { super(); this.insertion = insertion; @@ -46,16 +50,58 @@ public class RuinAndRecreateModule implements SearchStrategyModule { this.moduleName = moduleName; } + /** + * To make overall results reproducible, make sure this class is provided with the "global" random number generator. + * + * @param random + */ + public void setRandom(Random random) { + this.random = random; + } + + /** + * Minimum number of unassigned jobs that is reinserted in each iteration. + * + * @param minUnassignedJobsToBeReinserted + */ + public void setMinUnassignedJobsToBeReinserted(int minUnassignedJobsToBeReinserted) { + this.minUnassignedJobsToBeReinserted = minUnassignedJobsToBeReinserted; + } + + /** + * Proportion of unassigned jobs that is reinserted in each iteration. + * + * @param proportionOfUnassignedJobsToBeReinserted + */ + public void setProportionOfUnassignedJobsToBeReinserted(double proportionOfUnassignedJobsToBeReinserted) { + this.proportionOfUnassignedJobsToBeReinserted = proportionOfUnassignedJobsToBeReinserted; + } + @Override - public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) { - Collection ruinedJobs = ruin.ruin(vrpSolution.getRoutes()); - Set ruinedJobSet = new HashSet(); + public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution previousVrpSolution) { + Collection ruinedJobs = ruin.ruin(previousVrpSolution.getRoutes()); + Set ruinedJobSet = new HashSet<>(); ruinedJobSet.addAll(ruinedJobs); - ruinedJobSet.addAll(vrpSolution.getUnassignedJobs()); - Collection unassignedJobs = insertion.insertJobs(vrpSolution.getRoutes(), ruinedJobSet); - vrpSolution.getUnassignedJobs().clear(); - vrpSolution.getUnassignedJobs().addAll(unassignedJobs); - return vrpSolution; + List stillUnassignedInThisIteration = new ArrayList<>(); + if (previousVrpSolution.getUnassignedJobs().size() < minUnassignedJobsToBeReinserted) { + ruinedJobSet.addAll(previousVrpSolution.getUnassignedJobs()); + } else { + int noUnassignedToBeInserted = Math.max(minUnassignedJobsToBeReinserted, (int) (previousVrpSolution.getUnassignedJobs().size() * proportionOfUnassignedJobsToBeReinserted)); + List jobList = new ArrayList<>(previousVrpSolution.getUnassignedJobs()); + Collections.shuffle(jobList, random); + for (int i = 0; i < noUnassignedToBeInserted; i++) { + ruinedJobSet.add(jobList.get(i)); + } + for (int i = noUnassignedToBeInserted; i < jobList.size(); i++) { + stillUnassignedInThisIteration.add(jobList.get(i)); + } + } + Collection unassignedJobs = insertion.insertJobs(previousVrpSolution.getRoutes(), ruinedJobSet); + previousVrpSolution.getUnassignedJobs().clear(); + previousVrpSolution.getUnassignedJobs().addAll(unassignedJobs); + previousVrpSolution.getUnassignedJobs().addAll(stillUnassignedInThisIteration); + VehicleRoutingProblemSolution newSolution = previousVrpSolution; + return newSolution; } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculator.java index f706f4a0..e94bad61 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculator.java @@ -49,25 +49,25 @@ final class BreakInsertionCalculator implements JobInsertionCostsCalculator { private static final Logger logger = LoggerFactory.getLogger(BreakInsertionCalculator.class); - private HardRouteConstraint hardRouteLevelConstraint; + private final HardRouteConstraint hardRouteLevelConstraint; - private HardActivityConstraint hardActivityLevelConstraint; + private final HardActivityConstraint hardActivityLevelConstraint; - private SoftRouteConstraint softRouteConstraint; + private final SoftRouteConstraint softRouteConstraint; - private SoftActivityConstraint softActivityConstraint; + private final SoftActivityConstraint softActivityConstraint; - private VehicleRoutingTransportCosts transportCosts; + private final VehicleRoutingTransportCosts transportCosts; private final VehicleRoutingActivityCosts activityCosts; - private ActivityInsertionCostsCalculator additionalTransportCostsCalculator; + private final ActivityInsertionCostsCalculator additionalTransportCostsCalculator; - private JobActivityFactory activityFactory; + private final JobActivityFactory activityFactory; - private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; + private final AdditionalAccessEgressCalculator additionalAccessEgressCalculator; - public BreakInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager) { + public BreakInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager, JobActivityFactory activityFactory) { super(); this.transportCosts = routingCosts; this.activityCosts = activityCosts; @@ -77,12 +77,10 @@ final class BreakInsertionCalculator implements JobInsertionCostsCalculator { softRouteConstraint = constraintManager; this.additionalTransportCostsCalculator = additionalTransportCostsCalculator; additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); + this.activityFactory = activityFactory; logger.debug("initialise " + this); } - public void setJobActivityFactory(JobActivityFactory jobActivityFactory) { - this.activityFactory = jobActivityFactory; - } @Override public String toString() { diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculatorFactory.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculatorFactory.java new file mode 100644 index 00000000..0b550a47 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculatorFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.problem.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; + +public class BreakInsertionCalculatorFactory implements JobInsertionCostsCalculatorFactory { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakScheduling.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakScheduling.java index 90a7076f..66fbb182 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakScheduling.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakScheduling.java @@ -49,8 +49,7 @@ public class BreakScheduling implements InsertionStartsListener,JobInsertedListe public BreakScheduling(VehicleRoutingProblem vrp, StateManager stateManager, ConstraintManager constraintManager) { this.stateManager = stateManager; - this.breakInsertionCalculator = new BreakInsertionCalculator(vrp.getTransportCosts(),vrp.getActivityCosts(),new LocalActivityInsertionCostsCalculator(vrp.getTransportCosts(),vrp.getActivityCosts(),stateManager),constraintManager); - this.breakInsertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory()); + this.breakInsertionCalculator = new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), new LocalActivityInsertionCostsCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), stateManager), constraintManager, vrp.getJobActivityFactory()); eventListeners = new EventListeners(); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionBuilder.java index 98e30be4..3a30411a 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionBuilder.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionBuilder.java @@ -28,7 +28,10 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; - +/** + * use InsertionStrategyBuilder instead + */ +@Deprecated public class InsertionBuilder { private boolean fastRegret; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java new file mode 100644 index 00000000..310047fc --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java @@ -0,0 +1,239 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.algorithm.listener.VehicleRoutingAlgorithmListeners; +import com.graphhopper.jsprit.core.algorithm.recreate.listener.InsertionListener; +import com.graphhopper.jsprit.core.algorithm.state.StateManager; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleFleetManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; + + +public class InsertionStrategyBuilder { + + private boolean fastRegret; + + + public enum Strategy { + REGRET, BEST + } + + private VehicleRoutingProblem vrp; + + private StateManager stateManager; + + private boolean local = true; + + private ConstraintManager constraintManager; + + private VehicleFleetManager fleetManager; + + private double weightOfFixedCosts; + + private boolean considerFixedCosts = false; + + private ActivityInsertionCostsCalculator actInsertionCostsCalculator = null; + + private int forwaredLooking; + + private int memory; + + private ExecutorService executor; + + private int nuOfThreads; + + private double timeSlice; + + private int nNeighbors; + + private boolean timeScheduling = false; + + private boolean allowVehicleSwitch = true; + + private boolean addDefaultCostCalc = true; + + private Strategy strategy = Strategy.BEST; + + private boolean isFastRegret = false; + + private JobInsertionCostsCalculatorFactory shipmentInsertionCalculatorFactory; + + private JobInsertionCostsCalculatorFactory serviceInsertionCalculatorFactory; + + private JobInsertionCostsCalculatorFactory breakInsertionCalculatorFactory; + + public InsertionStrategyBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager, ConstraintManager constraintManager) { + super(); + this.vrp = vrp; + this.stateManager = stateManager; + this.constraintManager = constraintManager; + this.fleetManager = vehicleFleetManager; + } + + public InsertionStrategyBuilder setShipmentInsertionCalculatorFactory(JobInsertionCostsCalculatorFactory shipmentInsertionCalculatorFactory) { + this.shipmentInsertionCalculatorFactory = shipmentInsertionCalculatorFactory; + return this; + } + + public InsertionStrategyBuilder setServiceInsertionCalculator(JobInsertionCostsCalculatorFactory serviceInsertionCalculator) { + this.serviceInsertionCalculatorFactory = serviceInsertionCalculator; + return this; + } + + public InsertionStrategyBuilder setBreakInsertionCalculator(JobInsertionCostsCalculatorFactory breakInsertionCalculator) { + this.breakInsertionCalculatorFactory = breakInsertionCalculator; + return this; + } + + public InsertionStrategyBuilder setInsertionStrategy(Strategy strategy) { + this.strategy = strategy; + return this; + } + + public InsertionStrategyBuilder setRouteLevel(int forwardLooking, int memory) { + local = false; + this.forwaredLooking = forwardLooking; + this.memory = memory; + return this; + } + + public InsertionStrategyBuilder setRouteLevel(int forwardLooking, int memory, boolean addDefaultMarginalCostCalculation) { + local = false; + this.forwaredLooking = forwardLooking; + this.memory = memory; + this.addDefaultCostCalc = addDefaultMarginalCostCalculation; + return this; + } + + public InsertionStrategyBuilder setFastRegret(boolean fastRegret) { + this.isFastRegret = fastRegret; + return this; + } + + + public InsertionStrategyBuilder setLocalLevel() { + local = true; + return this; + } + + /** + * If addDefaulMarginalCostCalculation is false, no calculator is set which implicitly assumes that marginal cost calculation + * is controlled by your custom soft constraints. + * + * @param addDefaultMarginalCostCalculation + * @return + */ + public InsertionStrategyBuilder setLocalLevel(boolean addDefaultMarginalCostCalculation) { + local = true; + addDefaultCostCalc = addDefaultMarginalCostCalculation; + return this; + } + + public InsertionStrategyBuilder considerFixedCosts(double weightOfFixedCosts) { + this.weightOfFixedCosts = weightOfFixedCosts; + this.considerFixedCosts = true; + return this; + } + + public InsertionStrategyBuilder setActivityInsertionCostCalculator(ActivityInsertionCostsCalculator activityInsertionCostsCalculator) { + this.actInsertionCostsCalculator = activityInsertionCostsCalculator; + return this; + } + + public InsertionStrategyBuilder setConcurrentMode(ExecutorService executor, int nuOfThreads) { + this.executor = executor; + this.nuOfThreads = nuOfThreads; + return this; + } + + + public InsertionStrategy build() { + List iListeners = new ArrayList(); + List algorithmListeners = new ArrayList(); + JobInsertionCostsCalculatorBuilder calcBuilder = new JobInsertionCostsCalculatorBuilder(iListeners, algorithmListeners); + if (local) { + calcBuilder.setLocalLevel(addDefaultCostCalc); + } else { + calcBuilder.setRouteLevel(forwaredLooking, memory, addDefaultCostCalc); + } + if (shipmentInsertionCalculatorFactory != null) + calcBuilder.setShipmentCalculatorFactory(shipmentInsertionCalculatorFactory); + if (serviceInsertionCalculatorFactory != null) + calcBuilder.setServiceCalculatorFactory(serviceInsertionCalculatorFactory); + if (breakInsertionCalculatorFactory != null) + calcBuilder.setBreakCalculatorFactory(breakInsertionCalculatorFactory); + calcBuilder.setConstraintManager(constraintManager); + calcBuilder.setStateManager(stateManager); + calcBuilder.setVehicleRoutingProblem(vrp); + calcBuilder.setVehicleFleetManager(fleetManager); + calcBuilder.setActivityInsertionCostsCalculator(actInsertionCostsCalculator); + if (considerFixedCosts) { + calcBuilder.considerFixedCosts(weightOfFixedCosts); + } + if (timeScheduling) { + calcBuilder.experimentalTimeScheduler(timeSlice, nNeighbors); + } + calcBuilder.setAllowVehicleSwitch(allowVehicleSwitch); + JobInsertionCostsCalculator costCalculator = calcBuilder.build(); + + InsertionStrategy insertion; + if (strategy.equals(Strategy.BEST)) { + if (executor == null) { + insertion = new BestInsertion(costCalculator, vrp); + } else { + insertion = new BestInsertionConcurrent(costCalculator, executor, nuOfThreads, vrp); + } + } else if (strategy.equals(Strategy.REGRET)) { + if (executor == null) { + if (isFastRegret) { + RegretInsertionFast regret = new RegretInsertionFast(costCalculator, vrp, fleetManager); + regret.setSwitchAllowed(allowVehicleSwitch); + insertion = regret; + } else { + RegretInsertion regret = new RegretInsertion(costCalculator, vrp); + insertion = regret; + } + + } else { + if (isFastRegret) { + RegretInsertionConcurrentFast regret = new RegretInsertionConcurrentFast(costCalculator, vrp, executor, fleetManager); + regret.setSwitchAllowed(allowVehicleSwitch); + insertion = regret; + } else { + RegretInsertionConcurrent regret = new RegretInsertionConcurrent(costCalculator, vrp, executor); + insertion = regret; + } + + } + } else throw new IllegalStateException("you should never get here"); + for (InsertionListener l : iListeners) insertion.addListener(l); + return insertion; + } + + public InsertionStrategyBuilder setAllowVehicleSwitch(boolean allowVehicleSwitch) { + this.allowVehicleSwitch = allowVehicleSwitch; + return this; + } + + +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculator.java index 81d1670c..8a1414e3 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculator.java @@ -25,6 +25,6 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; public interface JobInsertionCostsCalculator { - public InsertionData getInsertionData(VehicleRoute currentRoute, Job newJob, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownCosts); + InsertionData getInsertionData(VehicleRoute currentRoute, Job newJob, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownCosts); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java index 1135adb8..faf760c0 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java @@ -94,6 +94,12 @@ public class JobInsertionCostsCalculatorBuilder { private boolean addDefaultCostCalc = true; + private JobInsertionCostsCalculatorFactory shipmentCalculatorFactory = new ShipmentInsertionCalculatorFactory(); + + private JobInsertionCostsCalculatorFactory serviceCalculatorFactory = new ServiceInsertionCalculatorFactory(); + + private JobInsertionCostsCalculatorFactory breakCalculatorFactory = new BreakInsertionCalculatorFactory(); + /** * Constructs the builder. *

@@ -109,6 +115,24 @@ public class JobInsertionCostsCalculatorBuilder { this.algorithmListeners = algorithmListeners; } + public JobInsertionCostsCalculatorBuilder setShipmentCalculatorFactory(JobInsertionCostsCalculatorFactory shipmentCalculatorFactory) { + if (shipmentCalculatorFactory == null) return this; + this.shipmentCalculatorFactory = shipmentCalculatorFactory; + return this; + } + + public JobInsertionCostsCalculatorBuilder setServiceCalculatorFactory(JobInsertionCostsCalculatorFactory serviceCalculatorFactory) { + if (serviceCalculatorFactory == null) return this; + this.serviceCalculatorFactory = serviceCalculatorFactory; + return this; + } + + public JobInsertionCostsCalculatorBuilder setBreakCalculatorFactory(JobInsertionCostsCalculatorFactory breakCalculatorFactory) { + if (breakCalculatorFactory == null) return this; + this.breakCalculatorFactory = breakCalculatorFactory; + return this; + } + /** * Sets activityStates. MUST be set. * @@ -287,20 +311,17 @@ public class JobInsertionCostsCalculatorBuilder { } }; - ShipmentInsertionCalculator shipmentInsertion = new ShipmentInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(),actInsertionCalc, constraintManager); - shipmentInsertion.setJobActivityFactory(activityFactory); - ServiceInsertionCalculator serviceInsertion = new ServiceInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), actInsertionCalc, constraintManager); - serviceInsertion.setJobActivityFactory(activityFactory); - BreakInsertionCalculator breakInsertionCalculator = new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), actInsertionCalc, constraintManager); - breakInsertionCalculator.setJobActivityFactory(activityFactory); + JobInsertionCostsCalculator shipmentInsertion = shipmentCalculatorFactory.create(vrp, actInsertionCalc, activityFactory, constraintManager); + JobInsertionCostsCalculator serviceInsertion = serviceCalculatorFactory.create(vrp, actInsertionCalc, activityFactory, constraintManager); + JobInsertionCostsCalculator breakInsertion = breakCalculatorFactory.create(vrp, actInsertionCalc, activityFactory, constraintManager); JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); switcher.put(Shipment.class, shipmentInsertion); switcher.put(Service.class, serviceInsertion); switcher.put(Pickup.class, serviceInsertion); switcher.put(Delivery.class, serviceInsertion); - switcher.put(Break.class, breakInsertionCalculator); + switcher.put(Break.class, breakInsertion); CalculatorPlusListeners calculatorPlusListeners = new CalculatorPlusListeners(switcher); if (configLocal != null) { diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorFactory.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorFactory.java new file mode 100644 index 00000000..b683b88d --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorFactory.java @@ -0,0 +1,29 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.problem.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; + +public interface JobInsertionCostsCalculatorFactory { + + JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager); + +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java index 0f91eab1..3e0725fe 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java @@ -37,7 +37,7 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; * * @author stefan */ -class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCalculator { +public class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCalculator { private VehicleRoutingTransportCosts routingCosts; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java index 9f91ff9b..6007decb 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java @@ -55,38 +55,35 @@ final class ServiceInsertionCalculator extends AbstractInsertionCalculator { // private HardActivityConstraint hardActivityLevelConstraint; - private SoftRouteConstraint softRouteConstraint; + private final SoftRouteConstraint softRouteConstraint; - private SoftActivityConstraint softActivityConstraint; + private final SoftActivityConstraint softActivityConstraint; - private VehicleRoutingTransportCosts transportCosts; + private final VehicleRoutingTransportCosts transportCosts; private final VehicleRoutingActivityCosts activityCosts; - private ActivityInsertionCostsCalculator additionalTransportCostsCalculator; + private final ActivityInsertionCostsCalculator activityInsertionCostsCalculator; - private JobActivityFactory activityFactory; + private final JobActivityFactory activityFactory; - private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; + private final AdditionalAccessEgressCalculator additionalAccessEgressCalculator; - private ConstraintManager constraintManager; + private final ConstraintManager constraintManager; - public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager) { + public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager, JobActivityFactory activityFactory) { super(); this.transportCosts = routingCosts; this.activityCosts = activityCosts; this.constraintManager = constraintManager; softActivityConstraint = constraintManager; softRouteConstraint = constraintManager; - this.additionalTransportCostsCalculator = additionalTransportCostsCalculator; + this.activityInsertionCostsCalculator = activityInsertionCostsCalculator; additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); + this.activityFactory = activityFactory; logger.debug("initialise {}", this); } - public void setJobActivityFactory(JobActivityFactory jobActivityFactory) { - this.activityFactory = jobActivityFactory; - } - @Override public String toString() { return "[name=calculatesServiceInsertion]"; @@ -151,7 +148,7 @@ final class ServiceInsertionCalculator extends AbstractInsertionCalculator { ConstraintsStatus status = fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime, failedActivityConstraints, constraintManager); if (status.equals(ConstraintsStatus.FULFILLED)) { double additionalICostsAtActLevel = softActivityConstraint.getCosts(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); - double additionalTransportationCosts = additionalTransportCostsCalculator.getCosts(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActStartTime); + double additionalTransportationCosts = activityInsertionCostsCalculator.getCosts(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActStartTime); if (additionalICostsAtRouteLevel + additionalICostsAtActLevel + additionalTransportationCosts < bestCost) { bestCost = additionalICostsAtRouteLevel + additionalICostsAtActLevel + additionalTransportationCosts; insertionIndex = actIndex; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculatorFactory.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculatorFactory.java new file mode 100644 index 00000000..ca9252f2 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculatorFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.problem.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; + +public class ServiceInsertionCalculatorFactory implements JobInsertionCostsCalculatorFactory { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new ServiceInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java index e8698483..9eb5967c 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java @@ -48,25 +48,21 @@ final class ShipmentInsertionCalculator extends AbstractInsertionCalculator { private final ConstraintManager constraintManager; -// private HardRouteConstraint hardRouteLevelConstraint; -// -// private HardActivityConstraint hardActivityLevelConstraint; + private final SoftRouteConstraint softRouteConstraint; - private SoftRouteConstraint softRouteConstraint; + private final SoftActivityConstraint softActivityConstraint; - private SoftActivityConstraint softActivityConstraint; + private final ActivityInsertionCostsCalculator activityInsertionCostsCalculator; - private ActivityInsertionCostsCalculator activityInsertionCostsCalculator; + private final VehicleRoutingTransportCosts transportCosts; - private VehicleRoutingTransportCosts transportCosts; + private final VehicleRoutingActivityCosts activityCosts; - private VehicleRoutingActivityCosts activityCosts; + private final JobActivityFactory activityFactory; - private JobActivityFactory activityFactory; + private final AdditionalAccessEgressCalculator additionalAccessEgressCalculator; - private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; - - public ShipmentInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager) { + public ShipmentInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager, JobActivityFactory jobActivityFactory) { super(); this.activityInsertionCostsCalculator = activityInsertionCostsCalculator; this.constraintManager = constraintManager; @@ -75,16 +71,13 @@ final class ShipmentInsertionCalculator extends AbstractInsertionCalculator { this.transportCosts = routingCosts; this.activityCosts = activityCosts; additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); + this.activityFactory = jobActivityFactory; logger.debug("initialise {}", this); } - public void setJobActivityFactory(JobActivityFactory activityFactory) { - this.activityFactory = activityFactory; - } - @Override public String toString() { - return "[name=calculatesServiceInsertion]"; + return "[name=calculatesShipmentInsertion]"; } /** diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFactory.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFactory.java new file mode 100644 index 00000000..540918f1 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFactory.java @@ -0,0 +1,30 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.problem.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; + +public class ShipmentInsertionCalculatorFactory implements JobInsertionCostsCalculatorFactory { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new ShipmentInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlex.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlex.java new file mode 100644 index 00000000..b490b246 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlex.java @@ -0,0 +1,274 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.problem.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus; +import com.graphhopper.jsprit.core.problem.constraint.SoftActivityConstraint; +import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint; +import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts; +import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts; +import com.graphhopper.jsprit.core.problem.driver.Driver; +import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.job.Shipment; +import com.graphhopper.jsprit.core.problem.misc.ActivityContext; +import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; +import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import com.graphhopper.jsprit.core.problem.solution.route.activity.End; +import com.graphhopper.jsprit.core.problem.solution.route.activity.Start; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + + +public final class ShipmentInsertionCalculatorFlex extends AbstractInsertionCalculator { + + private static final Logger logger = LoggerFactory.getLogger(ShipmentInsertionCalculatorFlex.class); + + private final ConstraintManager constraintManager; + +// private HardRouteConstraint hardRouteLevelConstraint; +// +// private HardActivityConstraint hardActivityLevelConstraint; + + private SoftRouteConstraint softRouteConstraint; + + private SoftActivityConstraint softActivityConstraint; + + private ActivityInsertionCostsCalculator activityInsertionCostsCalculator; + + private VehicleRoutingTransportCosts transportCosts; + + private VehicleRoutingActivityCosts activityCosts; + + private JobActivityFactory activityFactory; + + private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; + + private int evalIndexPickup = Integer.MAX_VALUE; + private int evalIndexDelivery = Integer.MAX_VALUE; + + public void setEvalIndexPickup(int evalIndexPickup) { + this.evalIndexPickup = evalIndexPickup; + } + + public void setEvalIndexDelivery(int evalIndexDelivery) { + this.evalIndexDelivery = evalIndexDelivery; + } + + public ShipmentInsertionCalculatorFlex(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager) { + super(); + this.activityInsertionCostsCalculator = activityInsertionCostsCalculator; + this.constraintManager = constraintManager; + this.softActivityConstraint = constraintManager; + this.softRouteConstraint = constraintManager; + this.transportCosts = routingCosts; + this.activityCosts = activityCosts; + additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); + logger.debug("initialise {}", this); + } + + public void setJobActivityFactory(JobActivityFactory activityFactory) { + this.activityFactory = activityFactory; + } + + @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 getInsertionData(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownCosts) { + JobInsertionContext insertionContext = new JobInsertionContext(currentRoute, jobToInsert, newVehicle, newDriver, newVehicleDepartureTime); + Shipment shipment = (Shipment) jobToInsert; + TourActivity pickupShipment = activityFactory.createActivities(shipment).get(0); + TourActivity deliverShipment = activityFactory.createActivities(shipment).get(1); + insertionContext.getAssociatedActivities().add(pickupShipment); + insertionContext.getAssociatedActivities().add(deliverShipment); + + /* + check hard route constraints + */ + InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager); + if (noInsertion != null) return noInsertion; + /* + check soft route constraints + */ + + + double additionalICostsAtRouteLevel = softRouteConstraint.getCosts(insertionContext); + + double bestCost = bestKnownCosts; + additionalICostsAtRouteLevel += additionalAccessEgressCalculator.getCosts(insertionContext); + + int pickupInsertionIndex = InsertionData.NO_INDEX; + int deliveryInsertionIndex = InsertionData.NO_INDEX; + + TimeWindow bestPickupTimeWindow = null; + TimeWindow bestDeliveryTimeWindow = null; + + + Start start = new Start(newVehicle.getStartLocation(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival()); + start.setEndTime(newVehicleDepartureTime); + End end = new End(newVehicle.getEndLocation(), 0.0, newVehicle.getLatestArrival()); + ActivityContext pickupContext = new ActivityContext(); + TourActivity prevAct = start; + double prevActEndTime = newVehicleDepartureTime; + + //loops + int i = 0; + boolean tourEnd = false; + //pickupShipmentLoop + List activities = currentRoute.getTourActivities().getActivities(); + + List failedActivityConstraints = new ArrayList<>(); + while (!tourEnd) { + TourActivity nextAct; + if (i < activities.size()) { + nextAct = activities.get(i); + } else { + nextAct = end; + tourEnd = true; + } + if (i > evalIndexPickup) break; + if (i == evalIndexPickup || evalIndexPickup == Integer.MAX_VALUE) { + boolean pickupInsertionNotFulfilledBreak = true; + for (TimeWindow pickupTimeWindow : shipment.getPickupTimeWindows()) { + pickupShipment.setTheoreticalEarliestOperationStartTime(pickupTimeWindow.getStart()); + pickupShipment.setTheoreticalLatestOperationStartTime(pickupTimeWindow.getEnd()); + ActivityContext activityContext = new ActivityContext(); + activityContext.setInsertionIndex(i); + insertionContext.setActivityContext(activityContext); + ConstraintsStatus pickupShipmentConstraintStatus = fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime, failedActivityConstraints, constraintManager); + if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) { + pickupInsertionNotFulfilledBreak = false; + continue; + } else if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) { + continue; + } else if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) { + pickupInsertionNotFulfilledBreak = false; + } + double additionalPickupICosts = softActivityConstraint.getCosts(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime); + double pickupAIC = calculate(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime); + + double shipmentPickupArrTime = prevActEndTime + transportCosts.getTransportTime(prevAct.getLocation(), pickupShipment.getLocation(), prevActEndTime, newDriver, newVehicle); + double shipmentPickupEndTime = Math.max(shipmentPickupArrTime, pickupShipment.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(pickupShipment, shipmentPickupArrTime, newDriver, newVehicle); + + pickupContext.setArrivalTime(shipmentPickupArrTime); + pickupContext.setEndTime(shipmentPickupEndTime); + pickupContext.setInsertionIndex(i); + insertionContext.setRelatedActivityContext(pickupContext); + + TourActivity prevActForDeliveryLoop = pickupShipment; + double prevActEndTimeForDeliveryLoop = shipmentPickupEndTime; + + /* + -------------------------------- + */ + //deliverShipmentLoop + int j = i; + boolean tourEndInDeliveryLoop = false; + while (!tourEndInDeliveryLoop) { + TourActivity nextActForDeliveryLoop; + if (j < activities.size()) { + nextActForDeliveryLoop = activities.get(j); + } else { + nextActForDeliveryLoop = end; + tourEndInDeliveryLoop = true; + } + if (j > evalIndexDelivery) break; + if (j == evalIndexDelivery || evalIndexDelivery == Integer.MAX_VALUE) { + boolean deliveryInsertionNotFulfilledBreak = true; + for (TimeWindow deliveryTimeWindow : shipment.getDeliveryTimeWindows()) { + deliverShipment.setTheoreticalEarliestOperationStartTime(deliveryTimeWindow.getStart()); + deliverShipment.setTheoreticalLatestOperationStartTime(deliveryTimeWindow.getEnd()); + ActivityContext activityContext_ = new ActivityContext(); + activityContext_.setInsertionIndex(j); + insertionContext.setActivityContext(activityContext_); + ConstraintsStatus deliverShipmentConstraintStatus = fulfilled(insertionContext, prevActForDeliveryLoop, deliverShipment, nextActForDeliveryLoop, prevActEndTimeForDeliveryLoop, failedActivityConstraints, constraintManager); + if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) { + double additionalDeliveryICosts = softActivityConstraint.getCosts(insertionContext, prevActForDeliveryLoop, deliverShipment, nextActForDeliveryLoop, prevActEndTimeForDeliveryLoop); + double deliveryAIC = calculate(insertionContext, prevActForDeliveryLoop, deliverShipment, nextActForDeliveryLoop, prevActEndTimeForDeliveryLoop); + double totalActivityInsertionCosts = pickupAIC + deliveryAIC + + additionalICostsAtRouteLevel + additionalPickupICosts + additionalDeliveryICosts; + if (totalActivityInsertionCosts < bestCost) { + bestCost = totalActivityInsertionCosts; + pickupInsertionIndex = i; + deliveryInsertionIndex = j; + bestPickupTimeWindow = pickupTimeWindow; + bestDeliveryTimeWindow = deliveryTimeWindow; + } + deliveryInsertionNotFulfilledBreak = false; + } else if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) { + deliveryInsertionNotFulfilledBreak = false; + } + } + if (deliveryInsertionNotFulfilledBreak) break; + } + //update prevAct and endTime + double nextActArrTime = prevActEndTimeForDeliveryLoop + transportCosts.getTransportTime(prevActForDeliveryLoop.getLocation(), nextActForDeliveryLoop.getLocation(), prevActEndTimeForDeliveryLoop, newDriver, newVehicle); + prevActEndTimeForDeliveryLoop = Math.max(nextActArrTime, nextActForDeliveryLoop.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextActForDeliveryLoop, nextActArrTime, newDriver, newVehicle); + prevActForDeliveryLoop = nextActForDeliveryLoop; + j++; + } + } + if (pickupInsertionNotFulfilledBreak) { + break; + } + } + + //update prevAct and endTime + double nextActArrTime = prevActEndTime + transportCosts.getTransportTime(prevAct.getLocation(), nextAct.getLocation(), prevActEndTime, newDriver, newVehicle); + prevActEndTime = Math.max(nextActArrTime, nextAct.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextAct, nextActArrTime, newDriver, newVehicle); + prevAct = nextAct; + i++; + } + + + if (pickupInsertionIndex == InsertionData.NO_INDEX) { + InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); + emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints); + return emptyInsertionData; + } + InsertionData insertionData = new InsertionData(bestCost, pickupInsertionIndex, deliveryInsertionIndex, newVehicle, newDriver); + pickupShipment.setTheoreticalEarliestOperationStartTime(bestPickupTimeWindow.getStart()); + pickupShipment.setTheoreticalLatestOperationStartTime(bestPickupTimeWindow.getEnd()); + deliverShipment.setTheoreticalEarliestOperationStartTime(bestDeliveryTimeWindow.getStart()); + deliverShipment.setTheoreticalLatestOperationStartTime(bestDeliveryTimeWindow.getEnd()); + insertionData.setVehicleDepartureTime(newVehicleDepartureTime); + insertionData.getEvents().add(new InsertActivity(currentRoute, newVehicle, deliverShipment, deliveryInsertionIndex)); + insertionData.getEvents().add(new InsertActivity(currentRoute, newVehicle, pickupShipment, pickupInsertionIndex)); + insertionData.getEvents().add(new SwitchVehicle(currentRoute, newVehicle, newVehicleDepartureTime)); + return insertionData; + } + + private double calculate(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double departureTimeAtPrevAct) { + return activityInsertionCostsCalculator.getCosts(iFacts, prevAct, nextAct, newAct, departureTimeAtPrevAct); + + } +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java index ad2a7ea0..c54ed687 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java @@ -87,7 +87,7 @@ public class VehicleRoutingProblem { private FleetSize fleetSize = FleetSize.INFINITE; - private Collection vehicleTypes = new ArrayList(); + private Map vehicleTypes = new HashMap<>(); private Collection initialRoutes = new ArrayList(); @@ -387,8 +387,13 @@ public class VehicleRoutingProblem { incVehicleTypeIdIndexCounter(); } uniqueVehicles.add(vehicle); - if (!vehicleTypes.contains(vehicle.getType())) { - vehicleTypes.add(vehicle.getType()); + if (!vehicleTypes.containsKey(vehicle.getType().getTypeId())) { + vehicleTypes.put(vehicle.getType().getTypeId(), vehicle.getType()); + } else { + VehicleType existingType = vehicleTypes.get(vehicle.getType().getTypeId()); + if (!vehicle.getType().equals(existingType)) { + throw new IllegalArgumentException("A type with type id " + vehicle.getType().getTypeId() + " already exists. However, types are different. Please use unique vehicle types only."); + } } String startLocationId = vehicle.getStartLocation().getId(); addLocationToTentativeLocations(vehicle.getStartLocation()); @@ -496,7 +501,7 @@ public class VehicleRoutingProblem { * @return collection of vehicle-types */ public Collection getAddedVehicleTypes() { - return Collections.unmodifiableCollection(vehicleTypes); + return Collections.unmodifiableCollection(vehicleTypes.values()); } /** @@ -588,7 +593,7 @@ public class VehicleRoutingProblem { this.jobs = builder.jobs; this.fleetSize = builder.fleetSize; this.vehicles = builder.uniqueVehicles; - this.vehicleTypes = builder.vehicleTypes; + this.vehicleTypes = builder.vehicleTypes.values(); this.initialVehicleRoutes = builder.initialRoutes; this.transportCosts = builder.transportCosts; this.activityCosts = builder.activityCosts; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Break.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Break.java index 0a73b398..367ea7dc 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Break.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Break.java @@ -22,7 +22,7 @@ import com.graphhopper.jsprit.core.problem.Capacity; import com.graphhopper.jsprit.core.problem.Skills; /** - * Pickup extends Service and is intended to model a Service where smth is LOADED (i.e. picked up) to a transport unit. + * Break extends Service and is intended to model a time-window based driver break. * * @author schroeder */ @@ -31,7 +31,7 @@ public class Break extends Service { public static class Builder extends Service.Builder { /** - * Returns a new instance of builder that builds a pickup. + * Returns a new instance of builder that builds a break. * * @param id the id of the pickup * @return the builder @@ -47,9 +47,9 @@ public class Break extends Service { } /** - * Builds Pickup. + * Builds Break. *

- *

Pickup type is "pickup" + *

Pickup type is "break" * * @return pickup * @throws IllegalStateException if neither locationId nor coordinate has been set diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java index 112c137c..931d6187 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java @@ -81,6 +81,37 @@ public class VehicleTypeImpl implements VehicleType { public String toString() { return "[fixed=" + fix + "][perTime=" + perTransportTimeUnit + "][perDistance=" + perDistanceUnit + "][perWaitingTimeUnit=" + perWaitingTimeUnit + "]"; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VehicleCostParams)) return false; + + VehicleCostParams that = (VehicleCostParams) o; + + if (Double.compare(that.fix, fix) != 0) return false; + if (Double.compare(that.perTransportTimeUnit, perTransportTimeUnit) != 0) return false; + if (Double.compare(that.perDistanceUnit, perDistanceUnit) != 0) return false; + if (Double.compare(that.perWaitingTimeUnit, perWaitingTimeUnit) != 0) return false; + return Double.compare(that.perServiceTimeUnit, perServiceTimeUnit) == 0; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(fix); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perTransportTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perDistanceUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perWaitingTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perServiceTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } } /** @@ -299,32 +330,30 @@ public class VehicleTypeImpl implements VehicleType { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((typeId == null) ? 0 : typeId.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VehicleTypeImpl)) return false; + + VehicleTypeImpl that = (VehicleTypeImpl) o; + + if (Double.compare(that.maxVelocity, maxVelocity) != 0) return false; + if (!typeId.equals(that.typeId)) return false; + if (profile != null ? !profile.equals(that.profile) : that.profile != null) return false; + if (!vehicleCostParams.equals(that.vehicleCostParams)) return false; + return capacityDimensions.equals(that.capacityDimensions); } - /** - * Two vehicle-types are equal if they have the same vehicleId. - */ @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - VehicleTypeImpl other = (VehicleTypeImpl) obj; - if (typeId == null) { - if (other.typeId != null) - return false; - } else if (!typeId.equals(other.typeId)) - return false; - return true; + public int hashCode() { + int result; + long temp; + result = typeId.hashCode(); + result = 31 * result + (profile != null ? profile.hashCode() : 0); + result = 31 * result + vehicleCostParams.hashCode(); + result = 31 * result + capacityDimensions.hashCode(); + temp = Double.doubleToLongBits(maxVelocity); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; } private final String typeId; diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModuleTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModuleTest.java new file mode 100644 index 00000000..ceb6d0cc --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModuleTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jsprit.core.algorithm.module; + +import com.graphhopper.jsprit.core.algorithm.recreate.InsertionStrategy; +import com.graphhopper.jsprit.core.algorithm.ruin.RuinStrategy; +import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; +import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.mockito.Mockito.mock; + +public class RuinAndRecreateModuleTest { + + @Test + public void initialNumOfUnassignedShouldWorkCorrectly() { + InsertionStrategy insertionStrategy = mock(InsertionStrategy.class); + RuinStrategy ruinStrategy = mock(RuinStrategy.class); + RuinAndRecreateModule module = new RuinAndRecreateModule("name", insertionStrategy, ruinStrategy); + Collection routes = new ArrayList<>(); + List unassigned = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + unassigned.add(mock(Job.class)); + } + VehicleRoutingProblemSolution previousSolution = new VehicleRoutingProblemSolution(routes, unassigned, 0); + VehicleRoutingProblemSolution newSolution = module.runAndGetSolution(previousSolution); + Assert.assertEquals(0, newSolution.getUnassignedJobs().size()); + } + + @Test + public void proportionOfUnassignedShouldWorkCorrectly() { + InsertionStrategy insertionStrategy = mock(InsertionStrategy.class); + RuinStrategy ruinStrategy = mock(RuinStrategy.class); + RuinAndRecreateModule module = new RuinAndRecreateModule("name", insertionStrategy, ruinStrategy); + module.setMinUnassignedJobsToBeReinserted(5); + module.setProportionOfUnassignedJobsToBeReinserted(0.01); + Collection routes = new ArrayList<>(); + List unassigned = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + unassigned.add(mock(Job.class)); + } + VehicleRoutingProblemSolution previousSolution = new VehicleRoutingProblemSolution(routes, unassigned, 0); + VehicleRoutingProblemSolution newSolution = module.runAndGetSolution(previousSolution); + Assert.assertEquals(15, newSolution.getUnassignedJobs().size()); + } + + @Test + public void proportionOfUnassignedShouldWorkCorrectly2() { + InsertionStrategy insertionStrategy = mock(InsertionStrategy.class); + RuinStrategy ruinStrategy = mock(RuinStrategy.class); + RuinAndRecreateModule module = new RuinAndRecreateModule("name", insertionStrategy, ruinStrategy); + module.setMinUnassignedJobsToBeReinserted(5); + module.setProportionOfUnassignedJobsToBeReinserted(0.5); + Collection routes = new ArrayList<>(); + List unassigned = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + unassigned.add(mock(Job.class)); + } + VehicleRoutingProblemSolution previousSolution = new VehicleRoutingProblemSolution(routes, unassigned, 0); + VehicleRoutingProblemSolution newSolution = module.runAndGetSolution(previousSolution); + Assert.assertEquals(10, newSolution.getUnassignedJobs().size()); + } +} diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java index 5a1a3468..3536558a 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java @@ -90,7 +90,7 @@ public class ServiceInsertionAndLoadConstraintsTest { ActivityInsertionCostsCalculator activityInsertionCostsCalculator; - ShipmentInsertionCalculator insertionCalculator; +// ShipmentInsertionCalculator insertionCalculator; VehicleRoutingProblem vehicleRoutingProblem; @@ -109,7 +109,7 @@ public class ServiceInsertionAndLoadConstraintsTest { private void createInsertionCalculator(HardRouteConstraint hardRouteLevelConstraint) { ConstraintManager constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); constraintManager.addConstraint(hardRouteLevelConstraint); - insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); +// insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, ); } @Test @@ -143,10 +143,9 @@ public class ServiceInsertionAndLoadConstraintsTest { stateManager.informInsertionStarts(Arrays.asList(route), null); JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); - ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - serviceInsertionCalc.setJobActivityFactory(activityFactory); - ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - insertionCalculator.setJobActivityFactory(activityFactory); + ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + switcher.put(Pickup.class, serviceInsertionCalc); switcher.put(Delivery.class, serviceInsertionCalc); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java new file mode 100644 index 00000000..91657700 --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java @@ -0,0 +1,367 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.algorithm.recreate.listener.InsertionListeners; +import com.graphhopper.jsprit.core.algorithm.state.StateManager; +import com.graphhopper.jsprit.core.problem.AbstractActivity; +import com.graphhopper.jsprit.core.problem.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint; +import com.graphhopper.jsprit.core.problem.constraint.PickupAndDeliverShipmentLoadActivityLevelConstraint; +import com.graphhopper.jsprit.core.problem.constraint.ShipmentPickupsFirstConstraint; +import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts; +import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts; +import com.graphhopper.jsprit.core.problem.driver.Driver; +import com.graphhopper.jsprit.core.problem.driver.DriverImpl; +import com.graphhopper.jsprit.core.problem.job.Pickup; +import com.graphhopper.jsprit.core.problem.job.Service; +import com.graphhopper.jsprit.core.problem.job.Shipment; +import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; +import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import com.graphhopper.jsprit.core.problem.solution.route.activity.DeliverShipment; +import com.graphhopper.jsprit.core.problem.solution.route.activity.PickupService; +import com.graphhopper.jsprit.core.problem.solution.route.activity.PickupShipment; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; +import com.graphhopper.jsprit.core.problem.solution.route.state.RouteAndActivityStateGetter; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleType; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; +import com.graphhopper.jsprit.core.util.CostFactory; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +public class ShipmentInsertionCalculatorFlexTest { + + VehicleRoutingTransportCosts routingCosts; + + VehicleRoutingProblem vehicleRoutingProblem; + + VehicleRoutingActivityCosts activityCosts = new VehicleRoutingActivityCosts() { + + @Override + public double getActivityCost(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle) { + return 0; + } + + @Override + public double getActivityDuration(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle) { + return tourAct.getOperationTime(); + } + + }; + + HardRouteConstraint hardRouteLevelConstraint = new HardRouteConstraint() { + + @Override + public boolean fulfilled(JobInsertionContext insertionContext) { + return true; + } + + }; + + ActivityInsertionCostsCalculator activityInsertionCostsCalculator; + + ShipmentInsertionCalculatorFlex insertionCalculator; + + Vehicle vehicle; + + ConstraintManager constraintManager; + + @Before + public void doBefore() { + routingCosts = CostFactory.createManhattanCosts(); + VehicleType type = VehicleTypeImpl.Builder.newInstance("t").addCapacityDimension(0, 2).setCostPerDistance(1).build(); + vehicle = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance("0,0")).setType(type).build(); + activityInsertionCostsCalculator = new LocalActivityInsertionCostsCalculator(routingCosts, activityCosts, mock(StateManager.class)); + constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); + constraintManager.addConstraint(hardRouteLevelConstraint); + vehicleRoutingProblem = mock(VehicleRoutingProblem.class); + } + +// private void createInsertionCalculator(HardRouteConstraint hardRouteLevelConstraint) { +// ConstraintManager constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); +// constraintManager.addConstraint(hardRouteLevelConstraint); +// insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, ); +// } + + @Test + public void whenCalculatingInsertionCostsOfShipment_itShouldReturnCorrectCostValue() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment)); + activities.add(new DeliverShipment(shipment)); + when(activityFactory.createActivities(shipment)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + InsertionData iData = insertionCalculator.getInsertionData(route, shipment, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(40.0, iData.getInsertionCost(), 0.05); + } + + @Test + public void whenCalculatingInsertionIntoExistingRoute_itShouldReturnCorrectCosts() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); + new Inserter(new InsertionListeners(), vehicleRoutingProblem).insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment2)); + activities.add(new DeliverShipment(shipment2)); + when(activityFactory.createActivities(shipment2)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(0.0, iData.getInsertionCost(), 0.05); + assertEquals(1, iData.getPickupInsertionIndex()); + assertEquals(2, iData.getDeliveryInsertionIndex()); + } + + private List getTourActivities(Shipment shipment) { + List acts = new ArrayList(); + PickupShipment pick = new PickupShipment(shipment); + DeliverShipment del = new DeliverShipment(shipment); + acts.add(pick); + acts.add(del); + return acts; + } + + @Test + public void whenInsertingShipmentInRouteWithNotEnoughCapacity_itShouldReturnNoInsertion() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); + new Inserter(new InsertionListeners(), vehicleRoutingProblem).insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + + constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); + constraintManager.addConstraint(new HardRouteConstraint() { + + @Override + public boolean fulfilled(JobInsertionContext insertionContext) { + return false; + } + + }); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment2)); + activities.add(new DeliverShipment(shipment2)); + when(activityFactory.createActivities(shipment2)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); + assertTrue(iData instanceof InsertionData.NoInsertionFound); + + } + + + @Test + public void whenInsertingThirdShipment_itShouldCalcCorrectVal() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,0").build()).setDeliveryLocation(Location.newInstance("9,10")).build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); + when(vehicleRoutingProblem.copyAndGetActivities(shipment2)).thenReturn(getTourActivities(shipment2)); + Inserter inserter = new Inserter(new InsertionListeners(), vehicleRoutingProblem); + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment3)); + activities.add(new DeliverShipment(shipment3)); + when(activityFactory.createActivities(shipment3)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(0.0, iData.getInsertionCost(), 0.05); + assertEquals(0, iData.getPickupInsertionIndex()); + assertEquals(1, iData.getDeliveryInsertionIndex()); + } + + @Test + public void whenInsertingThirdShipment_itShouldCalcCorrectVal2() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,0").build()).setDeliveryLocation(Location.newInstance("9,9")).build(); + when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); + when(vehicleRoutingProblem.copyAndGetActivities(shipment2)).thenReturn(getTourActivities(shipment2)); + VehicleRoute route = VehicleRoute.emptyRoute(); + Inserter inserter = new Inserter(new InsertionListeners(), vehicleRoutingProblem); + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment3)); + activities.add(new DeliverShipment(shipment3)); + when(activityFactory.createActivities(shipment3)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(2.0, iData.getInsertionCost(), 0.05); + assertEquals(0, iData.getPickupInsertionIndex()); + assertEquals(1, iData.getDeliveryInsertionIndex()); + } + + @Test + public void whenInstertingShipmentWithLoadConstraintWhereCapIsNotSufficient_capConstraintsAreFulfilled() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,0").build()).setDeliveryLocation(Location.newInstance("9,9")).build(); + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + VehicleRoutingProblem vrp = vrpBuilder.addJob(shipment).addJob(shipment2).addJob(shipment3).build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + route.setVehicleAndDepartureTime(vehicle, 0.0); + + Inserter inserter = new Inserter(new InsertionListeners(), vrp); + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + StateManager stateManager = new StateManager(vrp); + stateManager.updateLoadStates(); + stateManager.informInsertionStarts(Arrays.asList(route), null); + + ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); + constraintManager.addConstraint(new PickupAndDeliverShipmentLoadActivityLevelConstraint(stateManager), ConstraintManager.Priority.CRITICAL); + constraintManager.addConstraint(new ShipmentPickupsFirstConstraint(), ConstraintManager.Priority.CRITICAL); + + ShipmentInsertionCalculatorFlex insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, + activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory()); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, DriverImpl.noDriver(), Double.MAX_VALUE); + assertTrue(iData instanceof InsertionData.NoInsertionFound); + + } + + @Ignore + @Test + public void whenInsertingShipmentWithLoadConstraintWhereCapIsNotSufficient_capConstraintsAreFulfilledV2() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,0").build()).setDeliveryLocation(Location.newInstance("9,9")).build(); + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + VehicleRoutingProblem vrp = vrpBuilder.addJob(shipment).addJob(shipment2).addJob(shipment3).build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + route.setVehicleAndDepartureTime(vehicle, 0.0); + + Inserter inserter = new Inserter(new InsertionListeners(), vrp); + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + StateManager stateManager = new StateManager(vrp); + stateManager.updateLoadStates(); + stateManager.informInsertionStarts(Arrays.asList(route), null); + + ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); + constraintManager.addConstraint(new PickupAndDeliverShipmentLoadActivityLevelConstraint(stateManager), ConstraintManager.Priority.CRITICAL); +// constraintManager.addConstraint(new ShipmentPickupsFirstConstraint(), ConstraintManager.Priority.CRITICAL); + + ShipmentInsertionCalculatorFlex insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, + activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setEvalIndexPickup(0); + insertionCalculator.setEvalIndexDelivery(3); + insertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory()); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, DriverImpl.noDriver(), Double.MAX_VALUE); + assertTrue(iData instanceof InsertionData.NoInsertionFound); + + } + + + @Test + public void whenInsertingServiceWhileNoCapIsAvailable_itMustReturnNoInsertionData() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + VehicleRoutingProblem vrp = vrpBuilder.addJob(shipment).addJob(shipment2).build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + route.setVehicleAndDepartureTime(vehicle, 0.0); + + Inserter inserter = new Inserter(new InsertionListeners(), vrp); + + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + StateManager stateManager = new StateManager(vrp); + stateManager.updateLoadStates(); + stateManager.informInsertionStarts(Arrays.asList(route), null); + + ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); + constraintManager.addLoadConstraint(); + + stateManager.informInsertionStarts(Arrays.asList(route), null); + + Pickup service = (Pickup) Pickup.Builder.newInstance("pick").addSizeDimension(0, 1).setLocation(Location.newInstance("5,5")).build(); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupService(service)); + when(activityFactory.createActivities(service)).thenReturn(activities); + + JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); + ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + ShipmentInsertionCalculatorFlex insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + switcher.put(Pickup.class, serviceInsertionCalc); + switcher.put(Service.class, serviceInsertionCalc); + switcher.put(Shipment.class, insertionCalculator); + + + InsertionData iData = switcher.getInsertionData(route, service, vehicle, 0, DriverImpl.noDriver(), Double.MAX_VALUE); +// routeActVisitor.visit(route); + + assertEquals(3, iData.getDeliveryInsertionIndex()); + } + + +} diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java index 9713744a..274bb5fe 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java @@ -94,21 +94,24 @@ public class ShipmentInsertionCalculatorTest { Vehicle vehicle; + ConstraintManager constraintManager; + @Before public void doBefore() { routingCosts = CostFactory.createManhattanCosts(); VehicleType type = VehicleTypeImpl.Builder.newInstance("t").addCapacityDimension(0, 2).setCostPerDistance(1).build(); vehicle = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance("0,0")).setType(type).build(); activityInsertionCostsCalculator = new LocalActivityInsertionCostsCalculator(routingCosts, activityCosts, mock(StateManager.class)); - createInsertionCalculator(hardRouteLevelConstraint); + constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); + constraintManager.addConstraint(hardRouteLevelConstraint); vehicleRoutingProblem = mock(VehicleRoutingProblem.class); } - private void createInsertionCalculator(HardRouteConstraint hardRouteLevelConstraint) { - ConstraintManager constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); - constraintManager.addConstraint(hardRouteLevelConstraint); - insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - } +// private void createInsertionCalculator(HardRouteConstraint hardRouteLevelConstraint) { +// ConstraintManager constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); +// constraintManager.addConstraint(hardRouteLevelConstraint); +// insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, ); +// } @Test public void whenCalculatingInsertionCostsOfShipment_itShouldReturnCorrectCostValue() { @@ -119,7 +122,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment)); activities.add(new DeliverShipment(shipment)); when(activityFactory.createActivities(shipment)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment, vehicle, 0.0, null, Double.MAX_VALUE); assertEquals(40.0, iData.getInsertionCost(), 0.05); } @@ -137,7 +140,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment2)); activities.add(new DeliverShipment(shipment2)); when(activityFactory.createActivities(shipment2)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); assertEquals(0.0, iData.getInsertionCost(), 0.05); @@ -161,7 +164,9 @@ public class ShipmentInsertionCalculatorTest { VehicleRoute route = VehicleRoute.emptyRoute(); when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); new Inserter(new InsertionListeners(), vehicleRoutingProblem).insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); - createInsertionCalculator(new HardRouteConstraint() { + + constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); + constraintManager.addConstraint(new HardRouteConstraint() { @Override public boolean fulfilled(JobInsertionContext insertionContext) { @@ -175,7 +180,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment2)); activities.add(new DeliverShipment(shipment2)); when(activityFactory.createActivities(shipment2)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); assertTrue(iData instanceof InsertionData.NoInsertionFound); @@ -201,7 +206,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment3)); activities.add(new DeliverShipment(shipment3)); when(activityFactory.createActivities(shipment3)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); assertEquals(0.0, iData.getInsertionCost(), 0.05); @@ -226,7 +231,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment3)); activities.add(new DeliverShipment(shipment3)); when(activityFactory.createActivities(shipment3)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); @@ -260,8 +265,7 @@ public class ShipmentInsertionCalculatorTest { constraintManager.addConstraint(new ShipmentPickupsFirstConstraint(), ConstraintManager.Priority.CRITICAL); ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, - activityInsertionCostsCalculator, constraintManager); - insertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory()); + activityInsertionCostsCalculator, constraintManager, vrp.getJobActivityFactory()); InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, DriverImpl.noDriver(), Double.MAX_VALUE); assertTrue(iData instanceof InsertionData.NoInsertionFound); @@ -293,22 +297,20 @@ public class ShipmentInsertionCalculatorTest { stateManager.informInsertionStarts(Arrays.asList(route), null); - JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); - ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - switcher.put(Pickup.class, serviceInsertionCalc); - switcher.put(Service.class, serviceInsertionCalc); - switcher.put(Shipment.class, insertionCalculator); - -// Service service = Service.Builder.newInstance("pick", 1).setLocationId("5,5").build(); Pickup service = (Pickup) Pickup.Builder.newInstance("pick").addSizeDimension(0, 1).setLocation(Location.newInstance("5,5")).build(); JobActivityFactory activityFactory = mock(JobActivityFactory.class); List activities = new ArrayList(); activities.add(new PickupService(service)); when(activityFactory.createActivities(service)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); - serviceInsertionCalc.setJobActivityFactory(activityFactory); + + JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); + ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + switcher.put(Pickup.class, serviceInsertionCalc); + switcher.put(Service.class, serviceInsertionCalc); + switcher.put(Shipment.class, insertionCalculator); + InsertionData iData = switcher.getInsertionData(route, service, vehicle, 0, DriverImpl.noDriver(), Double.MAX_VALUE); // routeActVisitor.visit(route); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java index 6345e700..fe26084e 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java @@ -136,13 +136,13 @@ public class TestCalculatesServiceInsertion { VehicleRoutingActivityCosts actCosts = mock(VehicleRoutingActivityCosts.class); - serviceInsertion = new ServiceInsertionCalculator(costs, vrp.getActivityCosts(), new LocalActivityInsertionCostsCalculator(costs, actCosts, states), cManager); - serviceInsertion.setJobActivityFactory(new JobActivityFactory() { + serviceInsertion = new ServiceInsertionCalculator(costs, vrp.getActivityCosts(), new LocalActivityInsertionCostsCalculator(costs, actCosts, states), cManager, new JobActivityFactory() { @Override public List createActivities(Job job) { return vrp.copyAndGetActivities(job); } }); + } @Test diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java index 7e4dbec2..046f1934 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java @@ -316,6 +316,17 @@ public class VehicleRoutingProblemTest { builder.addVehicle(vehicle2); } + @Test(expected = IllegalArgumentException.class) + public void whenAddingVehicleTypesWithSameIdButDifferentCosts_itShouldThrowException() { + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type1 = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleType type2 = VehicleTypeImpl.Builder.newInstance("type").setCostPerServiceTime(2d).build(); + VehicleImpl vehicle1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(Location.newInstance("loc")).setType(type1).build(); + VehicleImpl vehicle2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(Location.newInstance("loc")).setType(type2).build(); + builder.addVehicle(vehicle1); + builder.addVehicle(vehicle2); + } + @Test(expected = IllegalArgumentException.class) public void whenBuildingProblemWithSameBreakId_itShouldThrowException(){ VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java index 0c85b96a..bd145479 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java @@ -124,6 +124,7 @@ public class VehicleTypeImplTest { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").setCostPerDistance(-10).build(); } + @Test public void whenSettingPerDistanceCosts_itShouldBeSetCorrectly() { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").setCostPerDistance(10).build(); assertEquals(10.0, type.getVehicleCostParams().perDistanceUnit, 0.0); @@ -166,4 +167,32 @@ public class VehicleTypeImplTest { assertEquals(42, two.getUserData()); assertNull(three.getUserData()); } + + @Test + public void typesShouldBeEqual() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + assertTrue(one.equals(two)); + } + + @Test + public void typesShouldBeNotEqual() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + assertFalse(one.equals(two)); + } + + @Test + public void typesShouldBeNotEqual2() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 20).build(); + assertFalse(one.equals(two)); + } + + @Test + public void typesShouldBeEqual2() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + assertTrue(one.equals(two)); + } }