From 9d8a90bffec82dd36bcc0c04c890c1bf13dd6320 Mon Sep 17 00:00:00 2001 From: Toni Rajkovski Date: Thu, 20 Jul 2017 17:21:42 +0200 Subject: [PATCH 1/7] Add additional info that is reported to UnassignedJobReasonTracker The iteration number and the insertion data are sent to UnassignedJobReasonTracker when informing about unassigned job. With those information we can have better understanding when and why the job was rejected. --- .../recreate/AbstractInsertionStrategy.java | 4 +- .../algorithm/recreate/BestInsertion.java | 2 +- .../recreate/BestInsertionConcurrent.java | 8 ++- .../algorithm/recreate/RegretInsertion.java | 2 +- .../recreate/RegretInsertionConcurrent.java | 2 +- .../RegretInsertionConcurrentFast.java | 2 +- .../recreate/RegretInsertionFast.java | 2 +- .../recreate/listener/InsertionListeners.java | 4 +- .../listener/JobUnassignedListener.java | 5 +- .../core/util/UnassignedJobReasonTracker.java | 61 +++++++++++++++---- 10 files changed, 65 insertions(+), 27 deletions(-) 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..ccc974b9a 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 @@ -93,8 +93,8 @@ public Collection insertJobs(Collection vehicleRoutes, Collec return badJobs; } - public void markUnassigned(Job unassigned, List reasons) { - insertionsListeners.informJobUnassignedListeners(unassigned, reasons); + public void markUnassigned(Job unassigned, InsertionData insertionData) { + insertionsListeners.informJobUnassignedListeners(unassigned, insertionData); } public abstract Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs); 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..4907dedf1 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 @@ -94,7 +94,7 @@ public Collection insertUnassignedJobs(Collection vehicleRout } if (bestInsertion == null) { badJobs.add(unassignedJob); - markUnassigned(unassignedJob, empty.getFailedConstraintNames()); + markUnassigned(unassignedJob, empty); } else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); } 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..1ae83e898 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 @@ -104,7 +104,7 @@ public Collection insertUnassignedJobs(Collection vehicleRout Collections.shuffle(unassignedJobList, random); Collections.sort(unassignedJobList, new AccordingToPriorities()); List batches = distributeRoutes(vehicleRoutes, nuOfBatches); - List failedConstraintNames = new ArrayList<>(); + List failedInsertions = new ArrayList<>(); for (final Job unassignedJob : unassignedJobList) { Insertion bestInsertion = null; double bestInsertionCost = Double.MAX_VALUE; @@ -123,7 +123,7 @@ public Insertion call() throws Exception { Future futureIData = completionService.take(); Insertion insertion = futureIData.get(); if (insertion.insertionData instanceof NoInsertionFound) { - failedConstraintNames.addAll(insertion.getInsertionData().getFailedConstraintNames()); + failedInsertions.add(insertion.getInsertionData()); continue; } if (insertion.getInsertionData().getInsertionCost() < bestInsertionCost) { @@ -145,7 +145,9 @@ public Insertion call() throws Exception { } if (bestInsertion == null) { badJobs.add(unassignedJob); - markUnassigned(unassignedJob, failedConstraintNames); + for (InsertionData insertionData: failedInsertions) { + markUnassigned(unassignedJob, insertionData); + } } else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); } 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..c3005464a 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 @@ -121,7 +121,7 @@ public Collection insertUnassignedJobs(Collection routes, Col Job unassigned = bad.getJob(); jobs.remove(unassigned); badJobs.add(unassigned); - markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames()); + markUnassigned(unassigned, bad.getInsertionData()); } } return badJobs; 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..1d119281a 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 @@ -125,7 +125,7 @@ public Collection insertUnassignedJobs(Collection routes, Col Job unassigned = bad.getJob(); jobs.remove(unassigned); badJobs.add(unassigned); - markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames()); + markUnassigned(unassigned, bad.getInsertionData()); } } return badJobs; 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..b7c6f2870 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 @@ -163,7 +163,7 @@ public Collection insertUnassignedJobs(Collection routes, Col Job unassigned = bad.getJob(); jobs.remove(unassigned); badJobs.add(unassigned); - markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames()); + markUnassigned(unassigned, bad.getInsertionData()); } } return badJobs; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java index d805e55d9..c11182cb4 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java @@ -160,7 +160,7 @@ public Collection insertUnassignedJobs(Collection routes, Col Job unassigned = bad.getJob(); jobs.remove(unassigned); badJobs.add(unassigned); - markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames()); + markUnassigned(unassigned, bad.getInsertionData()); } } return badJobs; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java index f0c5e9a8e..aa1f244ff 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java @@ -75,10 +75,10 @@ public void informInsertionEndsListeners(Collection vehicleRoutes) } } - public void informJobUnassignedListeners(Job unassigned, List reasons) { + public void informJobUnassignedListeners(Job unassigned, InsertionData insertionData) { for (InsertionListener l : listeners) { if (l instanceof JobUnassignedListener) { - ((JobUnassignedListener) l).informJobUnassigned(unassigned, reasons); + ((JobUnassignedListener) l).informJobUnassigned(unassigned, insertionData); } } } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java index 55a2921db..687b32e36 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java @@ -18,15 +18,16 @@ package com.graphhopper.jsprit.core.algorithm.recreate.listener; +import com.graphhopper.jsprit.core.algorithm.recreate.InsertionData; import com.graphhopper.jsprit.core.problem.job.Job; -import java.util.Collection; +import java.util.List; /** * Created by schroeder on 06/02/17. */ public interface JobUnassignedListener extends InsertionListener { - void informJobUnassigned(Job unassigned, Collection failedConstraintNames); + void informJobUnassigned(Job unassigned, InsertionData insertionData); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java index 63dbf9388..83dd1eab7 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java @@ -18,8 +18,12 @@ package com.graphhopper.jsprit.core.util; +import com.graphhopper.jsprit.core.algorithm.listener.IterationStartsListener; +import com.graphhopper.jsprit.core.algorithm.recreate.InsertionData; import com.graphhopper.jsprit.core.algorithm.recreate.listener.JobUnassignedListener; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; import org.apache.commons.math3.stat.Frequency; import java.util.*; @@ -27,7 +31,7 @@ /** * Created by schroeder on 06/02/17. */ -public class UnassignedJobReasonTracker implements JobUnassignedListener { +public class UnassignedJobReasonTracker implements JobUnassignedListener, IterationStartsListener { public static String getMostLikelyFailedConstraintName(Frequency failedConstraintNamesFrequency) { if (failedConstraintNamesFrequency == null) return "no reason found"; @@ -44,7 +48,9 @@ public static String getMostLikelyFailedConstraintName(Frequency failedConstrain return mostLikely; } - Map failedConstraintNamesFrequencyMapping = new HashMap<>(); + private int iterationNumber; + + Map> failedConstraintNamesFrequencyMapping = new HashMap<>(); Map codesToHumanReadableReason = new HashMap<>(); @@ -64,6 +70,8 @@ public UnassignedJobReasonTracker() { failedConstraintNamesToCode.put("PickupAndDeliverShipmentLoadActivityLevelConstraint", 3); failedConstraintNamesToCode.put("ServiceLoadActivityLevelConstraint", 3); failedConstraintNamesToCode.put("MaxDistanceConstraint", 4); + + failedConstraintNamesFrequencyMapping.put(iterationNumber, new HashMap()); } public void ignore(String simpleNameOfConstraint) { @@ -71,13 +79,14 @@ public void ignore(String simpleNameOfConstraint) { } @Override - public void informJobUnassigned(Job unassigned, Collection failedConstraintNames) { - if (!this.failedConstraintNamesFrequencyMapping.containsKey(unassigned.getId())) { - this.failedConstraintNamesFrequencyMapping.put(unassigned.getId(), new Frequency()); + public void informJobUnassigned(Job unassigned, InsertionData insertionData) { + Map frequencyMap = failedConstraintNamesFrequencyMapping.get(iterationNumber); + if (!frequencyMap.containsKey(unassigned.getId())) { + frequencyMap.put(unassigned.getId(), new Frequency()); } - for (String r : failedConstraintNames) { + for (String r : insertionData.getFailedConstraintNames()) { if (failedConstraintNamesToBeIgnored.contains(r)) continue; - this.failedConstraintNamesFrequencyMapping.get(unassigned.getId()).addValue(r); + frequencyMap.get(unassigned.getId()).addValue(r); } } @@ -97,7 +106,7 @@ public void put(String simpleNameOfFailedConstraint, int code, String reason) { */ @Deprecated public Map getReasons() { - return Collections.unmodifiableMap(failedConstraintNamesFrequencyMapping); + return Collections.unmodifiableMap(aggregateFailedConstraintNamesFrequencyMapping()); } /** @@ -106,7 +115,27 @@ public Map getReasons() { * @return */ public Map getFailedConstraintNamesFrequencyMapping() { - return Collections.unmodifiableMap(failedConstraintNamesFrequencyMapping); + return Collections.unmodifiableMap(aggregateFailedConstraintNamesFrequencyMapping()); + } + + /** + * Aggregates the failed constraints from all iterations. + * + * @return + */ + protected Map aggregateFailedConstraintNamesFrequencyMapping() { + Map aggregatedMap = new HashMap<>(); + for (Map map : failedConstraintNamesFrequencyMapping.values()) { + for (Map.Entry frequencyEntry : map.entrySet()) { + String jobId = frequencyEntry.getKey(); + Frequency frequency = frequencyEntry.getValue(); + if (!aggregatedMap.containsKey(jobId)) { + aggregatedMap.put(jobId, new Frequency()); + } + aggregatedMap.get(jobId).merge(frequency); + } + } + return aggregatedMap; } /** @@ -151,8 +180,8 @@ public String getHumanReadableReason(String failedConstraintName) { * @return */ public int getMostLikelyReasonCode(String jobId) { - if (!this.failedConstraintNamesFrequencyMapping.containsKey(jobId)) return -1; - Frequency reasons = this.failedConstraintNamesFrequencyMapping.get(jobId); + Map frequencyMap = aggregateFailedConstraintNamesFrequencyMapping(); + Frequency reasons = frequencyMap.get(jobId); String mostLikelyReason = getMostLikelyFailedConstraintName(reasons); return toCode(mostLikelyReason); } @@ -164,8 +193,9 @@ public int getMostLikelyReasonCode(String jobId) { * @return */ public String getMostLikelyReason(String jobId) { - if (!this.failedConstraintNamesFrequencyMapping.containsKey(jobId)) return "no reason found"; - Frequency reasons = this.failedConstraintNamesFrequencyMapping.get(jobId); + Map frequencyMap = aggregateFailedConstraintNamesFrequencyMapping(); + if (!frequencyMap.containsKey(jobId)) return "no reason found"; + Frequency reasons = frequencyMap.get(jobId); String mostLikelyReason = getMostLikelyFailedConstraintName(reasons); int code = toCode(mostLikelyReason); if (code == -1) return mostLikelyReason; @@ -178,5 +208,10 @@ private int toCode(String mostLikelyReason) { else return -1; } + @Override + public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) { + iterationNumber = i; + failedConstraintNamesFrequencyMapping.put(iterationNumber, new HashMap()); + } } From 5a323509cf2d39b7814cca2935f10d95d93613a2 Mon Sep 17 00:00:00 2001 From: Toni Rajkovski Date: Fri, 21 Jul 2017 10:02:32 +0200 Subject: [PATCH 2/7] Add handling of InsertionData in UnassignedJobReasonTracker and Unit Tests for new methods --- .../core/util/UnassignedJobReasonTracker.java | 55 ++++-- .../util/UnassignedJobReasonTrackerTest.java | 176 +++++++++++++++++- 2 files changed, 213 insertions(+), 18 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java index 83dd1eab7..5c86a4130 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java @@ -50,7 +50,8 @@ public static String getMostLikelyFailedConstraintName(Frequency failedConstrain private int iterationNumber; - Map> failedConstraintNamesFrequencyMapping = new HashMap<>(); + // iterationNumber -> jobId -> list + Map>> failedInsertions = new HashMap<>(); Map codesToHumanReadableReason = new HashMap<>(); @@ -71,7 +72,7 @@ public UnassignedJobReasonTracker() { failedConstraintNamesToCode.put("ServiceLoadActivityLevelConstraint", 3); failedConstraintNamesToCode.put("MaxDistanceConstraint", 4); - failedConstraintNamesFrequencyMapping.put(iterationNumber, new HashMap()); + failedInsertions.put(iterationNumber, new HashMap>()); } public void ignore(String simpleNameOfConstraint) { @@ -80,14 +81,11 @@ public void ignore(String simpleNameOfConstraint) { @Override public void informJobUnassigned(Job unassigned, InsertionData insertionData) { - Map frequencyMap = failedConstraintNamesFrequencyMapping.get(iterationNumber); - if (!frequencyMap.containsKey(unassigned.getId())) { - frequencyMap.put(unassigned.getId(), new Frequency()); - } - for (String r : insertionData.getFailedConstraintNames()) { - if (failedConstraintNamesToBeIgnored.contains(r)) continue; - frequencyMap.get(unassigned.getId()).addValue(r); + Map> failedInsertionsInIteration = failedInsertions.get(iterationNumber); + if (!failedInsertionsInIteration.containsKey(unassigned.getId())) { + failedInsertionsInIteration.put(unassigned.getId(), new ArrayList()); } + failedInsertionsInIteration.get(unassigned.getId()).add(insertionData); } public void put(String simpleNameOfFailedConstraint, int code, String reason) { @@ -119,25 +117,47 @@ public Map getFailedConstraintNamesFrequencyMapping() { } /** - * Aggregates the failed constraints from all iterations. + * Aggregates the failed constraints from all insertion data from iterations. * * @return */ protected Map aggregateFailedConstraintNamesFrequencyMapping() { Map aggregatedMap = new HashMap<>(); - for (Map map : failedConstraintNamesFrequencyMapping.values()) { - for (Map.Entry frequencyEntry : map.entrySet()) { - String jobId = frequencyEntry.getKey(); - Frequency frequency = frequencyEntry.getValue(); + for (Map> jobInsertionsMap : failedInsertions.values()) { + for (Map.Entry> jobInsertionEntry : jobInsertionsMap.entrySet()) { + String jobId = jobInsertionEntry.getKey(); if (!aggregatedMap.containsKey(jobId)) { aggregatedMap.put(jobId, new Frequency()); } - aggregatedMap.get(jobId).merge(frequency); + for (InsertionData failedInsertion: jobInsertionEntry.getValue()) { + for (String failedConstraint: failedInsertion.getFailedConstraintNames()) { + if (!failedConstraintNamesToBeIgnored.contains(failedConstraint)) { + aggregatedMap.get(jobId).addValue(failedConstraint); + } + } + } } } return aggregatedMap; } + public Collection getFailedInsertionsForJob(String jobId) { + Collection result = new ArrayList<>(); + for (Map> jobInsertionMap: failedInsertions.values()) { + if (jobInsertionMap.containsKey(jobId)) { + result.addAll(jobInsertionMap.get(jobId)); + } + } + return result; + } + + public Collection getFailedInsertionsInIterationForJob(int iterationNumber, String jobId) { + if (failedInsertions.containsKey(iterationNumber) && failedInsertions.get(iterationNumber).containsKey(jobId)) { + return Collections.unmodifiableList(failedInsertions.get(iterationNumber).get(jobId)); + } + return Collections.unmodifiableList(new ArrayList()); + } + /** * Returns an unmodifiable map of codes and reason pairs. * @@ -209,9 +229,10 @@ private int toCode(String mostLikelyReason) { } @Override - public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) { + public void informIterationStarts(int i, VehicleRoutingProblem problem, + Collection solutions) { iterationNumber = i; - failedConstraintNamesFrequencyMapping.put(iterationNumber, new HashMap()); + failedInsertions.put(iterationNumber, new HashMap>()); } } diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java index aee8a95dc..e1590a57c 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java @@ -20,6 +20,7 @@ import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; import com.graphhopper.jsprit.core.algorithm.box.Jsprit; +import com.graphhopper.jsprit.core.algorithm.recreate.InsertionData; import com.graphhopper.jsprit.core.algorithm.state.StateId; import com.graphhopper.jsprit.core.algorithm.state.StateManager; import com.graphhopper.jsprit.core.problem.Location; @@ -27,6 +28,7 @@ import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; import com.graphhopper.jsprit.core.problem.constraint.MaxDistanceConstraint; import com.graphhopper.jsprit.core.problem.cost.TransportDistance; +import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.job.Service; import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; @@ -39,6 +41,7 @@ import org.junit.Before; import org.junit.Test; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -141,7 +144,175 @@ public double getDistance(Location from, Location to, double departureTime, Vehi } @Test - public void testFreq() { + public void shouldAggregateFailedConstraintNamesFrequencyMapping() { + // given + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + Job job = Service.Builder.newInstance("job").setLocation(Location.newInstance(0, 0)).build(); + + // iteration 0 + InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + insertion1.addFailedConstrainName("constraint1"); + insertion1.addFailedConstrainName("constraint2"); + reasonTracker.informIterationStarts(0, null, null); + reasonTracker.informJobUnassigned(job, insertion1); + + // iteration 1 + InsertionData insertion2 = new InsertionData(2, 2, 2, null,null); + insertion1.addFailedConstrainName("constraint2"); + insertion1.addFailedConstrainName("constraint3"); + reasonTracker.informIterationStarts(1, null, null); + reasonTracker.informJobUnassigned(job, insertion2); + + //when + Map frequencyMap = reasonTracker.aggregateFailedConstraintNamesFrequencyMapping(); + + //then + Assert.assertEquals(1, frequencyMap.get("job").getCount("constraint1")); + Assert.assertEquals(2, frequencyMap.get("job").getCount("constraint2")); + Assert.assertEquals(1, frequencyMap.get("job").getCount("constraint3")); + } + + @Test + public void shouldGetFailedInsertionsForJobWhenWhenFailedInsertionsAreAddedForJob() { + // given + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + Job job = Service.Builder.newInstance("job").setLocation(Location.newInstance(0, 0)).build(); + + // iteration1 + InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + reasonTracker.informIterationStarts(0, null, null); + reasonTracker.informJobUnassigned(job, insertion1); + + // iteration2 + InsertionData insertion2 = new InsertionData(2, 2, 2, null,null); + reasonTracker.informIterationStarts(2, null, null); + reasonTracker.informJobUnassigned(job, insertion2); + + //when + Collection failedInsertionsJob = reasonTracker.getFailedInsertionsForJob("job"); + Collection failedInsertionsNonExistentJob = reasonTracker.getFailedInsertionsForJob("non_existent"); + + //then + Assert.assertEquals(2, failedInsertionsJob.size()); + Assert.assertEquals(1, failedInsertionsJob.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); + Assert.assertEquals(2, failedInsertionsJob.toArray(new InsertionData[]{})[1].getPickupInsertionIndex()); + Assert.assertEquals(0, failedInsertionsNonExistentJob.size()); + } + + @Test + public void shouldGetFailedInsertionsForJobWhenDifferentInsertionsForDifferentJobsAreAdded() { + // given + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + Job job1 = Service.Builder.newInstance("job1").setLocation(Location.newInstance(0, 0)).build(); + Job job2 = Service.Builder.newInstance("job2").setLocation(Location.newInstance(0, 0)).build(); + + // iteration1 + InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + reasonTracker.informIterationStarts(0, null, null); + reasonTracker.informJobUnassigned(job1, insertion1); + + // iteration2 + InsertionData insertion2 = new InsertionData(2, 2, 2, null,null); + reasonTracker.informIterationStarts(2, null, null); + reasonTracker.informJobUnassigned(job2, insertion2); + + //when + Collection failedInsertionsJob1 = reasonTracker.getFailedInsertionsForJob("job1"); + Collection failedInsertionsJob2 = reasonTracker.getFailedInsertionsForJob("job2"); + + //then + Assert.assertEquals(1, failedInsertionsJob1.size()); + Assert.assertEquals(1, failedInsertionsJob2.size()); + Assert.assertEquals(1, failedInsertionsJob1.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); + Assert.assertEquals(2, failedInsertionsJob2.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); + } + + @Test + public void shouldGetFailedInsertionsForJobWhenNoFailedInsertions() { + // given + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + + //when + Collection failedInsertions = reasonTracker.getFailedInsertionsForJob("job1"); + + //then + Assert.assertEquals(0, failedInsertions.size()); + } + + @Test + public void shouldGetFailedInsertionsInIterationForJobWhenNoFailedInsertions() { + // given + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + + //when + Collection failedInsertions = reasonTracker.getFailedInsertionsInIterationForJob(1, "job1"); + + //then + Assert.assertEquals(0, failedInsertions.size()); + } + + @Test + public void shouldGetFailedInsertionsInIterationForJobWhenFailedInsertionsAddedInDifferentIterations() { + // given + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + Job job = Service.Builder.newInstance("job").setLocation(Location.newInstance(0, 0)).build(); + + // adding the failed insertion in iteration 1 + InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + reasonTracker.informIterationStarts(1, null, null); + reasonTracker.informJobUnassigned(job, insertion1); + + //when + Collection failedInsertionsIteration2 = reasonTracker.getFailedInsertionsInIterationForJob(2, "job"); + + //then + Assert.assertEquals(0, failedInsertionsIteration2.size()); + } + + @Test + public void shouldGetFailedInsertionsInIterationForJobWhenDifferentJobInsertionsAddedInDifferentIterations() { + // given + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + Job job1 = Service.Builder.newInstance("job1").setLocation(Location.newInstance(0, 0)).build(); + Job job2 = Service.Builder.newInstance("job2").setLocation(Location.newInstance(0, 0)).build(); + + // adding the failed insertion in iteration 1 for job1 + InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + reasonTracker.informIterationStarts(1, null, null); + reasonTracker.informJobUnassigned(job1, insertion1); + + // adding the failed insertion in iteration 2 for job2 + InsertionData insertion2 = new InsertionData(2, 2, 2, null,null); + reasonTracker.informIterationStarts(2, null, null); + reasonTracker.informJobUnassigned(job2, insertion2); + + //when + Collection failedInsertionsJob1Iteration1 = + reasonTracker.getFailedInsertionsInIterationForJob(1, "job1"); + Collection failedInsertionsJob1Iteration2 = + reasonTracker.getFailedInsertionsInIterationForJob(2, "job1"); + Collection failedInsertionsJob2Iteration1 = + reasonTracker.getFailedInsertionsInIterationForJob(1, "job2"); + Collection failedInsertionsJob2Iteration2 = + reasonTracker.getFailedInsertionsInIterationForJob(2, "job2"); + Collection failedInsertionsNonExistentJobIteration1 = + reasonTracker.getFailedInsertionsInIterationForJob(1, "non_existent"); + + //then + // job 1 + Assert.assertEquals(1, failedInsertionsJob1Iteration1.size()); + Assert.assertEquals(0, failedInsertionsJob1Iteration2.size()); + Assert.assertEquals(1, failedInsertionsJob1Iteration1.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); + // job 2 + Assert.assertEquals(0, failedInsertionsJob2Iteration1.size()); + Assert.assertEquals(1, failedInsertionsJob2Iteration2.size()); + Assert.assertEquals(2, failedInsertionsJob2Iteration2.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); + // non existent job + Assert.assertEquals(0, failedInsertionsNonExistentJobIteration1.size()); + } + + @Test + public void shouldFrequencyWork() { Frequency frequency = new Frequency(); frequency.addValue("VehicleDependentTimeWindowHardActivityConstraint"); frequency.addValue("b"); @@ -152,5 +323,8 @@ public void testFreq() { Map.Entry, Long> e = entryIterator.next(); System.out.println(e.getKey().toString() + " " + e.getValue()); } + + Assert.assertEquals(2, frequency.getCount("VehicleDependentTimeWindowHardActivityConstraint")); + Assert.assertEquals(1, frequency.getCount("b")); } } From 684fe11bcca47f5f463296acdea1e0f24b230973 Mon Sep 17 00:00:00 2001 From: Toni Rajkovski Date: Mon, 24 Jul 2017 08:32:39 +0200 Subject: [PATCH 3/7] Create new class FailedConstraintInfo that contain information about failed constraint FailedConstraintInfo contains the info which constraint has failed for which job and which vehicle route. This info is reported to UnassignedJobResonTracker. --- .../recreate/AbstractInsertionCalculator.java | 35 ++++- .../recreate/AbstractInsertionStrategy.java | 5 +- .../algorithm/recreate/BestInsertion.java | 6 +- .../recreate/BestInsertionConcurrent.java | 11 +- .../algorithm/recreate/InsertionData.java | 9 +- .../recreate/InsertionDataUpdater.java | 7 +- .../algorithm/recreate/RegretInsertion.java | 11 +- .../recreate/RegretInsertionConcurrent.java | 2 +- .../RegretInsertionConcurrentFast.java | 2 +- .../recreate/RegretInsertionFast.java | 2 +- .../core/algorithm/recreate/ScoredJob.java | 9 +- .../recreate/ServiceInsertionCalculator.java | 5 +- .../recreate/ShipmentInsertionCalculator.java | 5 +- ...leTypeDependentJobInsertionCalculator.java | 2 +- .../recreate/listener/InsertionListeners.java | 5 +- .../listener/JobUnassignedListener.java | 4 +- .../core/util/FailedConstraintInfo.java | 68 ++++++++ .../core/util/UnassignedJobReasonTracker.java | 53 +++---- .../util/UnassignedJobReasonTrackerTest.java | 146 +++++++++--------- 19 files changed, 241 insertions(+), 146 deletions(-) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java 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..45eb8b20a 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 @@ -24,6 +24,7 @@ import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint; import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import java.util.ArrayList; import java.util.Collection; @@ -38,24 +39,33 @@ InsertionData checkRouteContraints(JobInsertionContext insertionContext, Constra for (HardRouteConstraint hardRouteConstraint : constraintManager.getHardRouteConstraints()) { if (!hardRouteConstraint.fulfilled(insertionContext)) { InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); - emptyInsertionData.addFailedConstrainName(hardRouteConstraint.getClass().getSimpleName()); + emptyInsertionData.addFailedConstrainInfo(new FailedConstraintInfo( + hardRouteConstraint.getClass().getSimpleName(), + insertionContext + )); return emptyInsertionData; } } return null; } - ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime, Collection failedActivityConstraints, ConstraintManager constraintManager) { + ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime, Collection failedActivityConstraints, ConstraintManager constraintManager) { 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(new FailedConstraintInfo( + c.getClass().getSimpleName(), + iFacts + )); return status; } else { if (status.equals(ConstraintsStatus.NOT_FULFILLED)) { - failed.add(c.getClass().getSimpleName()); + failed.add(new FailedConstraintInfo( + c.getClass().getSimpleName(), + iFacts + )); notFulfilled = status; } } @@ -68,11 +78,17 @@ 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(new FailedConstraintInfo( + c.getClass().getSimpleName(), + iFacts + )); return status; } else { if (status.equals(ConstraintsStatus.NOT_FULFILLED)) { - failed.add(c.getClass().getSimpleName()); + failed.add(new FailedConstraintInfo( + c.getClass().getSimpleName(), + iFacts + )); notFulfilled = status; } } @@ -85,7 +101,10 @@ 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(new FailedConstraintInfo( + constraint.getClass().getSimpleName(), + iFacts + )); return status; } } 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 ccc974b9a..b50f55470 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 @@ -26,6 +26,7 @@ import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import com.graphhopper.jsprit.core.util.RandomNumberGeneration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -93,8 +94,8 @@ public Collection insertJobs(Collection vehicleRoutes, Collec return badJobs; } - public void markUnassigned(Job unassigned, InsertionData insertionData) { - insertionsListeners.informJobUnassignedListeners(unassigned, insertionData); + public void markUnassigned(Job unassigned, List failedConstraint) { + insertionsListeners.informJobUnassignedListeners(unassigned, failedConstraint); } public abstract Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs); 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 4907dedf1..c0aef2475 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 @@ -74,7 +74,7 @@ public Collection insertUnassignedJobs(Collection vehicleRout for (VehicleRoute vehicleRoute : vehicleRoutes) { InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost); if (iData instanceof InsertionData.NoInsertionFound) { - empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames()); + empty.getFailedConstraints().addAll(iData.getFailedConstraints()); continue; } if (iData.getInsertionCost() < bestInsertionCost + noiseMaker.makeNoise()) { @@ -90,11 +90,11 @@ public Collection insertUnassignedJobs(Collection vehicleRout vehicleRoutes.add(newRoute); } } else { - empty.getFailedConstraintNames().addAll(newIData.getFailedConstraintNames()); + empty.getFailedConstraints().addAll(newIData.getFailedConstraints()); } if (bestInsertion == null) { badJobs.add(unassignedJob); - markUnassigned(unassignedJob, empty); + markUnassigned(unassignedJob, empty.getFailedConstraints()); } else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); } 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 1ae83e898..9fb3ef89d 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 @@ -24,6 +24,7 @@ import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -104,7 +105,7 @@ public Collection insertUnassignedJobs(Collection vehicleRout Collections.shuffle(unassignedJobList, random); Collections.sort(unassignedJobList, new AccordingToPriorities()); List batches = distributeRoutes(vehicleRoutes, nuOfBatches); - List failedInsertions = new ArrayList<>(); + List failedConstraints = new ArrayList<>(); for (final Job unassignedJob : unassignedJobList) { Insertion bestInsertion = null; double bestInsertionCost = Double.MAX_VALUE; @@ -123,7 +124,7 @@ public Insertion call() throws Exception { Future futureIData = completionService.take(); Insertion insertion = futureIData.get(); if (insertion.insertionData instanceof NoInsertionFound) { - failedInsertions.add(insertion.getInsertionData()); + failedConstraints.addAll(insertion.getInsertionData().getFailedConstraints()); continue; } if (insertion.getInsertionData().getInsertionCost() < bestInsertionCost) { @@ -145,9 +146,7 @@ public Insertion call() throws Exception { } if (bestInsertion == null) { badJobs.add(unassignedJob); - for (InsertionData insertionData: failedInsertions) { - markUnassigned(unassignedJob, insertionData); - } + markUnassigned(unassignedJob, failedConstraints); } else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute()); } @@ -162,7 +161,7 @@ private Insertion getBestInsertion(Batch batch, Job unassignedJob) { 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()); + empty.getFailedConstraints().addAll(iData.getFailedConstraints()); continue; } if (iData.getInsertionCost() < bestInsertionCost) { 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..65ef4b8f8 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 @@ -19,6 +19,7 @@ import com.graphhopper.jsprit.core.problem.driver.Driver; import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import java.util.ArrayList; import java.util.List; @@ -76,7 +77,7 @@ List getEvents() { return events; } - private List reasons = new ArrayList<>(); + private List reasons = new ArrayList<>(); /** * @return the additionalTime @@ -85,11 +86,11 @@ public double getAdditionalTime() { return additionalTime; } - public void addFailedConstrainName(String name) { - reasons.add(name); + public void addFailedConstrainInfo(FailedConstraintInfo failedConstraintInfo) { + reasons.add(failedConstraintInfo); } - public List getFailedConstraintNames() { + public List getFailedConstraints() { return reasons; } 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..fd68f3a47 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 @@ -23,6 +23,7 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; import com.graphhopper.jsprit.core.problem.vehicle.VehicleFleetManager; import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import java.util.*; @@ -79,7 +80,7 @@ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, V InsertionData secondBest = null; TreeSet priorityQueue = priorityQueues[j.getIndex()]; Iterator iterator = priorityQueue.iterator(); - List failedConstraintNames = new ArrayList<>(); + List failedConstraintNames = new ArrayList<>(); while(iterator.hasNext()){ VersionedInsertionData versionedIData = iterator.next(); if(bestRoute != null){ @@ -88,7 +89,7 @@ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, V } } if (versionedIData.getiData() instanceof InsertionData.NoInsertionFound) { - failedConstraintNames.addAll(versionedIData.getiData().getFailedConstraintNames()); + failedConstraintNames.addAll(versionedIData.getiData().getFailedConstraints()); continue; } if(!(versionedIData.getRoute().getVehicle() instanceof VehicleImpl.NoVehicle)) { @@ -140,7 +141,7 @@ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, V } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) { secondBest = iData; } - } else failedConstraintNames.addAll(iData.getFailedConstraintNames()); + } else failedConstraintNames.addAll(iData.getFailedConstraints()); if (best == null) { badJobs.add(new ScoredJob.BadJob(j, failedConstraintNames)); continue; 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 c3005464a..09552e01c 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 @@ -22,6 +22,7 @@ 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.util.FailedConstraintInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -121,7 +122,7 @@ public Collection insertUnassignedJobs(Collection routes, Col Job unassigned = bad.getJob(); jobs.remove(unassigned); badJobs.add(unassigned); - markUnassigned(unassigned, bad.getInsertionData()); + markUnassigned(unassigned, bad.getInsertionData().getFailedConstraints()); } } return badJobs; @@ -160,7 +161,7 @@ static ScoredJob getScoredJob(Collection routes, Job unassignedJob InsertionData best = null; InsertionData secondBest = null; VehicleRoute bestRoute = null; - List failedConstraintNames = new ArrayList<>(); + List failedConstraints = new ArrayList<>(); double benchmark = Double.MAX_VALUE; for (VehicleRoute route : routes) { if (secondBest != null) { @@ -168,7 +169,7 @@ static ScoredJob getScoredJob(Collection routes, Job unassignedJob } 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()); + failedConstraints.addAll(iData.getFailedConstraints()); continue; } if (best == null) { @@ -196,9 +197,9 @@ static ScoredJob getScoredJob(Collection routes, Job unassignedJob } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) { secondBest = iData; } - } else failedConstraintNames.addAll(iData.getFailedConstraintNames()); + } else failedConstraints.addAll(iData.getFailedConstraints()); if (best == null) { - ScoredJob.BadJob badJob = new ScoredJob.BadJob(unassignedJob, failedConstraintNames); + ScoredJob.BadJob badJob = new ScoredJob.BadJob(unassignedJob, failedConstraints); return badJob; } double score = 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 1d119281a..506c6cc28 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 @@ -125,7 +125,7 @@ public Collection insertUnassignedJobs(Collection routes, Col Job unassigned = bad.getJob(); jobs.remove(unassigned); badJobs.add(unassigned); - markUnassigned(unassigned, bad.getInsertionData()); + markUnassigned(unassigned, bad.getInsertionData().getFailedConstraints()); } } return badJobs; 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 b7c6f2870..a4f57f349 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 @@ -163,7 +163,7 @@ public Collection insertUnassignedJobs(Collection routes, Col Job unassigned = bad.getJob(); jobs.remove(unassigned); badJobs.add(unassigned); - markUnassigned(unassigned, bad.getInsertionData()); + markUnassigned(unassigned, bad.getInsertionData().getFailedConstraints()); } } return badJobs; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java index c11182cb4..335a090c5 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RegretInsertionFast.java @@ -160,7 +160,7 @@ public Collection insertUnassignedJobs(Collection routes, Col Job unassigned = bad.getJob(); jobs.remove(unassigned); badJobs.add(unassigned); - markUnassigned(unassigned, bad.getInsertionData()); + markUnassigned(unassigned, bad.getInsertionData().getFailedConstraints()); } } return badJobs; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java index f0f8950fa..6245c6455 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ScoredJob.java @@ -20,6 +20,7 @@ import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import java.util.List; @@ -30,13 +31,13 @@ class ScoredJob { static class BadJob extends ScoredJob { - BadJob(Job job, List failedConstraintNames) { - super(job, 0., getEmptyInsertion(failedConstraintNames), null, false); + BadJob(Job job, List failedConstraints) { + super(job, 0., getEmptyInsertion(failedConstraints), null, false); } - private static InsertionData getEmptyInsertion(List failedConstraintNames) { + private static InsertionData getEmptyInsertion(List failedConstraints) { InsertionData empty = new InsertionData.NoInsertionFound(); - empty.getFailedConstraintNames().addAll(failedConstraintNames); + empty.getFailedConstraints().addAll(failedConstraints); return empty; } } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java index 9f91ff9bf..23848c47d 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java @@ -35,6 +35,7 @@ import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -111,7 +112,7 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager); if (noInsertion != null) return noInsertion; - Collection failedActivityConstraints = new ArrayList<>(); + Collection failedActivityConstraints = new ArrayList<>(); /* check soft constraints at route level @@ -170,7 +171,7 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job } if(insertionIndex == InsertionData.NO_INDEX) { InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); - emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints); + emptyInsertionData.getFailedConstraints().addAll(failedActivityConstraints); return emptyInsertionData; } InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java index e86984835..7dff4b11c 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculator.java @@ -35,6 +35,7 @@ import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -135,7 +136,7 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job //pickupShipmentLoop List activities = currentRoute.getTourActivities().getActivities(); - List failedActivityConstraints = new ArrayList<>(); + List failedActivityConstraints = new ArrayList<>(); while (!tourEnd) { TourActivity nextAct; if (i < activities.size()) { @@ -235,7 +236,7 @@ else if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) { } if (pickupInsertionIndex == InsertionData.NO_INDEX) { InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); - emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints); + emptyInsertionData.getFailedConstraints().addAll(failedActivityConstraints); return emptyInsertionData; } InsertionData insertionData = new InsertionData(bestCost, pickupInsertionIndex, deliveryInsertionIndex, newVehicle, newDriver); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java index ad522012e..f98434e35 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/VehicleTypeDependentJobInsertionCalculator.java @@ -115,7 +115,7 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job else depTime = v.getEarliestDeparture(); InsertionData iData = insertionCalculator.getInsertionData(currentRoute, jobToInsert, v, depTime, selectedDriver, bestKnownCost_); if (iData instanceof InsertionData.NoInsertionFound) { - bestIData.getFailedConstraintNames().addAll(iData.getFailedConstraintNames()); + bestIData.getFailedConstraints().addAll(iData.getFailedConstraints()); continue; } if (iData.getInsertionCost() < bestKnownCost_) { diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java index aa1f244ff..ad687831a 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/InsertionListeners.java @@ -21,6 +21,7 @@ import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import java.util.ArrayList; import java.util.Collection; @@ -75,10 +76,10 @@ public void informInsertionEndsListeners(Collection vehicleRoutes) } } - public void informJobUnassignedListeners(Job unassigned, InsertionData insertionData) { + public void informJobUnassignedListeners(Job unassigned, List failedConstraints) { for (InsertionListener l : listeners) { if (l instanceof JobUnassignedListener) { - ((JobUnassignedListener) l).informJobUnassigned(unassigned, insertionData); + ((JobUnassignedListener) l).informJobUnassigned(unassigned, failedConstraints); } } } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java index 687b32e36..eed4f9fb0 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java @@ -18,8 +18,8 @@ package com.graphhopper.jsprit.core.algorithm.recreate.listener; -import com.graphhopper.jsprit.core.algorithm.recreate.InsertionData; import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; import java.util.List; @@ -28,6 +28,6 @@ */ public interface JobUnassignedListener extends InsertionListener { - void informJobUnassigned(Job unassigned, InsertionData insertionData); + void informJobUnassigned(Job unassigned, List failedConstraintsInfo); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java new file mode 100644 index 000000000..c881b1104 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java @@ -0,0 +1,68 @@ +package com.graphhopper.jsprit.core.util; + +import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by tonirajkovski on 7/23/17. + */ +public class FailedConstraintInfo { + private String failedConstraint; + private String job; + private String vehicle; + private List activities = new ArrayList<>(); + private int insertionIndex; + + public FailedConstraintInfo(String failedConstraint, JobInsertionContext jobInsertionContext) { + this.failedConstraint = failedConstraint; + if (jobInsertionContext != null) { + this.job = jobInsertionContext.getJob().getId(); + this.vehicle = jobInsertionContext.getNewVehicle().getId(); + if (jobInsertionContext.getActivityContext() != null) { + this.insertionIndex = jobInsertionContext.getActivityContext().getInsertionIndex(); + } + if (jobInsertionContext.getRoute() != null && jobInsertionContext.getRoute().getActivities() != null) { + for (TourActivity activity: jobInsertionContext.getRoute().getTourActivities().getActivities()) { + if (activity instanceof TourActivity.JobActivity) { + activities.add(((TourActivity.JobActivity) activity).getJob().getId() + "-" + activity.getName()); + } + } + } + } + } + + public String getFailedConstraint() { + return failedConstraint; + } + + public String getVehicle() { + return vehicle; + } + + public int getInsertionIndex() { + return insertionIndex; + } + + public List getActivities() { + return activities; + } + + public String toString() { + StringBuilder route = new StringBuilder(); + route.append(vehicle).append(" [ "); + for (String activity: activities) { + route.append(activity).append(" "); + } + route.append("]"); + return String.format("Constraint '%s' failed for job insertion of job '%s' on position '%d' on route '%s'", + failedConstraint, + job, + insertionIndex, + route + ); + } +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java index 5c86a4130..abc4b2921 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java @@ -19,7 +19,6 @@ package com.graphhopper.jsprit.core.util; import com.graphhopper.jsprit.core.algorithm.listener.IterationStartsListener; -import com.graphhopper.jsprit.core.algorithm.recreate.InsertionData; import com.graphhopper.jsprit.core.algorithm.recreate.listener.JobUnassignedListener; import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; import com.graphhopper.jsprit.core.problem.job.Job; @@ -50,8 +49,8 @@ public static String getMostLikelyFailedConstraintName(Frequency failedConstrain private int iterationNumber; - // iterationNumber -> jobId -> list - Map>> failedInsertions = new HashMap<>(); + // iterationNumber -> jobId -> list + Map>> failedConstraints = new HashMap<>(); Map codesToHumanReadableReason = new HashMap<>(); @@ -72,7 +71,7 @@ public UnassignedJobReasonTracker() { failedConstraintNamesToCode.put("ServiceLoadActivityLevelConstraint", 3); failedConstraintNamesToCode.put("MaxDistanceConstraint", 4); - failedInsertions.put(iterationNumber, new HashMap>()); + failedConstraints.put(iterationNumber, new HashMap>()); } public void ignore(String simpleNameOfConstraint) { @@ -80,12 +79,12 @@ public void ignore(String simpleNameOfConstraint) { } @Override - public void informJobUnassigned(Job unassigned, InsertionData insertionData) { - Map> failedInsertionsInIteration = failedInsertions.get(iterationNumber); - if (!failedInsertionsInIteration.containsKey(unassigned.getId())) { - failedInsertionsInIteration.put(unassigned.getId(), new ArrayList()); + public void informJobUnassigned(Job unassigned, List failedConstraintInfo) { + Map> failedConstraintsInIteration = failedConstraints.get(iterationNumber); + if (!failedConstraintsInIteration.containsKey(unassigned.getId())) { + failedConstraintsInIteration.put(unassigned.getId(), new ArrayList()); } - failedInsertionsInIteration.get(unassigned.getId()).add(insertionData); + failedConstraintsInIteration.get(unassigned.getId()).addAll(failedConstraintInfo); } public void put(String simpleNameOfFailedConstraint, int code, String reason) { @@ -117,23 +116,21 @@ public Map getFailedConstraintNamesFrequencyMapping() { } /** - * Aggregates the failed constraints from all insertion data from iterations. + * Aggregates the failed constraints from all constraints data from iterations. * * @return */ protected Map aggregateFailedConstraintNamesFrequencyMapping() { Map aggregatedMap = new HashMap<>(); - for (Map> jobInsertionsMap : failedInsertions.values()) { - for (Map.Entry> jobInsertionEntry : jobInsertionsMap.entrySet()) { - String jobId = jobInsertionEntry.getKey(); + for (Map> jobFailedConstraintsMap : failedConstraints.values()) { + for (Map.Entry> jobFailedConstraintEntry : jobFailedConstraintsMap.entrySet()) { + String jobId = jobFailedConstraintEntry.getKey(); if (!aggregatedMap.containsKey(jobId)) { aggregatedMap.put(jobId, new Frequency()); } - for (InsertionData failedInsertion: jobInsertionEntry.getValue()) { - for (String failedConstraint: failedInsertion.getFailedConstraintNames()) { - if (!failedConstraintNamesToBeIgnored.contains(failedConstraint)) { - aggregatedMap.get(jobId).addValue(failedConstraint); - } + for (FailedConstraintInfo failedConstraintInfo: jobFailedConstraintEntry.getValue()) { + if (!failedConstraintNamesToBeIgnored.contains(failedConstraintInfo.getFailedConstraint())) { + aggregatedMap.get(jobId).addValue(failedConstraintInfo.getFailedConstraint()); } } } @@ -141,21 +138,21 @@ protected Map aggregateFailedConstraintNamesFrequencyMapping( return aggregatedMap; } - public Collection getFailedInsertionsForJob(String jobId) { - Collection result = new ArrayList<>(); - for (Map> jobInsertionMap: failedInsertions.values()) { - if (jobInsertionMap.containsKey(jobId)) { - result.addAll(jobInsertionMap.get(jobId)); + public Collection getFailedConstraintsForJob(String jobId) { + Collection result = new ArrayList<>(); + for (Map> failedConstraints: failedConstraints.values()) { + if (failedConstraints.containsKey(jobId)) { + result.addAll(failedConstraints.get(jobId)); } } return result; } - public Collection getFailedInsertionsInIterationForJob(int iterationNumber, String jobId) { - if (failedInsertions.containsKey(iterationNumber) && failedInsertions.get(iterationNumber).containsKey(jobId)) { - return Collections.unmodifiableList(failedInsertions.get(iterationNumber).get(jobId)); + public Collection getFailedConstraintsInIterationForJob(int iterationNumber, String jobId) { + if (failedConstraints.containsKey(iterationNumber) && failedConstraints.get(iterationNumber).containsKey(jobId)) { + return Collections.unmodifiableList(failedConstraints.get(iterationNumber).get(jobId)); } - return Collections.unmodifiableList(new ArrayList()); + return Collections.unmodifiableList(new ArrayList()); } /** @@ -232,7 +229,7 @@ private int toCode(String mostLikelyReason) { public void informIterationStarts(int i, VehicleRoutingProblem problem, Collection solutions) { iterationNumber = i; - failedInsertions.put(iterationNumber, new HashMap>()); + failedConstraints.put(iterationNumber, new HashMap>()); } } diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java index e1590a57c..d0996c754 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java @@ -20,7 +20,6 @@ import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; import com.graphhopper.jsprit.core.algorithm.box.Jsprit; -import com.graphhopper.jsprit.core.algorithm.recreate.InsertionData; import com.graphhopper.jsprit.core.algorithm.state.StateId; import com.graphhopper.jsprit.core.algorithm.state.StateManager; import com.graphhopper.jsprit.core.problem.Location; @@ -41,10 +40,7 @@ import org.junit.Before; import org.junit.Test; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; +import java.util.*; /** * Created by schroeder on 06/02/17. @@ -150,18 +146,18 @@ public void shouldAggregateFailedConstraintNamesFrequencyMapping() { Job job = Service.Builder.newInstance("job").setLocation(Location.newInstance(0, 0)).build(); // iteration 0 - InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); - insertion1.addFailedConstrainName("constraint1"); - insertion1.addFailedConstrainName("constraint2"); + List failedConstraints = new ArrayList<>(); + failedConstraints.add(new FailedConstraintInfo("constraint1", null)); + failedConstraints.add(new FailedConstraintInfo("constraint2", null)); reasonTracker.informIterationStarts(0, null, null); - reasonTracker.informJobUnassigned(job, insertion1); + reasonTracker.informJobUnassigned(job, failedConstraints); // iteration 1 - InsertionData insertion2 = new InsertionData(2, 2, 2, null,null); - insertion1.addFailedConstrainName("constraint2"); - insertion1.addFailedConstrainName("constraint3"); + failedConstraints.clear(); + failedConstraints.add(new FailedConstraintInfo("constraint2", null)); + failedConstraints.add(new FailedConstraintInfo("constraint3", null)); reasonTracker.informIterationStarts(1, null, null); - reasonTracker.informJobUnassigned(job, insertion2); + reasonTracker.informJobUnassigned(job, failedConstraints); //when Map frequencyMap = reasonTracker.aggregateFailedConstraintNamesFrequencyMapping(); @@ -173,142 +169,150 @@ public void shouldAggregateFailedConstraintNamesFrequencyMapping() { } @Test - public void shouldGetFailedInsertionsForJobWhenWhenFailedInsertionsAreAddedForJob() { + public void shouldGetFailedConstraintsForJobWhenWhenFailedConstraintsAreAddedForJob() { // given UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); Job job = Service.Builder.newInstance("job").setLocation(Location.newInstance(0, 0)).build(); // iteration1 - InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + List failedConstraints = new ArrayList<>(); + failedConstraints.add(new FailedConstraintInfo("c1", null)); reasonTracker.informIterationStarts(0, null, null); - reasonTracker.informJobUnassigned(job, insertion1); + reasonTracker.informJobUnassigned(job, failedConstraints); // iteration2 - InsertionData insertion2 = new InsertionData(2, 2, 2, null,null); + failedConstraints.clear(); + failedConstraints.add(new FailedConstraintInfo("c2", null)); reasonTracker.informIterationStarts(2, null, null); - reasonTracker.informJobUnassigned(job, insertion2); + reasonTracker.informJobUnassigned(job, failedConstraints); //when - Collection failedInsertionsJob = reasonTracker.getFailedInsertionsForJob("job"); - Collection failedInsertionsNonExistentJob = reasonTracker.getFailedInsertionsForJob("non_existent"); + Collection failedConstraintsForJob = reasonTracker.getFailedConstraintsForJob("job"); + Collection failedConstraintsNonExistentJob = reasonTracker.getFailedConstraintsForJob("non_existent"); //then - Assert.assertEquals(2, failedInsertionsJob.size()); - Assert.assertEquals(1, failedInsertionsJob.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); - Assert.assertEquals(2, failedInsertionsJob.toArray(new InsertionData[]{})[1].getPickupInsertionIndex()); - Assert.assertEquals(0, failedInsertionsNonExistentJob.size()); + Assert.assertEquals(2, failedConstraintsForJob.size()); + Assert.assertEquals("c1", failedConstraintsForJob.toArray(new FailedConstraintInfo[]{})[0].getFailedConstraint()); + Assert.assertEquals("c2", failedConstraintsForJob.toArray(new FailedConstraintInfo[]{})[1].getFailedConstraint()); + Assert.assertEquals(0, failedConstraintsNonExistentJob.size()); } @Test - public void shouldGetFailedInsertionsForJobWhenDifferentInsertionsForDifferentJobsAreAdded() { + public void shouldGetFailedConstraintsForJobWhenDifferentConstraintsForDifferentJobsAreAdded() { // given UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); Job job1 = Service.Builder.newInstance("job1").setLocation(Location.newInstance(0, 0)).build(); Job job2 = Service.Builder.newInstance("job2").setLocation(Location.newInstance(0, 0)).build(); + List failedConstraints = new ArrayList<>(); + // iteration1 - InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + failedConstraints.add(new FailedConstraintInfo("c1", null)); reasonTracker.informIterationStarts(0, null, null); - reasonTracker.informJobUnassigned(job1, insertion1); + reasonTracker.informJobUnassigned(job1, failedConstraints); // iteration2 - InsertionData insertion2 = new InsertionData(2, 2, 2, null,null); + failedConstraints.clear(); + failedConstraints.add(new FailedConstraintInfo("c2", null)); reasonTracker.informIterationStarts(2, null, null); - reasonTracker.informJobUnassigned(job2, insertion2); + reasonTracker.informJobUnassigned(job2, failedConstraints); //when - Collection failedInsertionsJob1 = reasonTracker.getFailedInsertionsForJob("job1"); - Collection failedInsertionsJob2 = reasonTracker.getFailedInsertionsForJob("job2"); + Collection failedConstraintsJob1 = reasonTracker.getFailedConstraintsForJob("job1"); + Collection failedConstraintsJob2 = reasonTracker.getFailedConstraintsForJob("job2"); //then - Assert.assertEquals(1, failedInsertionsJob1.size()); - Assert.assertEquals(1, failedInsertionsJob2.size()); - Assert.assertEquals(1, failedInsertionsJob1.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); - Assert.assertEquals(2, failedInsertionsJob2.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); + Assert.assertEquals(1, failedConstraintsJob1.size()); + Assert.assertEquals(1, failedConstraintsJob2.size()); + Assert.assertEquals("c1", failedConstraintsJob1.toArray(new FailedConstraintInfo[]{})[0].getFailedConstraint()); + Assert.assertEquals("c2", failedConstraintsJob2.toArray(new FailedConstraintInfo[]{})[0].getFailedConstraint()); } @Test - public void shouldGetFailedInsertionsForJobWhenNoFailedInsertions() { + public void shouldGetFailedConstraintsForJobWhenNoFailedConstraints() { // given UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); //when - Collection failedInsertions = reasonTracker.getFailedInsertionsForJob("job1"); + Collection failedConstraintsForJob = reasonTracker.getFailedConstraintsForJob("job1"); //then - Assert.assertEquals(0, failedInsertions.size()); + Assert.assertEquals(0, failedConstraintsForJob.size()); } @Test - public void shouldGetFailedInsertionsInIterationForJobWhenNoFailedInsertions() { + public void shouldGetFailedConstraintsInIterationForJobWhenNoFailedConstraints() { // given UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); //when - Collection failedInsertions = reasonTracker.getFailedInsertionsInIterationForJob(1, "job1"); + Collection failedConstraintsForJob = reasonTracker.getFailedConstraintsInIterationForJob(1, "job1"); //then - Assert.assertEquals(0, failedInsertions.size()); + Assert.assertEquals(0, failedConstraintsForJob.size()); } @Test - public void shouldGetFailedInsertionsInIterationForJobWhenFailedInsertionsAddedInDifferentIterations() { + public void shouldGetFailedConstraintsInIterationForJobWhenFailedConstraintsAddedInDifferentIterations() { // given UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); Job job = Service.Builder.newInstance("job").setLocation(Location.newInstance(0, 0)).build(); - // adding the failed insertion in iteration 1 - InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + List failedConstraints = new ArrayList<>(); + // adding the failed constraint in iteration 1 + failedConstraints.add(new FailedConstraintInfo("c1", null)); reasonTracker.informIterationStarts(1, null, null); - reasonTracker.informJobUnassigned(job, insertion1); + reasonTracker.informJobUnassigned(job, failedConstraints); //when - Collection failedInsertionsIteration2 = reasonTracker.getFailedInsertionsInIterationForJob(2, "job"); + Collection failedConstraintsIteration2 = reasonTracker.getFailedConstraintsInIterationForJob(2, "job"); //then - Assert.assertEquals(0, failedInsertionsIteration2.size()); + Assert.assertEquals(0, failedConstraintsIteration2.size()); } @Test - public void shouldGetFailedInsertionsInIterationForJobWhenDifferentJobInsertionsAddedInDifferentIterations() { + public void shouldGetFailedConstraintsInIterationForJobWhenDifferentJobConstraintsAddedInDifferentIterations() { // given UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); Job job1 = Service.Builder.newInstance("job1").setLocation(Location.newInstance(0, 0)).build(); Job job2 = Service.Builder.newInstance("job2").setLocation(Location.newInstance(0, 0)).build(); - // adding the failed insertion in iteration 1 for job1 - InsertionData insertion1 = new InsertionData(1, 1, 1, null,null); + List failedConstraints = new ArrayList<>(); + // adding the failed constraint in iteration 1 for job1 + failedConstraints.add(new FailedConstraintInfo("c1", null)); reasonTracker.informIterationStarts(1, null, null); - reasonTracker.informJobUnassigned(job1, insertion1); + reasonTracker.informJobUnassigned(job1, failedConstraints); - // adding the failed insertion in iteration 2 for job2 - InsertionData insertion2 = new InsertionData(2, 2, 2, null,null); + // adding the failed constraint in iteration 2 for job2 + failedConstraints.clear(); + failedConstraints.add(new FailedConstraintInfo("c2", null)); reasonTracker.informIterationStarts(2, null, null); - reasonTracker.informJobUnassigned(job2, insertion2); + reasonTracker.informJobUnassigned(job2, failedConstraints); //when - Collection failedInsertionsJob1Iteration1 = - reasonTracker.getFailedInsertionsInIterationForJob(1, "job1"); - Collection failedInsertionsJob1Iteration2 = - reasonTracker.getFailedInsertionsInIterationForJob(2, "job1"); - Collection failedInsertionsJob2Iteration1 = - reasonTracker.getFailedInsertionsInIterationForJob(1, "job2"); - Collection failedInsertionsJob2Iteration2 = - reasonTracker.getFailedInsertionsInIterationForJob(2, "job2"); - Collection failedInsertionsNonExistentJobIteration1 = - reasonTracker.getFailedInsertionsInIterationForJob(1, "non_existent"); + Collection failedConstraintsJob1Iteration1 = + reasonTracker.getFailedConstraintsInIterationForJob(1, "job1"); + Collection failedConstraintsJob1Iteration2 = + reasonTracker.getFailedConstraintsInIterationForJob(2, "job1"); + Collection failedConstraintsJob2Iteration1 = + reasonTracker.getFailedConstraintsInIterationForJob(1, "job2"); + Collection failedConstraintsJob2Iteration2 = + reasonTracker.getFailedConstraintsInIterationForJob(2, "job2"); + Collection failedConstraintsNonExistentJobIteration1 = + reasonTracker.getFailedConstraintsInIterationForJob(1, "non_existent"); //then // job 1 - Assert.assertEquals(1, failedInsertionsJob1Iteration1.size()); - Assert.assertEquals(0, failedInsertionsJob1Iteration2.size()); - Assert.assertEquals(1, failedInsertionsJob1Iteration1.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); + Assert.assertEquals(1, failedConstraintsJob1Iteration1.size()); + Assert.assertEquals(0, failedConstraintsJob1Iteration2.size()); + Assert.assertEquals("c1", failedConstraintsJob1Iteration1.toArray(new FailedConstraintInfo[]{})[0].getFailedConstraint()); // job 2 - Assert.assertEquals(0, failedInsertionsJob2Iteration1.size()); - Assert.assertEquals(1, failedInsertionsJob2Iteration2.size()); - Assert.assertEquals(2, failedInsertionsJob2Iteration2.toArray(new InsertionData[]{})[0].getPickupInsertionIndex()); + Assert.assertEquals(0, failedConstraintsJob2Iteration1.size()); + Assert.assertEquals(1, failedConstraintsJob2Iteration2.size()); + Assert.assertEquals("c2", failedConstraintsJob2Iteration2.toArray(new FailedConstraintInfo[]{})[0].getFailedConstraint()); // non existent job - Assert.assertEquals(0, failedInsertionsNonExistentJobIteration1.size()); + Assert.assertEquals(0, failedConstraintsNonExistentJobIteration1.size()); } @Test From 4ceab89adb2c14c61769bf9c9661568c56576206 Mon Sep 17 00:00:00 2001 From: Toni Rajkovski Date: Mon, 24 Jul 2017 08:33:07 +0200 Subject: [PATCH 4/7] Create example how can UnassignedJobReasonTracker be used --- .../UnassignedJobReasonTrackerExample.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/UnassignedJobReasonTrackerExample.java diff --git a/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/UnassignedJobReasonTrackerExample.java b/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/UnassignedJobReasonTrackerExample.java new file mode 100644 index 000000000..267ca994a --- /dev/null +++ b/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/UnassignedJobReasonTrackerExample.java @@ -0,0 +1,111 @@ +package com.graphhopper.jsprit.examples; + +import com.graphhopper.jsprit.analysis.toolbox.GraphStreamViewer; +import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; +import com.graphhopper.jsprit.core.algorithm.box.Jsprit; +import com.graphhopper.jsprit.core.algorithm.state.StateManager; +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint; +import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.job.Shipment; +import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; +import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl.Builder; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleType; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; +import com.graphhopper.jsprit.core.util.FailedConstraintInfo; +import com.graphhopper.jsprit.core.util.Solutions; +import com.graphhopper.jsprit.core.util.UnassignedJobReasonTracker; + +import java.util.Collection; + +public class UnassignedJobReasonTrackerExample { + + + public static void main(String[] args) { + final int WEIGHT_INDEX = 0; + VehicleType vehicleType1 = VehicleTypeImpl.Builder.newInstance("vehicleType1").addCapacityDimension(WEIGHT_INDEX, 2).build(); + VehicleType vehicleType2 = VehicleTypeImpl.Builder.newInstance("vehicleType2").addCapacityDimension(WEIGHT_INDEX, 4).build(); + + // vehicles + VehicleImpl v1 = Builder.newInstance("vehicle1").setStartLocation(Location.newInstance(0, 0)).setType(vehicleType1).build(); + VehicleImpl v2 = Builder.newInstance("vehicle2").setStartLocation(Location.newInstance(0, 10)).setType(vehicleType2).build(); + + // jobs + Shipment shipment1 = Shipment.Builder.newInstance("shipment1").addSizeDimension(WEIGHT_INDEX, 1) + .setPickupLocation(Location.newInstance(1, 0)) + .setDeliveryLocation(Location.newInstance(3, 0)) + .setPickupTimeWindow(new TimeWindow(0, 3)) + .setDeliveryTimeWindow(new TimeWindow(2, 7)) + .build(); + Shipment shipment2 = Shipment.Builder.newInstance("shipment2").addSizeDimension(WEIGHT_INDEX, 1) + .setPickupLocation(Location.newInstance(1, 10)) + .setDeliveryLocation(Location.newInstance(3, 10)) + .setPickupTimeWindow(new TimeWindow(1, 3)) + .setDeliveryTimeWindow(new TimeWindow(3, 7)) + .build(); + + // shipment3 requires more capacity so v1 is not be able to fulfill it, and v2 is too far away and it cannot fit into time windows + Shipment shipment3 = Shipment.Builder.newInstance("shipment3").addSizeDimension(WEIGHT_INDEX, 3) + .setPickupLocation(Location.newInstance(10, 0)) + .setDeliveryLocation(Location.newInstance(15, 0)) + .setPickupTimeWindow(new TimeWindow(10, 15)) + .setDeliveryTimeWindow(new TimeWindow(15, 16)) + .build(); + + // shipment4 is too far away from v1 so the time windows will fail, and for v2 the custom constraint will fail + Shipment shipment4 = Shipment.Builder.newInstance("shipment4").addSizeDimension(WEIGHT_INDEX, 1) + .setPickupLocation(Location.newInstance(10, 10)) + .setDeliveryLocation(Location.newInstance(15, 10)) + .setPickupTimeWindow(new TimeWindow(10, 15)) + .setDeliveryTimeWindow(new TimeWindow(15, 16)) + .build(); + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + vrpBuilder.addVehicle(v1).addVehicle(v2); + vrpBuilder.addJob(shipment1).addJob(shipment2).addJob(shipment3).addJob(shipment4); + + // prepare the algorithm + VehicleRoutingProblem problem = vrpBuilder.build(); + StateManager stateManager = new StateManager(problem); + + ConstraintManager constraintManager = new ConstraintManager(problem, stateManager); + DummyConstraint dummyConstraint = new DummyConstraint(); + constraintManager.addConstraint(dummyConstraint); + VehicleRoutingAlgorithm algorithm = Jsprit.Builder.newInstance(problem) + .setStateAndConstraintManager(stateManager, constraintManager) + .setProperty(Jsprit.Parameter.VEHICLE_SWITCH, "false") // needed so that constraint works + .buildAlgorithm(); + algorithm.setMaxIterations(10); + + // set the tracker + UnassignedJobReasonTracker tracker = new UnassignedJobReasonTracker(); + tracker.put("DummyConstraint", 50, "prevent shipment4 in vehicle2"); + algorithm.addListener(tracker); + + // run the algorithm' + Collection solutions = algorithm.searchSolutions(); + VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions); + + for (Job unassignedJob: bestSolution.getUnassignedJobs()) { + Collection failedConstraints = tracker.getFailedConstraintsForJob(unassignedJob.getId()); + for (FailedConstraintInfo failedConstraint : failedConstraints) { + System.out.println(failedConstraint.toString()); + } + } + + new GraphStreamViewer(problem, bestSolution).labelWith(GraphStreamViewer.Label.ID).setRenderDelay(200).display(); + } + + static class DummyConstraint implements HardRouteConstraint { + @Override + public boolean fulfilled(JobInsertionContext insertionContext) { + return !(insertionContext.getJob().getId().equals("shipment4") && insertionContext.getNewVehicle().getId().equals("vehicle2")); + } + } + +} From dad959c96ca85330791ef652dc5a86611cc410b2 Mon Sep 17 00:00:00 2001 From: Toni Rajkovski Date: Mon, 24 Jul 2017 12:55:48 +0200 Subject: [PATCH 5/7] Small refactor and added unit tests for FailedConstraintInfo --- .../recreate/AbstractInsertionCalculator.java | 49 +++++----- .../recreate/AbstractInsertionStrategy.java | 4 +- .../recreate/InsertionDataUpdater.java | 8 +- .../listener/JobUnassignedListener.java | 4 +- .../core/util/FailedConstraintInfo.java | 79 ++++++++++++---- .../core/util/UnassignedJobReasonTracker.java | 4 +- .../core/util/FailedConstraintInfoTest.java | 89 +++++++++++++++++++ .../util/UnassignedJobReasonTrackerTest.java | 22 ++--- .../UnassignedJobReasonTrackerExample.java | 8 +- 9 files changed, 203 insertions(+), 64 deletions(-) create mode 100644 jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FailedConstraintInfoTest.java 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 45eb8b20a..7cc618452 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 @@ -39,10 +39,11 @@ InsertionData checkRouteContraints(JobInsertionContext insertionContext, Constra for (HardRouteConstraint hardRouteConstraint : constraintManager.getHardRouteConstraints()) { if (!hardRouteConstraint.fulfilled(insertionContext)) { InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); - emptyInsertionData.addFailedConstrainInfo(new FailedConstraintInfo( - hardRouteConstraint.getClass().getSimpleName(), - insertionContext - )); + emptyInsertionData.addFailedConstrainInfo(FailedConstraintInfo.Builder.newInstance() + .setFailedConstraint(hardRouteConstraint.getClass().getSimpleName()) + .loadInsertionContextData(insertionContext) + .build() + ); return emptyInsertionData; } } @@ -55,17 +56,17 @@ ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, To for (HardActivityConstraint c : constraintManager.getCriticalHardActivityConstraints()) { ConstraintsStatus status = c.fulfilled(iFacts, prevAct, newAct, nextAct, prevActDepTime); if (status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) { - failedActivityConstraints.add(new FailedConstraintInfo( - c.getClass().getSimpleName(), - iFacts - )); + failedActivityConstraints.add(FailedConstraintInfo.Builder.newInstance() + .setFailedConstraint(c.getClass().getSimpleName()) + .loadInsertionContextData(iFacts).build() + ); return status; } else { if (status.equals(ConstraintsStatus.NOT_FULFILLED)) { - failed.add(new FailedConstraintInfo( - c.getClass().getSimpleName(), - iFacts - )); + failed.add(FailedConstraintInfo.Builder.newInstance() + .setFailedConstraint(c.getClass().getSimpleName()) + .loadInsertionContextData(iFacts).build() + ); notFulfilled = status; } } @@ -78,17 +79,17 @@ 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(new FailedConstraintInfo( - c.getClass().getSimpleName(), - iFacts - )); + failedActivityConstraints.add(FailedConstraintInfo.Builder.newInstance() + .setFailedConstraint(c.getClass().getSimpleName()) + .loadInsertionContextData(iFacts).build() + ); return status; } else { if (status.equals(ConstraintsStatus.NOT_FULFILLED)) { - failed.add(new FailedConstraintInfo( - c.getClass().getSimpleName(), - iFacts - )); + failed.add(FailedConstraintInfo.Builder.newInstance() + .setFailedConstraint(c.getClass().getSimpleName()) + .loadInsertionContextData(iFacts).build() + ); notFulfilled = status; } } @@ -101,10 +102,10 @@ 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(new FailedConstraintInfo( - constraint.getClass().getSimpleName(), - iFacts - )); + failedActivityConstraints.add(FailedConstraintInfo.Builder.newInstance() + .setFailedConstraint(constraint.getClass().getSimpleName()) + .loadInsertionContextData(iFacts).build() + ); return status; } } 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 b50f55470..585969b50 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 @@ -94,8 +94,8 @@ public Collection insertJobs(Collection vehicleRoutes, Collec return badJobs; } - public void markUnassigned(Job unassigned, List failedConstraint) { - insertionsListeners.informJobUnassignedListeners(unassigned, failedConstraint); + public void markUnassigned(Job unassigned, List failedConstraints) { + insertionsListeners.informJobUnassignedListeners(unassigned, failedConstraints); } public abstract Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs); 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 fd68f3a47..cc74d6e90 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 @@ -80,7 +80,7 @@ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, V InsertionData secondBest = null; TreeSet priorityQueue = priorityQueues[j.getIndex()]; Iterator iterator = priorityQueue.iterator(); - List failedConstraintNames = new ArrayList<>(); + List failedConstraints = new ArrayList<>(); while(iterator.hasNext()){ VersionedInsertionData versionedIData = iterator.next(); if(bestRoute != null){ @@ -89,7 +89,7 @@ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, V } } if (versionedIData.getiData() instanceof InsertionData.NoInsertionFound) { - failedConstraintNames.addAll(versionedIData.getiData().getFailedConstraints()); + failedConstraints.addAll(versionedIData.getiData().getFailedConstraints()); continue; } if(!(versionedIData.getRoute().getVehicle() instanceof VehicleImpl.NoVehicle)) { @@ -141,9 +141,9 @@ static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, V } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) { secondBest = iData; } - } else failedConstraintNames.addAll(iData.getFailedConstraints()); + } else failedConstraints.addAll(iData.getFailedConstraints()); if (best == null) { - badJobs.add(new ScoredJob.BadJob(j, failedConstraintNames)); + badJobs.add(new ScoredJob.BadJob(j, failedConstraints)); continue; } double score = score(j, best, secondBest, scoringFunction); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java index eed4f9fb0..8c749fc72 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java @@ -21,13 +21,13 @@ import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.util.FailedConstraintInfo; -import java.util.List; +import java.util.Collection; /** * Created by schroeder on 06/02/17. */ public interface JobUnassignedListener extends InsertionListener { - void informJobUnassigned(Job unassigned, List failedConstraintsInfo); + void informJobUnassigned(Job unassigned, Collection failedConstraints); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java index c881b1104..487914535 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java @@ -1,6 +1,7 @@ package com.graphhopper.jsprit.core.util; import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.job.Service; import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; @@ -11,30 +12,68 @@ * Created by tonirajkovski on 7/23/17. */ public class FailedConstraintInfo { - private String failedConstraint; - private String job; - private String vehicle; - private List activities = new ArrayList<>(); - private int insertionIndex; - - public FailedConstraintInfo(String failedConstraint, JobInsertionContext jobInsertionContext) { - this.failedConstraint = failedConstraint; - if (jobInsertionContext != null) { - this.job = jobInsertionContext.getJob().getId(); - this.vehicle = jobInsertionContext.getNewVehicle().getId(); - if (jobInsertionContext.getActivityContext() != null) { - this.insertionIndex = jobInsertionContext.getActivityContext().getInsertionIndex(); - } - if (jobInsertionContext.getRoute() != null && jobInsertionContext.getRoute().getActivities() != null) { - for (TourActivity activity: jobInsertionContext.getRoute().getTourActivities().getActivities()) { - if (activity instanceof TourActivity.JobActivity) { - activities.add(((TourActivity.JobActivity) activity).getJob().getId() + "-" + activity.getName()); + + public static class Builder { + + private String failedConstraint; + private String job; + private String vehicle; + private List activities = new ArrayList<>(); + private int insertionIndex; + + public static FailedConstraintInfo.Builder newInstance() { + return new FailedConstraintInfo.Builder(); + } + + /** + * Builds the Failed Constraint info. + * + * @return {@link Service} + * @throws IllegalArgumentException if neither locationId nor coordinate is set. + */ + public T build() { + if (failedConstraint == null) throw new IllegalArgumentException("failed constraint is missing"); + return (T) new FailedConstraintInfo(this); + } + + public FailedConstraintInfo.Builder setFailedConstraint(String failedConstraint) { + this.failedConstraint = failedConstraint; + return this; + } + + public FailedConstraintInfo.Builder loadInsertionContextData(JobInsertionContext insertionContext) { + if (insertionContext != null) { + this.job = insertionContext.getJob().getId(); + this.vehicle = insertionContext.getNewVehicle().getId(); + if (insertionContext.getActivityContext() != null) { + this.insertionIndex = insertionContext.getActivityContext().getInsertionIndex(); + } + if (insertionContext.getRoute() != null && insertionContext.getRoute().getActivities() != null) { + for (TourActivity activity: insertionContext.getRoute().getTourActivities().getActivities()) { + if (activity instanceof TourActivity.JobActivity) { + activities.add(((TourActivity.JobActivity) activity).getJob().getId() + "-" + activity.getName()); + } } } } + return this; } } + private String failedConstraint; + private String job; + private String vehicle; + private List activities = new ArrayList<>(); + private int insertionIndex = -1; + + private FailedConstraintInfo(Builder builder) { + this.failedConstraint = builder.failedConstraint; + this.job = builder.job; + this.vehicle = builder.vehicle; + this.activities = builder.activities; + this.insertionIndex = builder.insertionIndex; + } + public String getFailedConstraint() { return failedConstraint; } @@ -51,6 +90,10 @@ public List getActivities() { return activities; } + public String getJob() { + return job; + } + public String toString() { StringBuilder route = new StringBuilder(); route.append(vehicle).append(" [ "); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java index abc4b2921..d6708021b 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java @@ -79,12 +79,12 @@ public void ignore(String simpleNameOfConstraint) { } @Override - public void informJobUnassigned(Job unassigned, List failedConstraintInfo) { + public void informJobUnassigned(Job unassigned, Collection failedConstraintsInfo) { Map> failedConstraintsInIteration = failedConstraints.get(iterationNumber); if (!failedConstraintsInIteration.containsKey(unassigned.getId())) { failedConstraintsInIteration.put(unassigned.getId(), new ArrayList()); } - failedConstraintsInIteration.get(unassigned.getId()).addAll(failedConstraintInfo); + failedConstraintsInIteration.get(unassigned.getId()).addAll(failedConstraintsInfo); } public void put(String simpleNameOfFailedConstraint, int code, String reason) { diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FailedConstraintInfoTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FailedConstraintInfoTest.java new file mode 100644 index 000000000..60c52760c --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FailedConstraintInfoTest.java @@ -0,0 +1,89 @@ +package com.graphhopper.jsprit.core.util; + +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.job.Service; +import com.graphhopper.jsprit.core.problem.misc.ActivityContext; +import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; +import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import com.graphhopper.jsprit.core.problem.solution.route.activity.ServiceActivity; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivities; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.when; + +/** + * Created by tonirajkovski on 7/24/17. + */ +@RunWith(MockitoJUnitRunner.class) +public class FailedConstraintInfoTest { + + @Mock + private Job job; + @Mock + private Vehicle vehicle; + @Mock + private ActivityContext activityContext; + @Mock + private VehicleRoute route; + @Mock + private TourActivities tourActivities; + @Mock + private JobInsertionContext jobInsertionContext; + + @Test + public void shouldLoadJobInsertionContextWhenNull() { + // given + FailedConstraintInfo.Builder builder = FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c1"); + + //when + FailedConstraintInfo failedConstraintInfo = builder.loadInsertionContextData(null).build(); + + //then + Assert.assertNull(failedConstraintInfo.getVehicle()); + Assert.assertNull(failedConstraintInfo.getJob()); + Assert.assertEquals(0, failedConstraintInfo.getInsertionIndex()); + // just assert that there will be no exception because of null values and will produce some value + Assert.assertNotNull(failedConstraintInfo.toString()); + } + + @Test + public void shouldLoadJobInsertionContextWhenNotNull() { + // given + FailedConstraintInfo.Builder builder = FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c1"); + + List activities = new ArrayList<>(); + activities.add(ServiceActivity.newInstance(Service.Builder.newInstance("testService").setLocation(Location.newInstance(1, 1)).build())); + + when(job.getId()).thenReturn("job"); + when(vehicle.getId()).thenReturn("vehicle"); + when(activityContext.getInsertionIndex()).thenReturn(2); + when(route.getTourActivities()).thenReturn(tourActivities); + when(tourActivities.getActivities()).thenReturn(activities); + + when(jobInsertionContext.getJob()).thenReturn(job); + when(jobInsertionContext.getRoute()).thenReturn(route); + when(jobInsertionContext.getActivityContext()).thenReturn(activityContext); + when(jobInsertionContext.getNewVehicle()).thenReturn(vehicle); + + //when + FailedConstraintInfo failedConstraintInfo = builder.loadInsertionContextData(jobInsertionContext).build(); + + //then + Assert.assertEquals("job", failedConstraintInfo.getJob()); + Assert.assertEquals(2, failedConstraintInfo.getInsertionIndex()); + Assert.assertEquals("vehicle", failedConstraintInfo.getVehicle()); + Assert.assertEquals(1, failedConstraintInfo.getActivities().size()); + Assert.assertEquals("testService-service", failedConstraintInfo.getActivities().get(0)); + } + +} diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java index d0996c754..ca3392e90 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java @@ -147,15 +147,15 @@ public void shouldAggregateFailedConstraintNamesFrequencyMapping() { // iteration 0 List failedConstraints = new ArrayList<>(); - failedConstraints.add(new FailedConstraintInfo("constraint1", null)); - failedConstraints.add(new FailedConstraintInfo("constraint2", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("constraint1").build()); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("constraint2").build()); reasonTracker.informIterationStarts(0, null, null); reasonTracker.informJobUnassigned(job, failedConstraints); // iteration 1 failedConstraints.clear(); - failedConstraints.add(new FailedConstraintInfo("constraint2", null)); - failedConstraints.add(new FailedConstraintInfo("constraint3", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("constraint2").build()); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("constraint3").build()); reasonTracker.informIterationStarts(1, null, null); reasonTracker.informJobUnassigned(job, failedConstraints); @@ -176,13 +176,13 @@ public void shouldGetFailedConstraintsForJobWhenWhenFailedConstraintsAreAddedFor // iteration1 List failedConstraints = new ArrayList<>(); - failedConstraints.add(new FailedConstraintInfo("c1", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c1").build()); reasonTracker.informIterationStarts(0, null, null); reasonTracker.informJobUnassigned(job, failedConstraints); // iteration2 failedConstraints.clear(); - failedConstraints.add(new FailedConstraintInfo("c2", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c2").build()); reasonTracker.informIterationStarts(2, null, null); reasonTracker.informJobUnassigned(job, failedConstraints); @@ -207,13 +207,13 @@ public void shouldGetFailedConstraintsForJobWhenDifferentConstraintsForDifferent List failedConstraints = new ArrayList<>(); // iteration1 - failedConstraints.add(new FailedConstraintInfo("c1", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c1").build()); reasonTracker.informIterationStarts(0, null, null); reasonTracker.informJobUnassigned(job1, failedConstraints); // iteration2 failedConstraints.clear(); - failedConstraints.add(new FailedConstraintInfo("c2", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c2").build()); reasonTracker.informIterationStarts(2, null, null); reasonTracker.informJobUnassigned(job2, failedConstraints); @@ -260,7 +260,7 @@ public void shouldGetFailedConstraintsInIterationForJobWhenFailedConstraintsAdde List failedConstraints = new ArrayList<>(); // adding the failed constraint in iteration 1 - failedConstraints.add(new FailedConstraintInfo("c1", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c1").build()); reasonTracker.informIterationStarts(1, null, null); reasonTracker.informJobUnassigned(job, failedConstraints); @@ -280,13 +280,13 @@ public void shouldGetFailedConstraintsInIterationForJobWhenDifferentJobConstrain List failedConstraints = new ArrayList<>(); // adding the failed constraint in iteration 1 for job1 - failedConstraints.add(new FailedConstraintInfo("c1", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c1").build()); reasonTracker.informIterationStarts(1, null, null); reasonTracker.informJobUnassigned(job1, failedConstraints); // adding the failed constraint in iteration 2 for job2 failedConstraints.clear(); - failedConstraints.add(new FailedConstraintInfo("c2", null)); + failedConstraints.add(FailedConstraintInfo.Builder.newInstance().setFailedConstraint("c2").build()); reasonTracker.informIterationStarts(2, null, null); reasonTracker.informJobUnassigned(job2, failedConstraints); diff --git a/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/UnassignedJobReasonTrackerExample.java b/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/UnassignedJobReasonTrackerExample.java index 267ca994a..199ec4279 100644 --- a/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/UnassignedJobReasonTrackerExample.java +++ b/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/UnassignedJobReasonTrackerExample.java @@ -9,6 +9,7 @@ import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint; 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.misc.JobInsertionContext; import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; @@ -65,9 +66,13 @@ public static void main(String[] args) { .setDeliveryTimeWindow(new TimeWindow(15, 16)) .build(); + Service service1 = Service.Builder.newInstance("service1").addSizeDimension(WEIGHT_INDEX, 1).setLocation(Location.newInstance(5, 7)).build(); + // service2 required more capacity that both vehicles are not able to provide + Service service2 = Service.Builder.newInstance("service2").addSizeDimension(WEIGHT_INDEX, 10).setLocation(Location.newInstance(5, 5)).build(); + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); vrpBuilder.addVehicle(v1).addVehicle(v2); - vrpBuilder.addJob(shipment1).addJob(shipment2).addJob(shipment3).addJob(shipment4); + vrpBuilder.addJob(shipment1).addJob(shipment2).addJob(shipment3).addJob(shipment4).addJob(service1).addJob(service2); // prepare the algorithm VehicleRoutingProblem problem = vrpBuilder.build(); @@ -91,6 +96,7 @@ public static void main(String[] args) { Collection solutions = algorithm.searchSolutions(); VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions); + // print the info about each unassigned job for (Job unassignedJob: bestSolution.getUnassignedJobs()) { Collection failedConstraints = tracker.getFailedConstraintsForJob(unassignedJob.getId()); for (FailedConstraintInfo failedConstraint : failedConstraints) { From 3c0f61d537c0289fb1c45d692833b9c7aa4c4c05 Mon Sep 17 00:00:00 2001 From: Toni Rajkovski Date: Mon, 24 Jul 2017 14:59:55 +0200 Subject: [PATCH 6/7] fix problem with java7 --- .../jsprit/core/util/UnassignedJobReasonTracker.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java index d6708021b..efc2633a3 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java @@ -140,9 +140,9 @@ protected Map aggregateFailedConstraintNamesFrequencyMapping( public Collection getFailedConstraintsForJob(String jobId) { Collection result = new ArrayList<>(); - for (Map> failedConstraints: failedConstraints.values()) { - if (failedConstraints.containsKey(jobId)) { - result.addAll(failedConstraints.get(jobId)); + for (Map> failedConstraintsMap: failedConstraints.values()) { + if (failedConstraintsMap.containsKey(jobId)) { + result.addAll(failedConstraintsMap.get(jobId)); } } return result; From 9208b20f25e469c9ec3bed5fd31ffc6dda88003a Mon Sep 17 00:00:00 2001 From: Toni Rajkovski Date: Mon, 24 Jul 2017 17:11:05 +0200 Subject: [PATCH 7/7] Optimize data in FailedConstraintInfo Due to performance degradation saving of the activities in the route is removed from FailedConstraintInfo --- .../core/util/FailedConstraintInfo.java | 26 +++---------------- .../core/util/FailedConstraintInfoTest.java | 9 ------- 2 files changed, 4 insertions(+), 31 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java index 487914535..6eb42843d 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FailedConstraintInfo.java @@ -6,6 +6,7 @@ import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -18,7 +19,6 @@ public static class Builder { private String failedConstraint; private String job; private String vehicle; - private List activities = new ArrayList<>(); private int insertionIndex; public static FailedConstraintInfo.Builder newInstance() { @@ -48,13 +48,6 @@ public FailedConstraintInfo.Builder loadInsertionContextData(JobInsertionCont if (insertionContext.getActivityContext() != null) { this.insertionIndex = insertionContext.getActivityContext().getInsertionIndex(); } - if (insertionContext.getRoute() != null && insertionContext.getRoute().getActivities() != null) { - for (TourActivity activity: insertionContext.getRoute().getTourActivities().getActivities()) { - if (activity instanceof TourActivity.JobActivity) { - activities.add(((TourActivity.JobActivity) activity).getJob().getId() + "-" + activity.getName()); - } - } - } } return this; } @@ -63,14 +56,12 @@ public FailedConstraintInfo.Builder loadInsertionContextData(JobInsertionCont private String failedConstraint; private String job; private String vehicle; - private List activities = new ArrayList<>(); private int insertionIndex = -1; private FailedConstraintInfo(Builder builder) { this.failedConstraint = builder.failedConstraint; this.job = builder.job; this.vehicle = builder.vehicle; - this.activities = builder.activities; this.insertionIndex = builder.insertionIndex; } @@ -86,26 +77,17 @@ public int getInsertionIndex() { return insertionIndex; } - public List getActivities() { - return activities; - } - public String getJob() { return job; } public String toString() { - StringBuilder route = new StringBuilder(); - route.append(vehicle).append(" [ "); - for (String activity: activities) { - route.append(activity).append(" "); - } - route.append("]"); - return String.format("Constraint '%s' failed for job insertion of job '%s' on position '%d' on route '%s'", + return String.format("Constraint '%s' failed for job insertion of job '%s' on position '%d' in vehicle '%s'", failedConstraint, job, insertionIndex, - route + vehicle ); } + } diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FailedConstraintInfoTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FailedConstraintInfoTest.java index 60c52760c..55dd833a9 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FailedConstraintInfoTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/FailedConstraintInfoTest.java @@ -34,10 +34,6 @@ public class FailedConstraintInfoTest { @Mock private ActivityContext activityContext; @Mock - private VehicleRoute route; - @Mock - private TourActivities tourActivities; - @Mock private JobInsertionContext jobInsertionContext; @Test @@ -67,11 +63,8 @@ public void shouldLoadJobInsertionContextWhenNotNull() { when(job.getId()).thenReturn("job"); when(vehicle.getId()).thenReturn("vehicle"); when(activityContext.getInsertionIndex()).thenReturn(2); - when(route.getTourActivities()).thenReturn(tourActivities); - when(tourActivities.getActivities()).thenReturn(activities); when(jobInsertionContext.getJob()).thenReturn(job); - when(jobInsertionContext.getRoute()).thenReturn(route); when(jobInsertionContext.getActivityContext()).thenReturn(activityContext); when(jobInsertionContext.getNewVehicle()).thenReturn(vehicle); @@ -82,8 +75,6 @@ public void shouldLoadJobInsertionContextWhenNotNull() { Assert.assertEquals("job", failedConstraintInfo.getJob()); Assert.assertEquals(2, failedConstraintInfo.getInsertionIndex()); Assert.assertEquals("vehicle", failedConstraintInfo.getVehicle()); - Assert.assertEquals(1, failedConstraintInfo.getActivities().size()); - Assert.assertEquals("testService-service", failedConstraintInfo.getActivities().get(0)); } }