solutions, VehicleRoutingProblemSolution newSolution) {
+ boolean solutionAccepted = false;
+ if (solutions.size() == 0) {
+ solutions.add(newSolution);
+ solutionAccepted = true;
+ } else {
+ double threshold = getThreshold();
+ VehicleRoutingProblemSolution currentSolution = solutions.iterator().next();
+ if ((newSolution.getCost() - bestEver) / newSolution.getCost() < threshold) {
+ solutions.remove(currentSolution);
+ solutions.add(newSolution);
+ solutionAccepted = true;
+ }
+ }
+ if (newSolution.getCost() < bestEver) bestEver = newSolution.getCost();
+ return solutionAccepted;
+ }
+
+ private double getThreshold() {
+ return initialThreshold - (initialThreshold - endThreshold) * currentIteration / maxIterations;
+ }
+
+ @Override
+ public String toString() {
+ return "[name=record-to-record-travel]";
+ }
+
+
+ @SuppressWarnings("UnusedDeclaration")
+ public double getInitialThreshold() {
+ return initialThreshold;
+ }
+
+ public double getEndThreshold() {
+ return endThreshold;
+ }
+
+ /**
+ * Sets initial threshold.
+ * Note that if initial threshold has been set, automatic generation of initial threshold is disabled.
+ *
+ * @param initialThreshold the initialThreshold to set
+ */
+ public void setInitialThreshold(double initialThreshold) {
+ this.initialThreshold = initialThreshold;
+ }
+
+ public void setEndThreshold(double endThreshold) {
+ this.endThreshold = endThreshold;
+ }
+
+ public void setMaxIterations(int maxIteration) {
+ this.maxIterations = maxIteration;
+ }
+
+
+ @Override
+ public void informAlgorithmStarts(VehicleRoutingProblem problem, VehicleRoutingAlgorithm algorithm, Collection solutions) {
+ reset();
+ this.maxIterations = algorithm.getMaxIterations();
+ }
+
+ private void reset() {
+ currentIteration = 0;
+ }
+
+ @Override
+ public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
+ currentIteration = i;
+ }
+
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/InsertionNoiseMaker.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/InsertionNoiseMaker.java
index 284910309..56160a36d 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/InsertionNoiseMaker.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/InsertionNoiseMaker.java
@@ -50,24 +50,16 @@ class InsertionNoiseMaker implements SoftActivityConstraint, IterationStartsList
this.noiseLevel = noiseLevel;
this.noiseProbability = noiseProbability;
this.maxCosts = maxCosts;
-// randomArray = new Random[vrp.getNuActivities() + 2];
-// for (int i = 0; i < randomArray.length; i++) {
-// Random r = new Random();
-// r.setSeed(random.nextLong());
-// randomArray[i] = r;
-// }
}
@Override
public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
- if (random.nextDouble() < noiseProbability) {
- makeNoise = true;
- } else makeNoise = false;
+
}
@Override
public double getCosts(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) {
- if (makeNoise) {
+ if (random.nextDouble() < noiseProbability) {
return noiseLevel * maxCosts * random.nextDouble();
}
return 0;
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 923863140..c0de26937 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 @@
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;
@@ -42,7 +43,6 @@
import com.graphhopper.jsprit.core.problem.vehicle.FiniteFleetManagerFactory;
import com.graphhopper.jsprit.core.problem.vehicle.InfiniteFleetManagerFactory;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleFleetManager;
-import com.graphhopper.jsprit.core.util.NoiseMaker;
import com.graphhopper.jsprit.core.util.RandomNumberGeneration;
import com.graphhopper.jsprit.core.util.Solutions;
@@ -54,6 +54,7 @@
public class Jsprit {
private final ActivityInsertionCostsCalculator activityInsertion;
+ private final JobFilter jobFilter;
public enum Construction {
@@ -82,7 +83,10 @@ public enum Strategy {
CLUSTER_BEST("cluster_best"),
CLUSTER_REGRET("cluster_regret"),
STRING_BEST("string_best"),
- STRING_REGRET("string_regret");
+ STRING_REGRET("string_regret"),
+ TIME_RELATED_REGRET("time_related_regret"),
+ TIME_RELATED_BEST("time_related_best");
+
String strategyName;
@@ -124,7 +128,10 @@ public enum Parameter {
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;
@@ -169,10 +176,19 @@ public static class Builder {
private ScoringFunction regretScorer = null;
+ private RegretScoringFunction regretScoringFunction = null;
+
private Map customStrategies = new HashMap<>();
private VehicleFleetManager fleetManager = null;
+ private JobFilter jobFilter = null;
+
+ private JobInsertionCostsCalculatorFactory serviceCalculatorFactory = null;
+
+ private JobInsertionCostsCalculatorFactory shipmentCalculatorFactory = null;
+
+
public static Builder newInstance(VehicleRoutingProblem vrp) {
return new Builder(vrp);
}
@@ -186,6 +202,10 @@ private Properties createDefaultProperties() {
Properties defaults = new Properties();
defaults.put(Strategy.RADIAL_BEST.toString(), "0.");
defaults.put(Strategy.RADIAL_REGRET.toString(), ".5");
+
+ defaults.put(Strategy.TIME_RELATED_BEST.toString(), "0.");
+ defaults.put(Strategy.TIME_RELATED_REGRET.toString(), "0.");
+
defaults.put(Strategy.RANDOM_BEST.toString(), ".5");
defaults.put(Strategy.RANDOM_REGRET.toString(), ".5");
@@ -199,6 +219,7 @@ private Properties createDefaultProperties() {
defaults.put(Strategy.WORST_BEST.toString(), "0.");
defaults.put(Strategy.WORST_REGRET.toString(), "1.");
+
defaults.put(Strategy.CLUSTER_BEST.toString(), "0.");
defaults.put(Strategy.CLUSTER_REGRET.toString(), "1.");
@@ -233,6 +254,9 @@ private Properties createDefaultProperties() {
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;
}
@@ -304,6 +328,32 @@ public Builder setRegretScorer(ScoringFunction scoringFunction) {
return this;
}
+ public Builder setRegretScoringFunction(RegretScoringFunction scoringFunction) {
+ this.regretScoringFunction = scoringFunction;
+ return this;
+ }
+
+ public Builder setJobFilter(JobFilter jobFilter) {
+ this.jobFilter = jobFilter;
+ return this;
+ }
+
+ /**
+ * Set a custom service insertion calculator factory
+ */
+ public Builder setServiceInsertionCalculatorFactory(JobInsertionCostsCalculatorFactory factory) {
+ this.serviceCalculatorFactory = factory;
+ return this;
+ }
+
+ /**
+ * Set a custom shipment insertion calculator factory
+ */
+ public Builder setShipmentInsertionCalculatorFactory(JobInsertionCostsCalculatorFactory factory) {
+ this.shipmentCalculatorFactory = factory;
+ return this;
+ }
+
public VehicleRoutingAlgorithm buildAlgorithm() {
return new Jsprit(this).create(vrp);
}
@@ -346,11 +396,11 @@ public int createNumberToBeRemoved() {
}
- private StateManager stateManager = null;
+ private StateManager stateManager;
- private ConstraintManager constraintManager = null;
+ private ConstraintManager constraintManager;
- private ExecutorService es = null;
+ private ExecutorService es;
private Integer noThreads;
@@ -358,7 +408,7 @@ public int createNumberToBeRemoved() {
private boolean addCoreConstraints;
- private SolutionCostCalculator objectiveFunction = null;
+ private SolutionCostCalculator objectiveFunction;
private Properties properties;
@@ -368,8 +418,14 @@ public int createNumberToBeRemoved() {
private ScoringFunction regretScorer;
+ private RegretScoringFunction regretScoringFunction;
+
private final Map customStrategies = new HashMap<>();
+ private final JobInsertionCostsCalculatorFactory serviceCalculatorFactory;
+
+ private final JobInsertionCostsCalculatorFactory shipmentCalculatorFactory;
+
private VehicleFleetManager vehicleFleetManager;
private Jsprit(Builder builder) {
@@ -383,23 +439,32 @@ private Jsprit(Builder builder) {
this.random = builder.random;
this.activityInsertion = builder.activityInsertionCalculator;
this.acceptor = builder.solutionAcceptor;
+ this.jobFilter = builder.jobFilter;
+ this.shipmentCalculatorFactory = builder.shipmentCalculatorFactory;
+ this.serviceCalculatorFactory = builder.serviceCalculatorFactory;
regretScorer = builder.regretScorer;
+ regretScoringFunction = builder.regretScoringFunction;
customStrategies.putAll(builder.customStrategies);
vehicleFleetManager = builder.fleetManager;
}
private void ini(VehicleRoutingProblem vrp) {
- if (regretScorer == null) regretScorer = getRegretScorer(vrp);
+ if (regretScorer == null) {
+ regretScorer = getRegretScorer(vrp);
+ }
+ if (regretScoringFunction == null) {
+ regretScoringFunction = new DefaultRegretScoringFunction(regretScorer);
+ }
}
private VehicleRoutingAlgorithm create(final VehicleRoutingProblem vrp) {
ini(vrp);
+ boolean isInfinite = vrp.getFleetSize().equals(VehicleRoutingProblem.FleetSize.INFINITE);
if (vehicleFleetManager == null) {
- if (vrp.getFleetSize().equals(VehicleRoutingProblem.FleetSize.INFINITE)) {
+ if (isInfinite) {
vehicleFleetManager = new InfiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager();
} else {
FiniteFleetManagerFactory finiteFleetManagerFactory = new FiniteFleetManagerFactory(vrp.getVehicles());
- finiteFleetManagerFactory.setRandom(random);
vehicleFleetManager = finiteFleetManagerFactory.createFleetManager();
}
}
@@ -436,76 +501,75 @@ private VehicleRoutingAlgorithm create(final VehicleRoutingProblem vrp) {
jobNeighborhoods.initialise();
final double maxCosts;
- if(properties.containsKey(Parameter.MAX_TRANSPORT_COSTS.toString())){
+ if (properties.containsKey(Parameter.MAX_TRANSPORT_COSTS.toString())) {
maxCosts = Double.parseDouble(getProperty(Parameter.MAX_TRANSPORT_COSTS.toString()));
- }
- else{
+ } else {
maxCosts = jobNeighborhoods.getMaxDistance();
}
- IterationStartsListener noiseConfigurator;
- if (noThreads > 1) {
- ConcurrentInsertionNoiseMaker noiseMaker = new ConcurrentInsertionNoiseMaker(vrp, maxCosts, noiseLevel, noiseProbability);
- noiseMaker.setRandom(random);
- constraintManager.addConstraint(noiseMaker);
- noiseConfigurator = noiseMaker;
- } else {
- InsertionNoiseMaker noiseMaker = new InsertionNoiseMaker(vrp, maxCosts, noiseLevel, noiseProbability);
- noiseMaker.setRandom(random);
- constraintManager.addConstraint(noiseMaker);
- noiseConfigurator = noiseMaker;
+ IterationStartsListener noiseConfigurator = null;
+ if (noiseProbability > 0) {
+ if (noThreads > 1) {
+ ConcurrentInsertionNoiseMaker noiseMaker = new ConcurrentInsertionNoiseMaker(vrp, maxCosts, noiseLevel, noiseProbability);
+ noiseMaker.setRandom(random);
+ constraintManager.addConstraint(noiseMaker);
+ noiseConfigurator = noiseMaker;
+ } else {
+ InsertionNoiseMaker noiseMaker = new InsertionNoiseMaker(vrp, maxCosts, noiseLevel, noiseProbability);
+ noiseMaker.setRandom(random);
+ constraintManager.addConstraint(noiseMaker);
+ noiseConfigurator = noiseMaker;
+ }
}
RuinRadial radial = new RuinRadial(vrp, vrp.getJobs().size(), jobNeighborhoods);
radial.setRandom(random);
- radial.setRuinShareFactory(new RuinShareFactoryImpl(
- toInteger(properties.getProperty(Parameter.RADIAL_MIN_SHARE.toString())),
- toInteger(properties.getProperty(Parameter.RADIAL_MAX_SHARE.toString())),
- random)
- );
-
- final RuinRandom random_for_regret = new RuinRandom(vrp, 0.5);
- random_for_regret.setRandom(random);
- random_for_regret.setRuinShareFactory(new RuinShareFactoryImpl(
- toInteger(properties.getProperty(Parameter.RANDOM_REGRET_MIN_SHARE.toString())),
- toInteger(properties.getProperty(Parameter.RANDOM_REGRET_MAX_SHARE.toString())),
- random)
+ radial.setJobFilter(jobFilter);
+ RuinShareFactoryImpl radialRuinFactory = new RuinShareFactoryImpl(
+ toInteger(properties.getProperty(Parameter.RADIAL_MIN_SHARE.toString())),
+ toInteger(properties.getProperty(Parameter.RADIAL_MAX_SHARE.toString())),
+ random);
+ radial.setRuinShareFactory(radialRuinFactory);
+
+ final RuinRandom randomForRegret = new RuinRandom(vrp, 0.5);
+ randomForRegret.setJobFilter(jobFilter);
+ randomForRegret.setRandom(random);
+ randomForRegret.setRuinShareFactory(new RuinShareFactoryImpl(
+ toInteger(properties.getProperty(Parameter.RANDOM_REGRET_MIN_SHARE.toString())),
+ toInteger(properties.getProperty(Parameter.RANDOM_REGRET_MAX_SHARE.toString())),
+ random)
);
- final RuinRandom random_for_best = new RuinRandom(vrp, 0.5);
- random_for_best.setRandom(random);
- random_for_best.setRuinShareFactory(new RuinShareFactoryImpl(
+ final RuinRandom randomForBest = new RuinRandom(vrp, 0.5);
+ randomForBest.setRandom(random);
+ randomForBest.setJobFilter(jobFilter);
+ randomForBest.setRuinShareFactory(new RuinShareFactoryImpl(
toInteger(properties.getProperty(Parameter.RANDOM_BEST_MIN_SHARE.toString())),
toInteger(properties.getProperty(Parameter.RANDOM_BEST_MAX_SHARE.toString())),
- random)
+ random)
);
final RuinWorst worst = new RuinWorst(vrp, (int) (vrp.getJobs().values().size() * 0.5));
worst.setRandom(random);
+ worst.setJobFilter(jobFilter);
worst.setRuinShareFactory(new RuinShareFactoryImpl(
- toInteger(properties.getProperty(Parameter.WORST_MIN_SHARE.toString())),
- toInteger(properties.getProperty(Parameter.WORST_MAX_SHARE.toString())),
- random)
+ toInteger(properties.getProperty(Parameter.WORST_MIN_SHARE.toString())),
+ toInteger(properties.getProperty(Parameter.WORST_MAX_SHARE.toString())),
+ random)
);
- IterationStartsListener noise = new IterationStartsListener() {
- @Override
- public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
- worst.setNoiseMaker(new NoiseMaker() {
-
- public double makeNoise() {
- if (random.nextDouble() < toDouble(getProperty(Parameter.RUIN_WORST_NOISE_PROB.toString()))) {
- return toDouble(getProperty(Parameter.RUIN_WORST_NOISE_LEVEL.toString()))
- * maxCosts * random.nextDouble();
- } else return 0.;
- }
- });
- }
- };
+ double ruinWorstNoiseProb = toDouble(getProperty(Parameter.RUIN_WORST_NOISE_PROB.toString()));
+ double ruinWorstNoiseLevel = toDouble(getProperty(Parameter.RUIN_WORST_NOISE_LEVEL.toString()));
+ IterationStartsListener noise = (i, problem, solutions) -> worst.setNoiseMaker(() -> {
+ if (random.nextDouble() < ruinWorstNoiseProb) {
+ return ruinWorstNoiseLevel * maxCosts * random.nextDouble();
+ } else return 0.;
+ });
final RuinClusters clusters = new RuinClusters(vrp, (int) (vrp.getJobs().values().size() * 0.5), jobNeighborhoods);
clusters.setRandom(random);
+ clusters.setJobFilter(jobFilter);
clusters.setRuinShareFactory(new RuinShareFactoryImpl(
- toInteger(properties.getProperty(Parameter.WORST_MIN_SHARE.toString())),
+ toInteger(properties.getProperty(Parameter.WORST_MIN_SHARE.toString())),
toInteger(properties.getProperty(Parameter.WORST_MAX_SHARE.toString())),
random)
);
@@ -519,13 +583,18 @@ public double makeNoise() {
stringRuin.setNoRoutes(kMin, kMax);
stringRuin.setStringLength(lMin, lMax);
stringRuin.setRandom(random);
+ stringRuin.setJobFilter(jobFilter);
+
+ final RuinTimeRelated ruinTimeRelated = new RuinTimeRelated(vrp);
+ ruinTimeRelated.setRuinShareFactory(radialRuinFactory);
+ ruinTimeRelated.setRandom(random);
+ ruinTimeRelated.setJobFilter(jobFilter);
AbstractInsertionStrategy regret;
- final ScoringFunction scorer;
boolean fastRegret = Boolean.parseBoolean(getProperty(Parameter.FAST_REGRET.toString()));
if (es != null) {
- if(fastRegret){
+ if (fastRegret) {
RegretInsertionConcurrentFast regretInsertion = (RegretInsertionConcurrentFast) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager)
.setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET)
.setConcurrentMode(es, noThreads)
@@ -533,9 +602,10 @@ public double makeNoise() {
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.setActivityInsertionCostCalculator(activityInsertion)
+ .setServiceInsertionCalculator(this.serviceCalculatorFactory)
+ .setShipmentInsertionCalculatorFactory(this.shipmentCalculatorFactory)
.build();
- scorer = regretScorer;
- regretInsertion.setScoringFunction(scorer);
+ regretInsertion.setRegretScoringFunction(regretScoringFunction);
regretInsertion.setDependencyTypes(constraintManager.getDependencyTypes());
regret = regretInsertion;
}
@@ -546,9 +616,10 @@ public double makeNoise() {
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.setActivityInsertionCostCalculator(activityInsertion)
+ .setServiceInsertionCalculator(this.serviceCalculatorFactory)
+ .setShipmentInsertionCalculatorFactory(this.shipmentCalculatorFactory)
.build();
- scorer = regretScorer;
- regretInsertion.setScoringFunction(scorer);
+ regretInsertion.setRegretScoringFunction(regretScoringFunction);
regret = regretInsertion;
}
} else {
@@ -559,9 +630,10 @@ public double makeNoise() {
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setActivityInsertionCostCalculator(activityInsertion)
+ .setServiceInsertionCalculator(this.serviceCalculatorFactory)
+ .setShipmentInsertionCalculatorFactory(this.shipmentCalculatorFactory)
.build();
- scorer = regretScorer;
- regretInsertion.setScoringFunction(scorer);
+ regretInsertion.setRegretScoringFunction(regretScoringFunction);
regretInsertion.setDependencyTypes(constraintManager.getDependencyTypes());
regret = regretInsertion;
}
@@ -571,21 +643,24 @@ public double makeNoise() {
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString())))
.setActivityInsertionCostCalculator(activityInsertion)
- .build();
- scorer = regretScorer;
- regretInsertion.setScoringFunction(scorer);
+ .setServiceInsertionCalculator(this.serviceCalculatorFactory)
+ .setShipmentInsertionCalculatorFactory(this.shipmentCalculatorFactory)
+ .build();
+ regretInsertion.setRegretScoringFunction(regretScoringFunction);
regret = regretInsertion;
}
}
regret.setRandom(random);
AbstractInsertionStrategy best;
- if (vrp.getJobs().size() < 250 || es == null) {
+ if ((vrp.getVehicles().size() == 1 && !isInfinite) || vrp.getJobs().size() < 100 || es == null) {
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)
+ .setServiceInsertionCalculator(this.serviceCalculatorFactory)
+ .setShipmentInsertionCalculatorFactory(this.shipmentCalculatorFactory)
.build();
best = bestInsertion;
} else {
@@ -595,6 +670,8 @@ public double makeNoise() {
.setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString())))
.setConcurrentMode(es, noThreads)
.setActivityInsertionCostCalculator(activityInsertion)
+ .setServiceInsertionCalculator(this.serviceCalculatorFactory)
+ .setShipmentInsertionCalculatorFactory(this.shipmentCalculatorFactory)
.build();
best = bestInsertion;
}
@@ -606,13 +683,10 @@ public double makeNoise() {
if (properties.containsKey(Parameter.THRESHOLD_INI_ABS.toString())) {
schrimpfAcceptance.setInitialThreshold(Double.valueOf(properties.getProperty(Parameter.THRESHOLD_INI_ABS.toString())));
} else {
- schrimpfThreshold = new IterationStartsListener() {
- @Override
- public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) {
- if (i == 1) {
- double initialThreshold = Solutions.bestOf(solutions).getCost() * toDouble(getProperty(Parameter.THRESHOLD_INI.toString()));
- schrimpfAcceptance.setInitialThreshold(initialThreshold);
- }
+ schrimpfThreshold = (i, problem, solutions) -> {
+ if (i == 1) {
+ double initialThreshold = Solutions.bestOf(solutions).getCost() * toDouble(getProperty(Parameter.THRESHOLD_INI.toString()));
+ schrimpfAcceptance.setInitialThreshold(initialThreshold);
}
};
}
@@ -620,49 +694,59 @@ public void informIterationStarts(int i, VehicleRoutingProblem problem, Collecti
}
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));
+ SearchStrategy radialRegret = new SearchStrategy(Strategy.RADIAL_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction);
+ radialRegret.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));
+ SearchStrategy radialBest = new SearchStrategy(Strategy.RADIAL_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
+ radialBest.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));
+ SearchStrategy timeRelatedRegret = new SearchStrategy(Strategy.TIME_RELATED_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction);
+ timeRelatedRegret.addModule(configureModule(new RuinAndRecreateModule(Strategy.TIME_RELATED_REGRET.toString(), regret, ruinTimeRelated)));
- 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));
+ SearchStrategy timeRelatedBest = new SearchStrategy(Strategy.TIME_RELATED_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
+ timeRelatedBest.addModule(configureModule(new RuinAndRecreateModule(Strategy.TIME_RELATED_BEST.toString(), best, ruinTimeRelated)));
- SearchStrategy worst_regret = new SearchStrategy(Strategy.WORST_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction);
- worst_regret.addModule(new RuinAndRecreateModule(Strategy.WORST_REGRET.toString(), regret, worst));
+ SearchStrategy randomBest = new SearchStrategy(Strategy.RANDOM_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
+ randomBest.addModule(configureModule(new RuinAndRecreateModule(Strategy.RANDOM_BEST.toString(), best, randomForBest)));
- SearchStrategy worst_best = new SearchStrategy(Strategy.WORST_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
- worst_best.addModule(new RuinAndRecreateModule(Strategy.WORST_BEST.toString(), best, worst));
+ SearchStrategy randomRegret = new SearchStrategy(Strategy.RANDOM_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction);
+ randomRegret.addModule(configureModule(new RuinAndRecreateModule(Strategy.RANDOM_REGRET.toString(), regret, randomForRegret)));
- 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));
+ SearchStrategy worstRegret = new SearchStrategy(Strategy.WORST_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction);
+ worstRegret.addModule(configureModule(new RuinAndRecreateModule(Strategy.WORST_REGRET.toString(), regret, worst)));
- 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));
+ SearchStrategy worstBest = new SearchStrategy(Strategy.WORST_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
+ worstBest.addModule(configureModule(new RuinAndRecreateModule(Strategy.WORST_BEST.toString(), best, worst)));
+
+ final SearchStrategy clustersRegret = new SearchStrategy(Strategy.CLUSTER_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction);
+ clustersRegret.addModule(configureModule(new RuinAndRecreateModule(Strategy.CLUSTER_REGRET.toString(), regret, clusters)));
+
+ final SearchStrategy clustersBest = new SearchStrategy(Strategy.CLUSTER_BEST.toString(), new SelectBest(), acceptor, objectiveFunction);
+ clustersBest.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);
if (addCoreConstraints) {
prettyBuilder.addCoreStateAndConstraintStuff();
}
- prettyBuilder.withStrategy(radial_regret, toDouble(getProperty(Strategy.RADIAL_REGRET.toString())))
- .withStrategy(radial_best, toDouble(getProperty(Strategy.RADIAL_BEST.toString())))
- .withStrategy(random_best, toDouble(getProperty(Strategy.RANDOM_BEST.toString())))
- .withStrategy(random_regret, toDouble(getProperty(Strategy.RANDOM_REGRET.toString())))
- .withStrategy(worst_best, toDouble(getProperty(Strategy.WORST_BEST.toString())))
- .withStrategy(worst_regret, toDouble(getProperty(Strategy.WORST_REGRET.toString())))
- .withStrategy(clusters_regret, toDouble(getProperty(Strategy.CLUSTER_REGRET.toString())))
- .withStrategy(clusters_best, toDouble(getProperty(Strategy.CLUSTER_BEST.toString())))
+ prettyBuilder.withStrategy(radialRegret, toDouble(getProperty(Strategy.RADIAL_REGRET.toString())))
+ .withStrategy(radialBest, toDouble(getProperty(Strategy.RADIAL_BEST.toString())))
+
+ .withStrategy(timeRelatedBest, toDouble(getProperty(Strategy.TIME_RELATED_BEST.toString())))
+ .withStrategy(timeRelatedRegret, toDouble(getProperty(Strategy.TIME_RELATED_REGRET.toString())))
+
+ .withStrategy(randomBest, toDouble(getProperty(Strategy.RANDOM_BEST.toString())))
+ .withStrategy(randomRegret, toDouble(getProperty(Strategy.RANDOM_REGRET.toString())))
+ .withStrategy(worstBest, toDouble(getProperty(Strategy.WORST_BEST.toString())))
+ .withStrategy(worstRegret, toDouble(getProperty(Strategy.WORST_REGRET.toString())))
+ .withStrategy(clustersRegret, toDouble(getProperty(Strategy.CLUSTER_REGRET.toString())))
+ .withStrategy(clustersBest, toDouble(getProperty(Strategy.CLUSTER_BEST.toString())))
.withStrategy(stringBest, toDouble(getProperty(Strategy.STRING_BEST.toString())))
.withStrategy(stringRegret, toDouble(getProperty(Strategy.STRING_REGRET.toString())));
@@ -679,10 +763,10 @@ public void informIterationStarts(int i, VehicleRoutingProblem problem, Collecti
VehicleRoutingAlgorithm vra = prettyBuilder.build();
- if(schrimpfThreshold != null) {
+ if (schrimpfThreshold != null) {
vra.addListener(schrimpfThreshold);
}
- vra.addListener(noiseConfigurator);
+ if (noiseConfigurator != null) vra.addListener(noiseConfigurator);
vra.addListener(noise);
vra.addListener(clusters);
if (increasingAbsoluteFixedCosts != null) vra.addListener(increasingAbsoluteFixedCosts);
@@ -691,12 +775,19 @@ public void informIterationStarts(int i, VehicleRoutingProblem problem, Collecti
vra.addListener(new BreakScheduling(vrp, stateManager, constraintManager));
}
handleExecutorShutdown(vra);
- vra.setMaxIterations(Integer.valueOf(properties.getProperty(Parameter.ITERATIONS.toString())));
+ vra.setMaxIterations(Integer.parseInt(properties.getProperty(Parameter.ITERATIONS.toString())));
return vra;
}
+ 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 9c6e85cc4..65d72d3fb 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
@@ -25,10 +25,9 @@
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 com.graphhopper.jsprit.core.util.RandomNumberGeneration;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
public class RuinAndRecreateModule implements SearchStrategyModule {
@@ -39,6 +38,12 @@ public class RuinAndRecreateModule implements SearchStrategyModule {
private String moduleName;
+ private Random random = RandomNumberGeneration.newInstance();
+
+ private int minUnassignedJobsToBeReinserted = Integer.MAX_VALUE;
+
+ private double proportionOfUnassignedJobsToBeReinserted = 1d;
+
public RuinAndRecreateModule(String moduleName, InsertionStrategy insertion, RuinStrategy ruin) {
super();
this.insertion = insertion;
@@ -46,16 +51,62 @@ public RuinAndRecreateModule(String moduleName, InsertionStrategy insertion, Rui
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();
- ruinedJobSet.addAll(ruinedJobs);
- ruinedJobSet.addAll(vrpSolution.getUnassignedJobs());
- Collection unassignedJobs = insertion.insertJobs(vrpSolution.getRoutes(), ruinedJobSet);
- vrpSolution.getUnassignedJobs().clear();
- vrpSolution.getUnassignedJobs().addAll(unassignedJobs);
- return vrpSolution;
+ public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution previousVrpSolution) {
+ Collection ruinedJobs = ruin.ruin(previousVrpSolution.getRoutes());
+ Set ruinedJobSet = new HashSet<>(ruinedJobs);
+ 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));
+ }
+ }
+ // Taking the ruinedJobSet and ordering it because the order coming out of the HashSet
+ // is not guaranteed. This can cause slight deviations in the selections of an insertion
+ // and result in different solutions.
+ // Additionally, ArrayList is faster for iteration than iterating over a HashSet
+ List orderedRuinedJobs = new ArrayList<>(ruinedJobSet);
+ orderedRuinedJobs.sort(Comparator.comparing(Job::getId));
+ Collection unassignedJobs = insertion.insertJobs(previousVrpSolution.getRoutes(), orderedRuinedJobs);
+ previousVrpSolution.getUnassignedJobs().clear();
+ previousVrpSolution.getUnassignedJobs().addAll(unassignedJobs);
+ previousVrpSolution.getUnassignedJobs().addAll(stillUnassignedInThisIteration);
+ return previousVrpSolution;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java
index 6f3d54a44..2fbff399d 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java
@@ -1,40 +1,31 @@
-/*
- * 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.constraint.ConstraintManager;
import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint;
import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus;
+import com.graphhopper.jsprit.core.problem.constraint.HardConstraint;
import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint;
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.TourActivity;
+import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
- * Created by schroeder on 06/02/17.
+ * Enhanced AbstractInsertionCalculator with more common functionality moved up
+ * to simplify implementation of concrete calculators.
*/
-abstract class AbstractInsertionCalculator implements JobInsertionCostsCalculator {
+public abstract class AbstractInsertionCalculator implements JobInsertionCostsCalculator {
- InsertionData checkRouteContraints(JobInsertionContext insertionContext, ConstraintManager constraintManager) {
+ /**
+ * Check if route constraints are fulfilled
+ */
+ protected InsertionData checkRouteConstraints(JobInsertionContext insertionContext, ConstraintManager constraintManager) {
for (HardRouteConstraint hardRouteConstraint : constraintManager.getHardRouteConstraints()) {
if (!hardRouteConstraint.fulfilled(insertionContext)) {
InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
@@ -45,17 +36,21 @@ InsertionData checkRouteContraints(JobInsertionContext insertionContext, Constra
return null;
}
- ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime, Collection failedActivityConstraints, ConstraintManager constraintManager) {
+ /**
+ * Check if activity constraints are fulfilled
+ */
+ protected ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime, Collection failedActivityConstraints, ConstraintManager constraintManager) {
+ if (!constraintManager.hasHardActivityConstraints()) return ConstraintsStatus.FULFILLED;
ConstraintsStatus notFulfilled = null;
- List failed = new ArrayList<>();
+ List failed = new ArrayList<>();
for (HardActivityConstraint c : constraintManager.getCriticalHardActivityConstraints()) {
ConstraintsStatus status = c.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime);
if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) {
- failedActivityConstraints.add(c.getClass().getSimpleName());
+ failedActivityConstraints.add(c);
return status;
} else {
if (status.equals(ConstraintsStatus.NOT_FULFILLED)) {
- failed.add(c.getClass().getSimpleName());
+ failed.add(c);
notFulfilled = status;
}
}
@@ -68,11 +63,11 @@ ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, To
for (HardActivityConstraint c : constraintManager.getHighPrioHardActivityConstraints()) {
ConstraintsStatus status = c.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime);
if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) {
- failedActivityConstraints.add(c.getClass().getSimpleName());
+ failedActivityConstraints.add(c);
return status;
} else {
if (status.equals(ConstraintsStatus.NOT_FULFILLED)) {
- failed.add(c.getClass().getSimpleName());
+ failed.add(c);
notFulfilled = status;
}
}
@@ -85,11 +80,60 @@ ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, To
for (HardActivityConstraint constraint : constraintManager.getLowPrioHardActivityConstraints()) {
ConstraintsStatus status = constraint.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime);
if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK) || status.equals(ConstraintsStatus.NOT_FULFILLED)) {
- failedActivityConstraints.add(constraint.getClass().getSimpleName());
+ failedActivityConstraints.add(constraint);
return status;
}
}
return ConstraintsStatus.FULFILLED;
}
-}
+ /**
+ * Creates a Start activity for a vehicle at a given departure time
+ */
+ protected Start createStartActivity(Vehicle vehicle, double departureTime) {
+ Start start = new Start(vehicle.getStartLocation(), vehicle.getEarliestDeparture(), vehicle.getLatestArrival());
+ start.setEndTime(departureTime);
+ return start;
+ }
+
+ /**
+ * Creates an End activity for a vehicle
+ */
+ protected End createEndActivity(Vehicle vehicle) {
+ return new End(vehicle.getEndLocation(), 0.0, vehicle.getLatestArrival());
+ }
+
+ /**
+ * Creates a NoInsertionFound result with failed constraint information
+ */
+ protected InsertionData createNoInsertionFoundResult(Collection failedConstraints) {
+ InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
+ for (HardConstraint failed : failedConstraints) {
+ emptyInsertionData.addFailedConstrainName(failed.getClass().getSimpleName());
+ }
+ return emptyInsertionData;
+ }
+
+ /**
+ * Adds events to insertion data for a job that requires two activities (like Shipment)
+ */
+ protected void addActivitiesAndVehicleSwitch(InsertionData insertionData, VehicleRoute route,
+ Vehicle vehicle, TourActivity firstActivity, int firstActivityIndex,
+ TourActivity secondActivity, int secondActivityIndex,
+ double departureTime) {
+ // Order matters here - we need to insert second activity before first to maintain indices
+ insertionData.getEvents().add(new InsertActivity(route, vehicle, secondActivity, secondActivityIndex));
+ insertionData.getEvents().add(new InsertActivity(route, vehicle, firstActivity, firstActivityIndex));
+ insertionData.getEvents().add(new SwitchVehicle(route, vehicle, departureTime));
+ }
+
+ /**
+ * Adds events to insertion data for a job that requires a single activity (like Service)
+ */
+ protected void addActivityAndVehicleSwitch(InsertionData insertionData, VehicleRoute route,
+ Vehicle vehicle, TourActivity activity, int activityIndex,
+ double departureTime) {
+ insertionData.getEvents().add(new InsertActivity(route, vehicle, activity, activityIndex));
+ insertionData.getEvents().add(new SwitchVehicle(route, vehicle, departureTime));
+ }
+}
\ No newline at end of file
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java
index 5f5973f5a..d6fd3e1bc 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionStrategy.java
@@ -89,7 +89,7 @@ public void setRandom(Random random) {
public Collection insertJobs(Collection vehicleRoutes, Collection unassignedJobs) {
insertionsListeners.informInsertionStarts(vehicleRoutes, unassignedJobs);
Collection badJobs = insertUnassignedJobs(vehicleRoutes, unassignedJobs);
- insertionsListeners.informInsertionEndsListeners(vehicleRoutes);
+ insertionsListeners.informInsertionEndsListeners(vehicleRoutes, badJobs);
return badJobs;
}
@@ -124,7 +124,7 @@ protected void insertJob(Job unassignedJob, InsertionData iData, VehicleRoute in
for (Event e : iData.getEvents()) {
eventListeners.inform(e);
}
- insertionsListeners.informJobInserted(unassignedJob, inRoute, iData.getInsertionCost(), iData.getAdditionalTime());
+ insertionsListeners.informJobInserted(unassignedJob, inRoute, iData);
}
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AdditionalAccessEgressCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AdditionalAccessEgressCalculator.java
index 9a938e6d2..affc72fa5 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AdditionalAccessEgressCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AdditionalAccessEgressCalculator.java
@@ -47,6 +47,8 @@ public AdditionalAccessEgressCalculator(VehicleRoutingTransportCosts routingCost
}
public double getCosts(JobInsertionContext insertionContext) {
+ if (insertionContext.getRoute().isEmpty()) return 0.0;
+ if (insertionContext.getRoute().getVehicle() == insertionContext.getNewVehicle()) return 0.0;
double delta_access = 0.0;
double delta_egress = 0.0;
VehicleRoute currentRoute = insertionContext.getRoute();
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java
index 576e41f48..0524ffead 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertion.java
@@ -37,18 +37,11 @@
*/
public final class BestInsertion extends AbstractInsertionStrategy {
- private static Logger logger = LoggerFactory.getLogger(BestInsertion.class);
+ final private static Logger logger = LoggerFactory.getLogger(BestInsertion.class);
private JobInsertionCostsCalculator bestInsertionCostCalculator;
- private NoiseMaker noiseMaker = new NoiseMaker() {
-
- @Override
- public double makeNoise() {
- return 0;
- }
-
- };
+ private NoiseMaker noiseMaker = () -> 0;
public BestInsertion(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem) {
super(vehicleRoutingProblem);
@@ -63,10 +56,10 @@ public String toString() {
@Override
public Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs) {
- List badJobs = new ArrayList(unassignedJobs.size());
- List unassignedJobList = new ArrayList(unassignedJobs);
+ List badJobs = new ArrayList<>(unassignedJobs.size());
+ List unassignedJobList = new ArrayList<>(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
- Collections.sort(unassignedJobList, new AccordingToPriorities());
+ unassignedJobList.sort(new AccordingToPriorities());
for (Job unassignedJob : unassignedJobList) {
Insertion bestInsertion = null;
InsertionData empty = new InsertionData.NoInsertionFound();
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java
index c7e6ae945..91441d3b9 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BestInsertionConcurrent.java
@@ -18,7 +18,6 @@
package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.algorithm.recreate.InsertionData.NoInsertionFound;
-import com.graphhopper.jsprit.core.algorithm.recreate.listener.InsertionListeners;
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.driver.Driver;
import com.graphhopper.jsprit.core.problem.job.Job;
@@ -31,7 +30,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
/**
@@ -40,12 +42,7 @@
public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
- static class Batch {
- List routes = new ArrayList();
-
- }
-
- class Insertion {
+ static class Insertion {
private final VehicleRoute route;
@@ -67,7 +64,7 @@ public InsertionData getInsertionData() {
}
- private static Logger logger = LoggerFactory.getLogger(BestInsertionConcurrent.class);
+ private final static Logger logger = LoggerFactory.getLogger(BestInsertionConcurrent.class);
private final static double NO_NEW_DEPARTURE_TIME_YET = -12345.12345;
@@ -75,20 +72,14 @@ public InsertionData getInsertionData() {
private final static Driver NO_NEW_DRIVER_YET = null;
- private InsertionListeners insertionsListeners;
-
- private JobInsertionCostsCalculator bestInsertionCostCalculator;
+ private final JobInsertionCostsCalculator bestInsertionCostCalculator;
- private int nuOfBatches;
-
- private ExecutorCompletionService completionService;
+ private final ExecutorService executorService;
public BestInsertionConcurrent(JobInsertionCostsCalculator jobInsertionCalculator, ExecutorService executorService, int nuOfBatches, VehicleRoutingProblem vehicleRoutingProblem) {
super(vehicleRoutingProblem);
- this.insertionsListeners = new InsertionListeners();
- this.nuOfBatches = nuOfBatches;
bestInsertionCostCalculator = jobInsertionCalculator;
- completionService = new ExecutorCompletionService(executorService);
+ this.executorService = executorService;
logger.debug("initialise {}", this);
}
@@ -99,29 +90,22 @@ public String toString() {
@Override
public Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs) {
- List badJobs = new ArrayList(unassignedJobs.size());
- List unassignedJobList = new ArrayList(unassignedJobs);
+ List badJobs = new ArrayList<>(unassignedJobs.size());
+ List unassignedJobList = new ArrayList<>(unassignedJobs);
Collections.shuffle(unassignedJobList, random);
- Collections.sort(unassignedJobList, new AccordingToPriorities());
- List batches = distributeRoutes(vehicleRoutes, nuOfBatches);
+ unassignedJobList.sort(new AccordingToPriorities());
List failedConstraintNames = new ArrayList<>();
for (final Job unassignedJob : unassignedJobList) {
Insertion bestInsertion = null;
double bestInsertionCost = Double.MAX_VALUE;
- for (final Batch batch : batches) {
- completionService.submit(new Callable() {
-
- @Override
- public Insertion call() throws Exception {
- return getBestInsertion(batch, unassignedJob);
- }
-
- });
+ List> tasks = new ArrayList<>(vehicleRoutes.size());
+ for (VehicleRoute route : vehicleRoutes) {
+ tasks.add(() -> getBestInsertion(route, unassignedJob));
}
try {
- for (int i = 0; i < batches.size(); i++) {
- Future futureIData = completionService.take();
- Insertion insertion = futureIData.get();
+ List> futureResponses = executorService.invokeAll(tasks);
+ for (int i = 0; i < vehicleRoutes.size(); i++) {
+ Insertion insertion = futureResponses.get(i).get();
if (insertion.insertionData instanceof NoInsertionFound) {
failedConstraintNames.addAll(insertion.getInsertionData().getFailedConstraintNames());
continue;
@@ -136,12 +120,12 @@ public Insertion call() throws Exception {
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
+
VehicleRoute newRoute = VehicleRoute.emptyRoute();
InsertionData newIData = bestInsertionCostCalculator.getInsertionData(newRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost);
if (newIData.getInsertionCost() < bestInsertionCost) {
bestInsertion = new Insertion(newRoute, newIData);
vehicleRoutes.add(newRoute);
- batches.get(random.nextInt(batches.size())).routes.add(newRoute);
}
if (bestInsertion == null) {
badJobs.add(unassignedJob);
@@ -153,50 +137,15 @@ public Insertion call() throws Exception {
}
- private Insertion getBestInsertion(Batch batch, Job unassignedJob) {
- Insertion bestInsertion = null;
+ private Insertion getBestInsertion(VehicleRoute vehicleRoute, Job unassignedJob) {
InsertionData empty = new InsertionData.NoInsertionFound();
- double bestInsertionCost = Double.MAX_VALUE;
- for (VehicleRoute vehicleRoute : batch.routes) {
- InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost);
- if (iData instanceof NoInsertionFound) {
- empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames());
- continue;
- }
- if (iData.getInsertionCost() < bestInsertionCost) {
- bestInsertion = new Insertion(vehicleRoute, iData);
- bestInsertionCost = iData.getInsertionCost();
- }
- }
- if (bestInsertion == null) return new Insertion(null, empty);
- return bestInsertion;
- }
-
- private List distributeRoutes(Collection vehicleRoutes, int nuOfBatches) {
- List batches = new ArrayList();
- for (int i = 0; i < nuOfBatches; i++) batches.add(new Batch());
- /*
- * if route.size < nuOfBatches add as much routes as empty batches are available
- * else add one empty route anyway
- */
- if (vehicleRoutes.size() < nuOfBatches) {
- int nOfNewRoutes = nuOfBatches - vehicleRoutes.size();
- for (int i = 0; i < nOfNewRoutes; i++) {
- vehicleRoutes.add(VehicleRoute.emptyRoute());
- }
+ InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE);
+ if (iData instanceof NoInsertionFound) {
+ empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames());
+ return new Insertion(null, empty);
} else {
- vehicleRoutes.add(VehicleRoute.emptyRoute());
- }
- /*
- * distribute routes to batches equally
- */
- int count = 0;
- for (VehicleRoute route : vehicleRoutes) {
- if (count == nuOfBatches) count = 0;
- batches.get(count).routes.add(route);
- count++;
+ return new Insertion(vehicleRoute, iData);
}
- return batches;
}
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 66fbb182c..c283cdf31 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
@@ -30,7 +30,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
/**
* Created by schroeder on 07/04/16.
@@ -45,8 +47,6 @@ public class BreakScheduling implements InsertionStartsListener,JobInsertedListe
private final EventListeners eventListeners;
- private Set modifiedRoutes = new HashSet();
-
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, vrp.getJobActivityFactory());
@@ -54,23 +54,23 @@ public BreakScheduling(VehicleRoutingProblem vrp, StateManager stateManager, Con
}
@Override
- public void informJobInserted(Job job2insert, VehicleRoute inRoute, double additionalCosts, double additionalTime) {
+ public void informJobInserted(Job job2insert, VehicleRoute inRoute, InsertionData insertionData) {
Break aBreak = inRoute.getVehicle().getBreak();
- if(aBreak != null){
+ if (aBreak != null) {
boolean removed = inRoute.getTourActivities().removeJob(aBreak);
- if(removed){
+ if (removed) {
logger.trace("ruin: {}", aBreak.getId());
- stateManager.removed(aBreak,inRoute);
+ stateManager.removed(aBreak, inRoute);
stateManager.reCalculateStates(inRoute);
}
- if(inRoute.getEnd().getArrTime() > aBreak.getTimeWindow().getEnd()){
+ if (inRoute.getEnd().getArrTime() > aBreak.getTimeWindow().getEnd()) {
InsertionData iData = breakInsertionCalculator.getInsertionData(inRoute, aBreak, inRoute.getVehicle(), inRoute.getDepartureTime(), inRoute.getDriver(), Double.MAX_VALUE);
if(!(iData instanceof InsertionData.NoInsertionFound)){
logger.trace("insert: [jobId={}]{}", aBreak.getId(), iData);
for(Event e : iData.getEvents()){
eventListeners.inform(e);
}
- stateManager.informJobInserted(aBreak,inRoute,0,0);
+ stateManager.informJobInserted(aBreak, inRoute, iData);
}
}
}
@@ -78,7 +78,6 @@ public void informJobInserted(Job job2insert, VehicleRoute inRoute, double addit
@Override
public void ruinStarts(Collection routes) {
-
}
@Override
@@ -88,9 +87,9 @@ public void ruinEnds(Collection routes, Collection unassigned
boolean removed = route.getTourActivities().removeJob(aBreak);
if(removed) logger.trace("ruin: {}", aBreak.getId());
}
- List breaks = new ArrayList();
+ List breaks = new ArrayList<>();
for (Job j : unassignedJobs) {
- if (j instanceof Break) {
+ if (j.getJobType().isBreak()) {
breaks.add((Break) j);
}
}
@@ -99,7 +98,6 @@ public void ruinEnds(Collection routes, Collection unassigned
@Override
public void removed(Job job, VehicleRoute fromRoute) {
- if(fromRoute.getVehicle().getBreak() != null) modifiedRoutes.add(fromRoute);
}
@Override
@@ -114,11 +112,10 @@ public void informInsertionStarts(Collection vehicleRoutes, Collec
for(Event e : iData.getEvents()){
eventListeners.inform(e);
}
- stateManager.informJobInserted(aBreak,route,0,0);
+ stateManager.informJobInserted(aBreak, route, iData);
}
}
}
}
-
}
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureLocalActivityInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureLocalActivityInsertionCalculator.java
index a140ea35e..914bf2090 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureLocalActivityInsertionCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ConfigureLocalActivityInsertionCalculator.java
@@ -50,7 +50,7 @@ public void informInsertionStarts(Collection vehicleRoutes, Collec
}
@Override
- public void informJobInserted(Job job2insert, VehicleRoute inRoute, double additionalCosts, double additionalTime) {
+ public void informJobInserted(Job job2insert, VehicleRoute inRoute, InsertionData insertionData) {
nuOfJobsToRecreate--;
double completenessRatio = (1 - ((double) nuOfJobsToRecreate / (double) vrp.getJobs().values().size()));
localActivityInsertionCostsCalculator.setSolutionCompletenessRatio(Math.max(0.5, completenessRatio));
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DefaultRegretScoringFunction.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DefaultRegretScoringFunction.java
new file mode 100644
index 000000000..cdeb8f9c6
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DefaultRegretScoringFunction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.job.Job;
+
+public class DefaultRegretScoringFunction implements RegretScoringFunction {
+
+ private final ScoringFunction scoringFunction;
+
+ public DefaultRegretScoringFunction(ScoringFunction scoringFunction) {
+ this.scoringFunction = scoringFunction;
+ }
+
+ @Override
+ public double score(InsertionData best, InsertionData secondBest, Job job) {
+ if (best == null) {
+ throw new IllegalStateException("cannot score job " + job.getId());
+ }
+ double score;
+ if (secondBest == null) { //either there is only one vehicle or there are more vehicles, but they cannot load unassignedJob
+ //if only one vehicle, I want the job to be inserted with min iCosts
+ //if there are more vehicles, I want this job to be prioritized since there are no alternatives
+ score = (11 - job.getPriority()) * (Integer.MAX_VALUE - best.getInsertionCost()) + scoringFunction.score(best, job);
+ } else {
+ score = (11 - job.getPriority()) * (secondBest.getInsertionCost() - best.getInsertionCost()) + scoringFunction.score(best, job);
+ }
+ return score;
+ }
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DefaultScorer.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DefaultScorer.java
index 280de34d2..a269245d2 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DefaultScorer.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DefaultScorer.java
@@ -20,9 +20,9 @@
import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
+import com.graphhopper.jsprit.core.problem.job.Activity;
import com.graphhopper.jsprit.core.problem.job.Job;
-import com.graphhopper.jsprit.core.problem.job.Service;
-import com.graphhopper.jsprit.core.problem.job.Shipment;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow;
/**
* Created by schroeder on 15/10/15.
@@ -41,56 +41,45 @@ public DefaultScorer(VehicleRoutingProblem vrp) {
this.vrp = vrp;
}
- public void setTimeWindowParam(double tw_param) {
- this.timeWindowParam = tw_param;
+ public void setTimeWindowParam(double twParam) {
+ this.timeWindowParam = twParam;
}
- public void setDepotDistanceParam(double depotDistance_param) {
- this.depotDistanceParam = depotDistance_param;
+ public void setDepotDistanceParam(double depotDistanceParam) {
+ this.depotDistanceParam = depotDistanceParam;
}
@Override
public double score(InsertionData best, Job job) {
- double score;
- if (job instanceof Service) {
- score = scoreService(best, job);
- } else if (job instanceof Shipment) {
- score = scoreShipment(best, job);
- } else throw new IllegalStateException("not supported");
- return score;
+ return scoreJob(best, job);
}
- private double scoreShipment(InsertionData best, Job job) {
- Shipment shipment = (Shipment) job;
- double maxDepotDistance_1 = Math.max(
- getDistance(best.getSelectedVehicle().getStartLocation(), shipment.getPickupLocation()),
- getDistance(best.getSelectedVehicle().getStartLocation(), shipment.getDeliveryLocation())
- );
- double maxDepotDistance_2 = Math.max(
- getDistance(best.getSelectedVehicle().getEndLocation(), shipment.getPickupLocation()),
- getDistance(best.getSelectedVehicle().getEndLocation(), shipment.getDeliveryLocation())
- );
- double maxDepotDistance = Math.max(maxDepotDistance_1, maxDepotDistance_2);
- double minTimeToOperate = Math.min(shipment.getPickupTimeWindow().getEnd() - shipment.getPickupTimeWindow().getStart(),
- shipment.getDeliveryTimeWindow().getEnd() - shipment.getDeliveryTimeWindow().getStart());
+ private double scoreJob(InsertionData best, Job job) {
+ Location startLocation = best.getSelectedVehicle().getStartLocation();
+ Location endLocation = best.getSelectedVehicle().getEndLocation();
+ double maxDepotDistance = 0;
+ double minTimeToOperate = Double.MAX_VALUE;
+ for (Activity act : job.getActivities()) {
+ maxDepotDistance = Math.max(maxDepotDistance, getDistance(startLocation, act.getLocation()));
+ maxDepotDistance = Math.max(maxDepotDistance, getDistance(endLocation, act.getLocation()));
+ TimeWindow tw = getLargestTimeWindow(act);
+ minTimeToOperate = Math.min(minTimeToOperate, tw.getEnd() - tw.getStart());
+ }
return Math.max(timeWindowParam * minTimeToOperate, minTimeWindowScore) + depotDistanceParam * maxDepotDistance;
}
- private double scoreService(InsertionData best, Job job) {
- Location location = ((Service) job).getLocation();
- double maxDepotDistance = 0;
- if (location != null) {
- maxDepotDistance = Math.max(
- getDistance(best.getSelectedVehicle().getStartLocation(), location),
- getDistance(best.getSelectedVehicle().getEndLocation(), location)
- );
+ private TimeWindow getLargestTimeWindow(Activity act) {
+ TimeWindow timeWindow = null;
+ for (TimeWindow tw : act.getTimeWindows()) {
+ if (timeWindow == null) timeWindow = tw;
+ else if (tw.larger(timeWindow)) timeWindow = tw;
}
- return Math.max(timeWindowParam * (((Service) job).getTimeWindow().getEnd() - ((Service) job).getTimeWindow().getStart()), minTimeWindowScore) +
- depotDistanceParam * maxDepotDistance;
+ return TimeWindow.newInstance(0, Double.MAX_VALUE);
}
private double getDistance(Location loc1, Location loc2) {
+ if (loc1 == null || loc2 == null) return 0d;
return vrp.getTransportCosts().getTransportCost(loc1, loc2, 0., null, null);
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DellAmicoFixCostCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DellAmicoFixCostCalculator.java
index a08fe6d76..6011bc43a 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DellAmicoFixCostCalculator.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/DellAmicoFixCostCalculator.java
@@ -54,7 +54,7 @@ public void informInsertionStarts(Collection routes, Collection= route.getTourActivities().getActivities().size()) {
+ if (iData.getDeliveryInsertionIndex() >= route.getTourActivities().size()) {
setEndLocation(route, (Service) job);
}
}
@@ -103,7 +103,7 @@ public ShipmentInsertionHandler(VehicleRoutingProblem vehicleRoutingProblem) {
@Override
public void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route) {
- if (job instanceof Shipment) {
+ if (job.getJobType().isShipment()) {
List acts = vehicleRoutingProblem.copyAndGetActivities(job);
TourActivity pickupShipment = acts.get(0);
TourActivity deliverShipment = acts.get(1);
@@ -153,6 +153,6 @@ public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicle
}
jobInsertionHandler.handleJobInsertion(job, insertionData, vehicleRoute);
- insertionListeners.informJobInserted(job, vehicleRoute, insertionData.getInsertionCost(), insertionData.getAdditionalTime());
+ insertionListeners.informJobInserted(job, vehicleRoute, insertionData);
}
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java
index fd78b3e9e..579063f4a 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionData.java
@@ -54,7 +54,7 @@ public static InsertionData createEmptyInsertionData() {
return noInsertion;
}
- static int NO_INDEX = -1;
+ public static int NO_INDEX = -1;
private final double insertionCost;
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java
index 8d019e2c2..872eeec89 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionDataUpdater.java
@@ -62,25 +62,22 @@ static VehicleRoute findRoute(Collection routes, Job job) {
}
static Comparator getComparator(){
- return new Comparator() {
- @Override
- public int compare(VersionedInsertionData o1, VersionedInsertionData o2) {
- if(o1.getiData().getInsertionCost() < o2.getiData().getInsertionCost()) return -1;
- return 1;
- }
+ return (o1, o2) -> {
+ if (o1.getiData().getInsertionCost() < o2.getiData().getInsertionCost()) return -1;
+ return 1;
};
}
- static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction, TreeSet[] priorityQueues, Map updates, List unassignedJobList, List badJobs) {
+ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, RegretScoringFunction scoringFunction, TreeSet[] priorityQueues, Map updates, List unassignedJobList, List badJobs) {
ScoredJob bestScoredJob = null;
- for(Job j : unassignedJobList){
+ for (Job j : unassignedJobList) {
VehicleRoute bestRoute = null;
InsertionData best = null;
InsertionData secondBest = null;
TreeSet priorityQueue = priorityQueues[j.getIndex()];
Iterator iterator = priorityQueue.iterator();
List failedConstraintNames = new ArrayList<>();
- while(iterator.hasNext()){
+ while (iterator.hasNext()) {
VersionedInsertionData versionedIData = iterator.next();
if(bestRoute != null){
if(versionedIData.getRoute() == bestRoute){
@@ -145,7 +142,7 @@ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, V
badJobs.add(new ScoredJob.BadJob(j, failedConstraintNames));
continue;
}
- double score = score(j, best, secondBest, scoringFunction);
+ double score = scoringFunction.score(best, secondBest, j);
ScoredJob scoredJob;
if (bestRoute == emptyRoute) {
scoredJob = new ScoredJob(j, score, best, bestRoute, true);
@@ -161,8 +158,5 @@ else if(scoredJob.getScore() > bestScoredJob.getScore()){
return bestScoredJob;
}
- static double score(Job unassignedJob, InsertionData best, InsertionData secondBest, ScoringFunction scoringFunction) {
- return Scorer.score(unassignedJob,best,secondBest,scoringFunction);
- }
}
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
index 310047fc6..2936c1246 100644
--- 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
@@ -23,30 +23,29 @@
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleFleetManager;
+import com.graphhopper.jsprit.core.util.RandomNumberGeneration;
import java.util.ArrayList;
import java.util.List;
+import java.util.Random;
import java.util.concurrent.ExecutorService;
public class InsertionStrategyBuilder {
- private boolean fastRegret;
-
-
public enum Strategy {
REGRET, BEST
}
- private VehicleRoutingProblem vrp;
+ private final VehicleRoutingProblem vrp;
- private StateManager stateManager;
+ private final StateManager stateManager;
private boolean local = true;
- private ConstraintManager constraintManager;
+ private final ConstraintManager constraintManager;
- private VehicleFleetManager fleetManager;
+ private final VehicleFleetManager fleetManager;
private double weightOfFixedCosts;
@@ -62,12 +61,6 @@ public enum Strategy {
private int nuOfThreads;
- private double timeSlice;
-
- private int nNeighbors;
-
- private boolean timeScheduling = false;
-
private boolean allowVehicleSwitch = true;
private boolean addDefaultCostCalc = true;
@@ -82,6 +75,8 @@ public enum Strategy {
private JobInsertionCostsCalculatorFactory breakInsertionCalculatorFactory;
+ private Random random = RandomNumberGeneration.getRandom();
+
public InsertionStrategyBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager, ConstraintManager constraintManager) {
super();
this.vrp = vrp;
@@ -110,6 +105,11 @@ public InsertionStrategyBuilder setInsertionStrategy(Strategy strategy) {
return this;
}
+ public InsertionStrategyBuilder setRandom(Random random) {
+ this.random = random;
+ return this;
+ }
+
public InsertionStrategyBuilder setRouteLevel(int forwardLooking, int memory) {
local = false;
this.forwaredLooking = forwardLooking;
@@ -136,13 +136,6 @@ public InsertionStrategyBuilder setLocalLevel() {
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;
@@ -168,8 +161,8 @@ public InsertionStrategyBuilder setConcurrentMode(ExecutorService executor, int
public InsertionStrategy build() {
- List iListeners = new ArrayList();
- List algorithmListeners = new ArrayList();
+ List iListeners = new ArrayList<>();
+ List algorithmListeners = new ArrayList<>();
JobInsertionCostsCalculatorBuilder calcBuilder = new JobInsertionCostsCalculatorBuilder(iListeners, algorithmListeners);
if (local) {
calcBuilder.setLocalLevel(addDefaultCostCalc);
@@ -190,27 +183,30 @@ public InsertionStrategy build() {
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);
+ BestInsertion bestInsertion = new BestInsertion(costCalculator, vrp);
+ bestInsertion.setRandom(random);
+ insertion = bestInsertion;
} else {
- insertion = new BestInsertionConcurrent(costCalculator, executor, nuOfThreads, vrp);
+ BestInsertionConcurrent bestInsertion = new BestInsertionConcurrent(costCalculator, executor, nuOfThreads, vrp);
+ bestInsertion.setRandom(random);
+ insertion = bestInsertion;
}
} else if (strategy.equals(Strategy.REGRET)) {
if (executor == null) {
if (isFastRegret) {
RegretInsertionFast regret = new RegretInsertionFast(costCalculator, vrp, fleetManager);
regret.setSwitchAllowed(allowVehicleSwitch);
+ regret.setRandom(random);
insertion = regret;
} else {
RegretInsertion regret = new RegretInsertion(costCalculator, vrp);
+ regret.setRandom(random);
insertion = regret;
}
@@ -218,9 +214,11 @@ public InsertionStrategy build() {
if (isFastRegret) {
RegretInsertionConcurrentFast regret = new RegretInsertionConcurrentFast(costCalculator, vrp, executor, fleetManager);
regret.setSwitchAllowed(allowVehicleSwitch);
+ regret.setRandom(random);
insertion = regret;
} else {
RegretInsertionConcurrent regret = new RegretInsertionConcurrent(costCalculator, vrp, executor);
+ regret.setRandom(random);
insertion = regret;
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobCalculatorSwitcher.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobCalculatorSelector.java
similarity index 89%
rename from jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobCalculatorSwitcher.java
rename to jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobCalculatorSelector.java
index da8c7a9e6..8c65eb136 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobCalculatorSwitcher.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobCalculatorSelector.java
@@ -26,9 +26,9 @@
import java.util.Map;
-class JobCalculatorSwitcher implements JobInsertionCostsCalculator {
+class JobCalculatorSelector implements JobInsertionCostsCalculator {
- private Map, JobInsertionCostsCalculator> calcMap = new HashMap, JobInsertionCostsCalculator>();
+ private final Map, JobInsertionCostsCalculator> calcMap = new HashMap, JobInsertionCostsCalculator>();
void put(Class extends Job> jobClass, JobInsertionCostsCalculator jic) {
calcMap.put(jobClass, jic);
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 faf760c04..e75979397 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
@@ -260,7 +260,7 @@ public JobInsertionCostsCalculator build() {
private void checkServicesOnly() {
for (Job j : vrp.getJobs().values()) {
- if (j instanceof Shipment) {
+ if (j.getJobType().isShipment()) {
throw new UnsupportedOperationException("currently the 'insert-on-route-level' option is only available for services (i.e. service, pickup, delivery), \n" +
"if you want to deal with shipments switch to option 'local-level' by either setting bestInsertionBuilder.setLocalLevel() or \n"
+ "by omitting the xml-tag 'route' when defining your insertionStrategy in algo-config.xml file");
@@ -316,14 +316,16 @@ public List createActivities(Job job) {
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, breakInsertion);
+ JobCalculatorSelector jobCalculatorSelector = new JobCalculatorSelector();
+ jobCalculatorSelector.put(Shipment.class, shipmentInsertion);
+ jobCalculatorSelector.put(Service.class, serviceInsertion);
+ jobCalculatorSelector.put(Pickup.class, serviceInsertion);
+ jobCalculatorSelector.put(Delivery.class, serviceInsertion);
+ jobCalculatorSelector.put(EnRoutePickup.class, serviceInsertion);
+ jobCalculatorSelector.put(EnRouteDelivery.class, serviceInsertion);
+ jobCalculatorSelector.put(Break.class, breakInsertion);
- CalculatorPlusListeners calculatorPlusListeners = new CalculatorPlusListeners(switcher);
+ CalculatorPlusListeners calculatorPlusListeners = new CalculatorPlusListeners(jobCalculatorSelector);
if (configLocal != null) {
calculatorPlusListeners.insertionListener.add(configLocal);
}
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 3e0725fea..69899eae8 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
@@ -19,9 +19,11 @@
package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.algorithm.state.InternalStates;
+import com.graphhopper.jsprit.core.problem.Location;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext;
+import com.graphhopper.jsprit.core.problem.solution.route.activity.ActWithoutStaticLocation;
import com.graphhopper.jsprit.core.problem.solution.route.activity.DeliverShipment;
import com.graphhopper.jsprit.core.problem.solution.route.activity.End;
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
@@ -58,9 +60,15 @@ public LocalActivityInsertionCostsCalculator(VehicleRoutingTransportCosts routin
@Override
public double getCosts(JobInsertionContext iFacts, TourActivity prevAct, TourActivity nextAct, TourActivity newAct, double depTimeAtPrevAct) {
-
- double tp_costs_prevAct_newAct = routingCosts.getTransportCost(prevAct.getLocation(), newAct.getLocation(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
- double tp_time_prevAct_newAct = routingCosts.getTransportTime(prevAct.getLocation(), newAct.getLocation(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
+ Location prevLocation = prevAct.getLocation();
+ if (prevAct instanceof ActWithoutStaticLocation) ((ActWithoutStaticLocation) prevAct).getPreviousLocation();
+ Location newLocation = newAct.getLocation();
+ if (newAct instanceof ActWithoutStaticLocation) newLocation = prevLocation;
+ Location nextLocation = nextAct.getLocation();
+ if (nextAct instanceof ActWithoutStaticLocation) ((ActWithoutStaticLocation) nextAct).getNextLocation();
+
+ double tp_costs_prevAct_newAct = routingCosts.getTransportCost(prevLocation, newLocation, depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
+ double tp_time_prevAct_newAct = routingCosts.getTransportTime(prevLocation, newLocation, depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
double newAct_arrTime = depTimeAtPrevAct + tp_time_prevAct_newAct;
double newAct_endTime = Math.max(newAct_arrTime, newAct.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(newAct, newAct_arrTime, iFacts.getNewDriver(), iFacts.getNewVehicle());
@@ -68,8 +76,9 @@ public double getCosts(JobInsertionContext iFacts, TourActivity prevAct, TourAct
if (isEnd(nextAct) && !toDepot(iFacts.getNewVehicle())) return tp_costs_prevAct_newAct + solutionCompletenessRatio * activityCostsWeight * act_costs_newAct;
- double tp_costs_newAct_nextAct = routingCosts.getTransportCost(newAct.getLocation(), nextAct.getLocation(), newAct_endTime, iFacts.getNewDriver(), iFacts.getNewVehicle());
- double tp_time_newAct_nextAct = routingCosts.getTransportTime(newAct.getLocation(), nextAct.getLocation(), newAct_endTime, iFacts.getNewDriver(), iFacts.getNewVehicle());
+
+ double tp_costs_newAct_nextAct = routingCosts.getTransportCost(newLocation, nextLocation, newAct_endTime, iFacts.getNewDriver(), iFacts.getNewVehicle());
+ double tp_time_newAct_nextAct = routingCosts.getTransportTime(newLocation, nextLocation, newAct_endTime, iFacts.getNewDriver(), iFacts.getNewVehicle());
double nextAct_arrTime = newAct_endTime + tp_time_newAct_nextAct;
double endTime_nextAct_new = Math.max(nextAct_arrTime, nextAct.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextAct, nextAct_arrTime, iFacts.getNewDriver(), iFacts.getNewVehicle());
double act_costs_nextAct = activityCosts.getActivityCost(nextAct, nextAct_arrTime, iFacts.getNewDriver(), iFacts.getNewVehicle());
@@ -80,11 +89,11 @@ public double getCosts(JobInsertionContext iFacts, TourActivity prevAct, TourAct
if (iFacts.getRoute().isEmpty()) {
double tp_costs_prevAct_nextAct = 0.;
if (newAct instanceof DeliverShipment)
- tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocation(), nextAct.getLocation(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
+ tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevLocation, nextLocation, depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle());
oldCosts += tp_costs_prevAct_nextAct;
} else {
- double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocation(), nextAct.getLocation(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
- double arrTime_nextAct = depTimeAtPrevAct + routingCosts.getTransportTime(prevAct.getLocation(), nextAct.getLocation(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
+ double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevLocation, nextLocation, prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
+ double arrTime_nextAct = depTimeAtPrevAct + routingCosts.getTransportTime(prevLocation, nextLocation, prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
double endTime_nextAct_old = Math.max(arrTime_nextAct, nextAct.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextAct, arrTime_nextAct, iFacts.getRoute().getDriver(),iFacts.getRoute().getVehicle());
double actCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct, iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle());
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java
index 30a7a98a4..916247efa 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertion.java
@@ -19,7 +19,6 @@
package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
-import com.graphhopper.jsprit.core.problem.job.Break;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import org.slf4j.Logger;
@@ -43,13 +42,16 @@
public class RegretInsertion extends AbstractInsertionStrategy {
+ private static final Logger logger = LoggerFactory.getLogger(RegretInsertion.class);
- private static Logger logger = LoggerFactory.getLogger(RegretInsertionFast.class);
+ private final JobInsertionCostsCalculator insertionCostsCalculator;
- private ScoringFunction scoringFunction;
+ private RegretScoringFunction regretScoringFunction;
- private JobInsertionCostsCalculator insertionCostsCalculator;
+ public void setRegretScoringFunction(RegretScoringFunction regretScoringFunction) {
+ this.regretScoringFunction = regretScoringFunction;
+ }
/**
* Sets the scoring function.
@@ -59,12 +61,12 @@ public class RegretInsertion extends AbstractInsertionStrategy {
* @param scoringFunction to score
*/
public void setScoringFunction(ScoringFunction scoringFunction) {
- this.scoringFunction = scoringFunction;
+ this.regretScoringFunction = new DefaultRegretScoringFunction(scoringFunction);
}
public RegretInsertion(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem) {
super(vehicleRoutingProblem);
- this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
+ this.regretScoringFunction = new DefaultRegretScoringFunction(new DefaultScorer(vehicleRoutingProblem));
this.insertionCostsCalculator = jobInsertionCalculator;
this.vrp = vehicleRoutingProblem;
logger.debug("initialise {}", this);
@@ -72,7 +74,7 @@ public RegretInsertion(JobInsertionCostsCalculator jobInsertionCalculator, Vehic
@Override
public String toString() {
- return "[name=regretInsertion][additionalScorer=" + scoringFunction + "]";
+ return "[name=regretInsertion][additionalScorer=" + regretScoringFunction + "]";
}
@@ -83,17 +85,16 @@ public String toString() {
*/
@Override
public Collection insertUnassignedJobs(Collection routes, Collection unassignedJobs) {
- List badJobs = new ArrayList(unassignedJobs.size());
+ List badJobs = new ArrayList<>(unassignedJobs.size());
Iterator jobIterator = unassignedJobs.iterator();
while (jobIterator.hasNext()){
Job job = jobIterator.next();
- if(job instanceof Break){
- VehicleRoute route = findRoute(routes,job);
- if(route == null){
+ if (job.getJobType().isBreak()) {
+ VehicleRoute route = findRoute(routes, job);
+ if (route == null) {
badJobs.add(job);
- }
- else {
+ } else {
InsertionData iData = insertionCostsCalculator.getInsertionData(route, job, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE);
if (iData instanceof InsertionData.NoInsertionFound) {
badJobs.add(job);
@@ -109,7 +110,7 @@ public Collection insertUnassignedJobs(Collection routes, Col
while (!jobs.isEmpty()) {
List unassignedJobList = new ArrayList<>(jobs);
List badJobList = new ArrayList<>();
- ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
+ ScoredJob bestScoredJob = getBestScoredUnassignedJob(routes, unassignedJobList, badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
routes.add(bestScoredJob.getRoute());
@@ -134,10 +135,10 @@ private VehicleRoute findRoute(Collection routes, Job job) {
return null;
}
- private ScoredJob nextJob(Collection routes, Collection unassignedJobList, List badJobs) {
+ private ScoredJob getBestScoredUnassignedJob(Collection routes, Collection unassignedJobList, List badJobs) {
ScoredJob bestScoredJob = null;
for (Job unassignedJob : unassignedJobList) {
- ScoredJob scoredJob = getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
+ ScoredJob scoredJob = Scorer.scoreUnassignedJob(routes, unassignedJob, insertionCostsCalculator, regretScoringFunction);
if (scoredJob instanceof ScoredJob.BadJob) {
badJobs.add(scoredJob);
continue;
@@ -156,63 +157,5 @@ private ScoredJob nextJob(Collection routes, Collection unass
return bestScoredJob;
}
- static ScoredJob getScoredJob(Collection routes, Job unassignedJob, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction) {
- InsertionData best = null;
- InsertionData secondBest = null;
- VehicleRoute bestRoute = null;
- List failedConstraintNames = new ArrayList<>();
- double benchmark = Double.MAX_VALUE;
- for (VehicleRoute route : routes) {
- if (secondBest != null) {
- benchmark = secondBest.getInsertionCost();
- }
- InsertionData iData = insertionCostsCalculator.getInsertionData(route, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, benchmark);
- if (iData instanceof InsertionData.NoInsertionFound) {
- failedConstraintNames.addAll(iData.getFailedConstraintNames());
- continue;
- }
- if (best == null) {
- best = iData;
- bestRoute = route;
- } else if (iData.getInsertionCost() < best.getInsertionCost()) {
- secondBest = best;
- best = iData;
- bestRoute = route;
- } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
- secondBest = iData;
- }
- }
-
- VehicleRoute emptyRoute = VehicleRoute.emptyRoute();
- InsertionData iData = insertionCostsCalculator.getInsertionData(emptyRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, benchmark);
- if (!(iData instanceof InsertionData.NoInsertionFound)) {
- if (best == null) {
- best = iData;
- bestRoute = emptyRoute;
- } else if (iData.getInsertionCost() < best.getInsertionCost()) {
- secondBest = best;
- best = iData;
- bestRoute = emptyRoute;
- } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
- secondBest = iData;
- }
- } else failedConstraintNames.addAll(iData.getFailedConstraintNames());
- if (best == null) {
- ScoredJob.BadJob badJob = new ScoredJob.BadJob(unassignedJob, failedConstraintNames);
- return badJob;
- }
- double score = score(unassignedJob, best, secondBest, scoringFunction);
- ScoredJob scoredJob;
- if (bestRoute == emptyRoute) {
- scoredJob = new ScoredJob(unassignedJob, score, best, bestRoute, true);
- } else scoredJob = new ScoredJob(unassignedJob, score, best, bestRoute, false);
- return scoredJob;
- }
-
-
- static double score(Job unassignedJob, InsertionData best, InsertionData secondBest, ScoringFunction scoringFunction) {
- return Scorer.score(unassignedJob,best,secondBest,scoringFunction);
- }
-
}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java
index b71ac5ab7..6788cc4a5 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrent.java
@@ -19,7 +19,6 @@
package com.graphhopper.jsprit.core.algorithm.recreate;
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
-import com.graphhopper.jsprit.core.problem.job.Break;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import org.slf4j.Logger;
@@ -29,7 +28,10 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
/**
* Insertion based on regret approach.
@@ -43,15 +45,14 @@
*/
public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
+ private static final Logger logger = LoggerFactory.getLogger(RegretInsertionConcurrentFast.class);
- private static Logger logger = LoggerFactory.getLogger(RegretInsertionConcurrentFast.class);
+ private final ExecutorService executorService;
- private ScoringFunction scoringFunction;
+ private RegretScoringFunction regretScoringFunction;
private final JobInsertionCostsCalculator insertionCostsCalculator;
- private final ExecutorCompletionService completionService;
-
/**
* Sets the scoring function.
*
@@ -60,21 +61,26 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
* @param scoringFunction to score
*/
public void setScoringFunction(ScoringFunction scoringFunction) {
- this.scoringFunction = scoringFunction;
+ this.regretScoringFunction = new DefaultRegretScoringFunction(scoringFunction);
+ }
+
+ public void setRegretScoringFunction(RegretScoringFunction regretScoringFunction) {
+ this.regretScoringFunction = regretScoringFunction;
}
public RegretInsertionConcurrent(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem, ExecutorService executorService) {
super(vehicleRoutingProblem);
- this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
+// this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
+ this.regretScoringFunction = new DefaultRegretScoringFunction(new DefaultScorer(vehicleRoutingProblem));
this.insertionCostsCalculator = jobInsertionCalculator;
this.vrp = vehicleRoutingProblem;
- completionService = new ExecutorCompletionService(executorService);
+ this.executorService = executorService;
logger.debug("initialise " + this);
}
@Override
public String toString() {
- return "[name=regretInsertion][additionalScorer=" + scoringFunction + "]";
+ return "[name=regretInsertion][additionalScorer=" + regretScoringFunction + "]";
}
@@ -87,17 +93,16 @@ public String toString() {
*/
@Override
public Collection insertUnassignedJobs(Collection routes, Collection unassignedJobs) {
- List badJobs = new ArrayList(unassignedJobs.size());
+ List badJobs = new ArrayList<>(unassignedJobs.size());
Iterator jobIterator = unassignedJobs.iterator();
while (jobIterator.hasNext()){
Job job = jobIterator.next();
- if(job instanceof Break){
- VehicleRoute route = findRoute(routes,job);
- if(route == null){
+ if (job.getJobType().isBreak()) {
+ VehicleRoute route = findRoute(routes, job);
+ if (route == null) {
badJobs.add(job);
- }
- else {
+ } else {
InsertionData iData = insertionCostsCalculator.getInsertionData(route, job, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, Double.MAX_VALUE);
if (iData instanceof InsertionData.NoInsertionFound) {
badJobs.add(job);
@@ -113,7 +118,7 @@ public Collection insertUnassignedJobs(Collection routes, Col
while (!jobs.isEmpty()) {
List unassignedJobList = new ArrayList<>(jobs);
List badJobList = new ArrayList<>();
- ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
+ ScoredJob bestScoredJob = calculateBestJob(routes, unassignedJobList, badJobList);
if (bestScoredJob != null) {
if (bestScoredJob.isNewRoute()) {
routes.add(bestScoredJob.getRoute());
@@ -131,24 +136,17 @@ public Collection insertUnassignedJobs(Collection routes, Col
return badJobs;
}
- private ScoredJob nextJob(final Collection routes, List unassignedJobList, List badJobList) {
+ private ScoredJob calculateBestJob(final Collection routes, List unassignedJobList, List badJobList) {
ScoredJob bestScoredJob = null;
-
+ List> tasks = new ArrayList<>(unassignedJobList.size());
for (final Job unassignedJob : unassignedJobList) {
- completionService.submit(new Callable() {
-
- @Override
- public ScoredJob call() throws Exception {
- return RegretInsertion.getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
- }
-
- });
+ tasks.add(() -> Scorer.scoreUnassignedJob(routes, unassignedJob, insertionCostsCalculator, regretScoringFunction));
}
try {
+ List> futureResponses = executorService.invokeAll(tasks);
for (int i = 0; i < unassignedJobList.size(); i++) {
- Future fsj = completionService.take();
- ScoredJob sJob = fsj.get();
+ ScoredJob sJob = futureResponses.get(i).get();
if (sJob instanceof ScoredJob.BadJob) {
badJobList.add(sJob);
continue;
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java
index be4f5d118..41d643df6 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionConcurrentFast.java
@@ -20,7 +20,6 @@
import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem;
import com.graphhopper.jsprit.core.problem.constraint.DependencyType;
-import com.graphhopper.jsprit.core.problem.job.Break;
import com.graphhopper.jsprit.core.problem.job.Job;
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
import com.graphhopper.jsprit.core.problem.vehicle.VehicleFleetManager;
@@ -44,17 +43,17 @@
public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
- private static Logger logger = LoggerFactory.getLogger(RegretInsertionConcurrentFast.class);
+ private static final Logger logger = LoggerFactory.getLogger(RegretInsertionConcurrentFast.class);
- private ScoringFunction scoringFunction;
+ private RegretScoringFunction regretScoringFunction;
private final JobInsertionCostsCalculator insertionCostsCalculator;
private final ExecutorService executor;
- private VehicleFleetManager fleetManager;
+ private final VehicleFleetManager fleetManager;
- private Set initialVehicleIds;
+ private final Set initialVehicleIds;
private boolean switchAllowed = true;
@@ -69,12 +68,16 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
* @param scoringFunction to score
*/
public void setScoringFunction(ScoringFunction scoringFunction) {
- this.scoringFunction = scoringFunction;
+ this.regretScoringFunction = new DefaultRegretScoringFunction(scoringFunction);
+ }
+
+ public void setRegretScoringFunction(RegretScoringFunction regretScoringFunction) {
+ this.regretScoringFunction = regretScoringFunction;
}
public RegretInsertionConcurrentFast(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem, ExecutorService executorService, VehicleFleetManager fleetManager) {
super(vehicleRoutingProblem);
- this.scoringFunction = new DefaultScorer(vehicleRoutingProblem);
+ this.regretScoringFunction = new DefaultRegretScoringFunction(new DefaultScorer(vehicleRoutingProblem));
this.insertionCostsCalculator = jobInsertionCalculator;
this.vrp = vehicleRoutingProblem;
this.executor = executorService;
@@ -85,7 +88,7 @@ public RegretInsertionConcurrentFast(JobInsertionCostsCalculator jobInsertionCal
@Override
public String toString() {
- return "[name=regretInsertion][additionalScorer=" + scoringFunction + "]";
+ return "[name=regretInsertion][additionalScorer=" + regretScoringFunction + "]";
}
public void setSwitchAllowed(boolean switchAllowed) {
@@ -93,7 +96,7 @@ public void setSwitchAllowed(boolean switchAllowed) {
}
private Set getInitialVehicleIds(VehicleRoutingProblem vehicleRoutingProblem) {
- Set ids = new HashSet();
+ Set ids = new HashSet<>();
for(VehicleRoute r : vehicleRoutingProblem.getInitialVehicleRoutes()){
ids.add(r.getVehicle().getId());
}
@@ -114,17 +117,16 @@ public void setDependencyTypes(DependencyType[] dependencyTypes){
*/
@Override
public Collection insertUnassignedJobs(Collection routes, Collection unassignedJobs) {
- List badJobs = new ArrayList(unassignedJobs.size());
+ List