From 90a8edaa6d94e7090e5b7204c4ad1dbff92704c9 Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Mon, 20 Aug 2018 23:30:23 +0200 Subject: [PATCH 1/9] add storing and indexing of non-job activities --- .../jsprit/core/problem/VehicleRoutingProblem.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java index 328d9a46..b13b1017 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java @@ -414,6 +414,16 @@ public class VehicleRoutingProblem { return this; } + public final List nonJobActivities = new ArrayList<>(); + + public void addNonJobActivities(Collection nonJobActivities) { + for (AbstractActivity act : nonJobActivities) { + act.setIndex(activityIndexCounter); + incActivityIndexCounter(); + this.nonJobActivities.add(act); + } + } + /** * Builds the {@link VehicleRoutingProblem}. *

From d4106fb6d05b676f26c47eba74a978a145fb5efd Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Mon, 20 Aug 2018 23:31:38 +0200 Subject: [PATCH 2/9] prevent casting of non-job activities to JobActivity --- .../analysis/toolbox/GraphStreamViewer.java | 44 ++++++++++--------- .../jsprit/core/algorithm/ruin/RuinWorst.java | 4 ++ 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/GraphStreamViewer.java b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/GraphStreamViewer.java index e04097f3..22d38ce9 100644 --- a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/GraphStreamViewer.java +++ b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/GraphStreamViewer.java @@ -572,28 +572,30 @@ public class GraphStreamViewer { n.addAttribute("ui.label", "start"); } for (TourActivity act : route.getActivities()) { - Job job = ((JobActivity) act).getJob(); - String currIdentifier = makeId(job.getId(), act.getLocation().getId()); - if (label.equals(Label.ACTIVITY)) { - Node actNode = g.getNode(currIdentifier); - actNode.addAttribute("ui.label", act.getName()); - } else if (label.equals(Label.JOB_NAME)) { - Node actNode = g.getNode(currIdentifier); - actNode.addAttribute("ui.label", job.getName()); - } else if (label.equals(Label.ARRIVAL_TIME)) { - Node actNode = g.getNode(currIdentifier); - actNode.addAttribute("ui.label", Time.parseSecondsToTime(act.getArrTime())); - } else if (label.equals(Label.DEPARTURE_TIME)) { - Node actNode = g.getNode(currIdentifier); - actNode.addAttribute("ui.label", Time.parseSecondsToTime(act.getEndTime())); + if (act instanceof JobActivity) { + Job job = ((JobActivity) act).getJob(); + String currIdentifier = makeId(job.getId(), act.getLocation().getId()); + if (label.equals(Label.ACTIVITY)) { + Node actNode = g.getNode(currIdentifier); + actNode.addAttribute("ui.label", act.getName()); + } else if (label.equals(Label.JOB_NAME)) { + Node actNode = g.getNode(currIdentifier); + actNode.addAttribute("ui.label", job.getName()); + } else if (label.equals(Label.ARRIVAL_TIME)) { + Node actNode = g.getNode(currIdentifier); + actNode.addAttribute("ui.label", Time.parseSecondsToTime(act.getArrTime())); + } else if (label.equals(Label.DEPARTURE_TIME)) { + Node actNode = g.getNode(currIdentifier); + actNode.addAttribute("ui.label", Time.parseSecondsToTime(act.getEndTime())); + } + g.addEdge(makeEdgeId(routeId, vehicle_edgeId), prevIdentifier, currIdentifier, true); + if (act instanceof PickupActivity) g.getNode(currIdentifier).addAttribute("ui.class", "pickupInRoute"); + else if (act instanceof DeliveryActivity) + g.getNode(currIdentifier).addAttribute("ui.class", "deliveryInRoute"); + prevIdentifier = currIdentifier; + vehicle_edgeId++; + sleep(renderDelay_in_ms); } - g.addEdge(makeEdgeId(routeId, vehicle_edgeId), prevIdentifier, currIdentifier, true); - if (act instanceof PickupActivity) g.getNode(currIdentifier).addAttribute("ui.class", "pickupInRoute"); - else if (act instanceof DeliveryActivity) - g.getNode(currIdentifier).addAttribute("ui.class", "deliveryInRoute"); - prevIdentifier = currIdentifier; - vehicle_edgeId++; - sleep(renderDelay_in_ms); } if (route.getVehicle().isReturnToDepot()) { String lastIdentifier = makeId(route.getVehicle().getId(), route.getVehicle().getEndLocation().getId()); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java index 620d1fed..a2cde83c 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java @@ -104,6 +104,10 @@ public final class RuinWorst extends AbstractRuinStrategy { TourActivity actBefore = route.getStart(); TourActivity actToEval = null; for (TourActivity act : route.getActivities()) { + if (!(act instanceof TourActivity.JobActivity)) { + continue; + } + if (actToEval == null) { actToEval = act; continue; From cf179b2f07685860b456041920cb10cf0bfd2625 Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Wed, 5 Sep 2018 13:49:35 +0200 Subject: [PATCH 3/9] change VRP.Builder.addNonJobActivities() return type void -> Builder --- .../jsprit/core/problem/VehicleRoutingProblem.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java index b13b1017..c688ce9c 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java @@ -414,14 +414,15 @@ public class VehicleRoutingProblem { return this; } - public final List nonJobActivities = new ArrayList<>(); + private final List nonJobActivities = new ArrayList<>(); - public void addNonJobActivities(Collection nonJobActivities) { + public Builder addNonJobActivities(Collection nonJobActivities) { for (AbstractActivity act : nonJobActivities) { act.setIndex(activityIndexCounter); incActivityIndexCounter(); this.nonJobActivities.add(act); } + return this; } /** From 3579dca52d08d08253626c3cf55ff699c41e6b49 Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Sat, 10 Nov 2018 12:26:24 +0100 Subject: [PATCH 4/9] remove redundant unmodifiableList() wrapping --- .../jsprit/core/problem/solution/route/VehicleRoute.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java index 40fe5907..ee14a75f 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/solution/route/VehicleRoute.java @@ -367,7 +367,7 @@ public class VehicleRoute { * @return list of tourActivities */ public List getActivities() { - return Collections.unmodifiableList(tourActivities.getActivities()); + return tourActivities.getActivities(); } /** From 3c1e8bc9d18a644043bb7727c1afa8318ef5ba3b Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Sat, 10 Nov 2018 12:38:39 +0100 Subject: [PATCH 5/9] replace (incorrect) iFacts.getNewRouteDepTime() with prevActDepTime --- .../jsprit/core/problem/constraint/MaxDistanceConstraint.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java index bb88358b..bf2d8800 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java @@ -78,8 +78,8 @@ public class MaxDistanceConstraint implements HardActivityConstraint { double maxDistance = getMaxDistance(iFacts.getNewVehicle()); if (currentDistance > maxDistance) return ConstraintsStatus.NOT_FULFILLED_BREAK; - double distancePrevAct2NewAct = distanceCalculator.getDistance(prevAct.getLocation(), newAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle()); - double distanceNewAct2nextAct = distanceCalculator.getDistance(newAct.getLocation(), nextAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle()); + double distancePrevAct2NewAct = distanceCalculator.getDistance(prevAct.getLocation(), newAct.getLocation(), prevActDepTime, iFacts.getNewVehicle()); + double distanceNewAct2nextAct = distanceCalculator.getDistance(newAct.getLocation(), nextAct.getLocation(), prevActDepTime, iFacts.getNewVehicle()); double distancePrevAct2NextAct = distanceCalculator.getDistance(prevAct.getLocation(), nextAct.getLocation(), prevActDepTime, iFacts.getNewVehicle()); if (prevAct instanceof Start && nextAct instanceof End) distancePrevAct2NextAct = 0; if (nextAct instanceof End && !iFacts.getNewVehicle().isReturnToDepot()) { From a87881ee5909d952c1b2030ee7a94368945a4c88 Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Tue, 13 Nov 2018 13:14:44 +0100 Subject: [PATCH 6/9] skip routes without JobActivities in RuinWorst --- .../com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java index a2cde83c..10c5cc42 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/ruin/RuinWorst.java @@ -123,6 +123,9 @@ public final class RuinWorst extends AbstractRuinStrategy { actBefore = actToEval; actToEval = act; } + if (actToEval == null) { + continue; + } double savings = savings(route, actBefore, actToEval, route.getEnd()); Job job = ((TourActivity.JobActivity) actToEval).getJob(); if (!savingsMap.containsKey(job)) { From 14993b3ca1286b0c9e03ff184149b7b15df049e1 Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Tue, 29 Jan 2019 14:49:43 +0100 Subject: [PATCH 7/9] update javadoc - lowest job priority has changed from 3 to 10 --- .../com/graphhopper/jsprit/core/problem/job/Service.java | 4 ++-- .../com/graphhopper/jsprit/core/problem/job/Shipment.java | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java index 99608c23..f9be6a4c 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java @@ -398,9 +398,9 @@ public class Service extends AbstractJob { } /** - * Get priority of service. Only 1 = high priority, 2 = medium and 3 = low are allowed. + * Get priority of service. Only 1 (high) to 10 (low) are allowed. *

- * Default is 2 = medium. + * Default is 2. * * @return priority */ diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java index 8d356a02..0af407c2 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java @@ -318,7 +318,7 @@ public class Shipment extends AbstractJob { /** * Set priority to shipment. Only 1 (high) to 10 (low) are allowed. *

- * Default is 2 = medium. + * Default is 2. * * @param priority * @return builder @@ -505,9 +505,9 @@ public class Shipment extends AbstractJob { } /** - * Get priority of shipment. Only 1 = high priority, 2 = medium and 3 = low are allowed. + * Get priority of shipment. Only 1 (high) to 10 (low) are allowed. *

- * Default is 2 = medium. + * Default is 2. * * @return priority */ From 89c1562b605baaae12aa6353de04cee79620a725 Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Wed, 3 Apr 2019 19:36:35 +0200 Subject: [PATCH 8/9] make plot background white (grey conflicted with grey series) --- .../java/com/graphhopper/jsprit/analysis/toolbox/Plotter.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/Plotter.java b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/Plotter.java index 2124fb02..c519e759 100644 --- a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/Plotter.java +++ b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/Plotter.java @@ -302,6 +302,10 @@ public class Plotter { final XYPlot plot = createPlot(problem, shipments, solution); JFreeChart chart = new JFreeChart(title, plot); + plot.setBackgroundPaint(Color.WHITE); + plot.setDomainGridlinesVisible(false); + plot.setRangeGridlinesVisible(false); + LegendTitle legend = createLegend(routes, shipments, plot); chart.removeLegend(); chart.addLegend(legend); From 990a4e7f68a7427e06f2baa5e3f3f93a0b5e37ac Mon Sep 17 00:00:00 2001 From: Michal Maciejewski Date: Tue, 7 May 2019 13:58:41 +0200 Subject: [PATCH 9/9] clean up VehicleRoutingAlgorithm.addInitialSolution() --- .../algorithm/VehicleRoutingAlgorithm.java | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java index 1155955b..f0c7a383 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java @@ -135,23 +135,28 @@ public class VehicleRoutingAlgorithm { this.objectiveFunction = objectiveFunction; } - /** - * Adds solution to the collection of initial solutions. - * - * @param solution the solution to be added - */ - public void addInitialSolution(VehicleRoutingProblemSolution solution) { + /** + * Adds solution to the collection of initial solutions. + * + * This method may lead to errors if tour activities in the solution are different to the + * ones in the VRP (including differences in indexing) + * + * @param solution the solution to be added + */ + public void addInitialSolution(VehicleRoutingProblemSolution solution) { // We will make changes so let's make a copy solution = VehicleRoutingProblemSolution.copyOf(solution); - verify(solution); + verifyAndAdaptSolution(solution); initialSolutions.add(solution); } - private void verify(VehicleRoutingProblemSolution solution) { - Set allJobs = new HashSet(problem.getJobs().values()); - allJobs.removeAll(solution.getUnassignedJobs()); + //this method may lead to errors if tour activities in the solution are different to the ones in the VRP + //(including differences in indexing) + private void verifyAndAdaptSolution(VehicleRoutingProblemSolution solution) { + Set jobsNotInSolution = new HashSet(problem.getJobs().values()); + jobsNotInSolution.removeAll(solution.getUnassignedJobs()); for (VehicleRoute route : solution.getRoutes()) { - allJobs.removeAll(route.getTourActivities().getJobs()); + jobsNotInSolution.removeAll(route.getTourActivities().getJobs()); if (route.getVehicle().getIndex() == 0) throw new IllegalStateException("vehicle used in initial solution has no index. probably a vehicle is used that has not been added to the " + " the VehicleRoutingProblem. only use vehicles that have already been added to the problem."); @@ -164,7 +169,9 @@ public class VehicleRoutingAlgorithm { } } - solution.getUnassignedJobs().addAll(allJobs); + //if solution is partial (not all jobs are considered), add these jobs to solution.unassignedJobs + solution.getUnassignedJobs().addAll(jobsNotInSolution); + //update the cost of solution (regardless if partial or not) solution.setCost(getObjectiveFunction().getCosts(solution)); // if (nuJobs != problem.getJobs().values().size()) {