diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7957f3..cd6636b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Change-log ========== +**v1.7** @ 2017-01-12 +- see [Whats new](https://github.com/graphhopper/jsprit/blob/master/WHATS_NEW.md) + **v1.6.2** @ 2016-02-02 new features: diff --git a/WHATS_NEW.md b/WHATS_NEW.md index 462a7cc0..04e27c30 100644 --- a/WHATS_NEW.md +++ b/WHATS_NEW.md @@ -1,15 +1,17 @@ WHATS NEW ========== ------------------------------ -?? new release **v1.7** +2017-01-12 new release **v1.7** - move packages to [graphhopper.com](https://graphhopper.com/) - change license from GPLv3 to [Apache v2](https://github.com/graphhopper/jsprit/blob/master/LICENSE.md) to make it even more attractive for other developers and their commercial applications - pushed binaries to maven central, i.e. made it better accessible and we get rid of our own repo - outsourced various io operations, e.g. reading writing problem/algorithm to a new module called [jsprit-io](https://github.com/graphhopper/jsprit/tree/master/jsprit-io). this way the core is even more lightweight and less dependent on other libraries - switched from log4j to the [logger facade slf4j](http://www.slf4j.org/) to allow developers to plugin the logger of their choice - made it [much more memory efficient](https://github.com/graphhopper/jsprit/issues/230) for large problems +- fixed [memory leak](https://github.com/graphhopper/jsprit/pull/282) - add [priority feature](https://github.com/graphhopper/jsprit/issues/242) - [refine exceptions](https://github.com/graphhopper/jsprit/issues/251) to get a clear separation of IllegalArgument and [IllegalState](https://stackoverflow.com/questions/12698275/whats-the-intended-use-of-illegalstateexception) +- many further improvements (see https://github.com/graphhopper/jsprit/issues?q=is%3Aissue+is%3Aclosed) 2016-02-02 new release **v1.6.2** diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index c49fe090..ac5af692 100644 --- a/docs/Getting-Started.md +++ b/docs/Getting-Started.md @@ -15,10 +15,12 @@ If you want to use the latest release of jsprit-core, add the following lines to
<dependency>
    <groupId>com.graphhopper</groupId>
    <artifactId>jsprit-core</artifactId>
-   <version>1.7-RC1</version>
+   <version>{version}</version>
 </dependency>
 
+Find the latest versions here: [mvn repository](https://mvnrepository.com/artifact/com.graphhopper/jsprit-core). + ####Build yourself If you want to build the master branch yourself, do this: diff --git a/docs/Simple-Example.md b/docs/Simple-Example.md index 091e8cc6..d61aa27e 100644 --- a/docs/Simple-Example.md +++ b/docs/Simple-Example.md @@ -16,7 +16,7 @@ First, build a vehicle with its vehicle-type:
/*
  * get a vehicle type-builder and build a type with the typeId "vehicleType" and a capacity of 2
- * you are free to add an arbitrary number of capacity dimensions with .addCacpacityDimension(dimensionIndex,dimensionValue)
+ * you are free to add an arbitrary number of capacity dimensions with .addCapacityDimension(dimensionIndex,dimensionValue)
  */
 final int WEIGHT_INDEX = 0;
 VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder.newInstance("vehicleType").addCapacityDimension(WEIGHT_INDEX,2);
diff --git a/jsprit-analysis/pom.xml b/jsprit-analysis/pom.xml
index a5707f18..409ef69d 100644
--- a/jsprit-analysis/pom.xml
+++ b/jsprit-analysis/pom.xml
@@ -20,7 +20,7 @@
     
         com.graphhopper
         jsprit
-        1.7-SNAPSHOT
+        1.7.1-SNAPSHOT
     
     4.0.0
     jsprit-analysis
diff --git a/jsprit-core/pom.xml b/jsprit-core/pom.xml
index f3d79fc0..d20a01b6 100644
--- a/jsprit-core/pom.xml
+++ b/jsprit-core/pom.xml
@@ -21,7 +21,7 @@
     
         com.graphhopper
         jsprit
-        1.7-SNAPSHOT
+        1.7.1-SNAPSHOT
     
     4.0.0
     jsprit-core
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java
index c0a243cc..557bc4cd 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java
@@ -737,7 +737,7 @@ public class Jsprit {
                     }
                 }
                 for(Job j : solution.getUnassignedJobs()){
-                    costs += maxCosts * 2 * (4 - j.getPriority());
+                    costs += maxCosts * 2 * (11 - j.getPriority());
                 }
                 return costs;
             }
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
new file mode 100644
index 00000000..6f3d54a4
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.algorithm.recreate;
+
+import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
+import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint;
+import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus;
+import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint;
+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.Collection;
+import java.util.List;
+
+/**
+ * Created by schroeder on 06/02/17.
+ */
+abstract class AbstractInsertionCalculator implements JobInsertionCostsCalculator {
+
+    InsertionData checkRouteContraints(JobInsertionContext insertionContext, ConstraintManager constraintManager) {
+        for (HardRouteConstraint hardRouteConstraint : constraintManager.getHardRouteConstraints()) {
+            if (!hardRouteConstraint.fulfilled(insertionContext)) {
+                InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
+                emptyInsertionData.addFailedConstrainName(hardRouteConstraint.getClass().getSimpleName());
+                return emptyInsertionData;
+            }
+        }
+        return null;
+    }
+
+    ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime, Collection failedActivityConstraints, ConstraintManager constraintManager) {
+        ConstraintsStatus notFulfilled = null;
+        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());
+                return status;
+            } else {
+                if (status.equals(ConstraintsStatus.NOT_FULFILLED)) {
+                    failed.add(c.getClass().getSimpleName());
+                    notFulfilled = status;
+                }
+            }
+        }
+        if (notFulfilled != null) {
+            failedActivityConstraints.addAll(failed);
+            return notFulfilled;
+        }
+
+        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());
+                return status;
+            } else {
+                if (status.equals(ConstraintsStatus.NOT_FULFILLED)) {
+                    failed.add(c.getClass().getSimpleName());
+                    notFulfilled = status;
+                }
+            }
+        }
+        if (notFulfilled != null) {
+            failedActivityConstraints.addAll(failed);
+            return notFulfilled;
+        }
+
+        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());
+                return status;
+            }
+        }
+        return ConstraintsStatus.FULFILLED;
+    }
+
+}
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 7d794a6d..5f5973f5 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
@@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory;
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
 import java.util.Random;
 
 public abstract class AbstractInsertionStrategy implements InsertionStrategy {
@@ -92,6 +93,10 @@ public abstract class AbstractInsertionStrategy implements InsertionStrategy {
         return badJobs;
     }
 
+    public void markUnassigned(Job unassigned, List reasons) {
+        insertionsListeners.informJobUnassignedListeners(unassigned, reasons);
+    }
+
     public abstract Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs);
 
     @Override
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 a78205ae..329a05ff 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
@@ -66,10 +66,12 @@ public final class BestInsertion extends AbstractInsertionStrategy {
         sometimesSortPriorities(unassignedJobList);
         for (Job unassignedJob : unassignedJobList) {
             Insertion bestInsertion = null;
+            InsertionData empty = new InsertionData.NoInsertionFound();
             double bestInsertionCost = Double.MAX_VALUE;
             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());
                     continue;
                 }
                 if (iData.getInsertionCost() < bestInsertionCost + noiseMaker.makeNoise()) {
@@ -84,14 +86,19 @@ public final class BestInsertion extends AbstractInsertionStrategy {
                     bestInsertion = new Insertion(newRoute, newIData);
                     vehicleRoutes.add(newRoute);
                 }
+            } else {
+                empty.getFailedConstraintNames().addAll(newIData.getFailedConstraintNames());
+            }
+            if (bestInsertion == null) {
+                badJobs.add(unassignedJob);
+                markUnassigned(unassignedJob, empty.getFailedConstraintNames());
             }
-            if (bestInsertion == null) badJobs.add(unassignedJob);
             else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
-//            nextInsertion();
         }
         return badJobs;
     }
 
+
     private void sometimesSortPriorities(List unassignedJobList) {
         if(random.nextDouble() < 0.5){
             Collections.sort(unassignedJobList, new Comparator() {
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 52d913df..2b014df0 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
@@ -101,6 +101,7 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
         Collections.shuffle(unassignedJobList, random);
         sometimesSortPriorities(unassignedJobList);
         List batches = distributeRoutes(vehicleRoutes, nuOfBatches);
+        List failedConstraintNames = new ArrayList<>();
         for (final Job unassignedJob : unassignedJobList) {
             Insertion bestInsertion = null;
             double bestInsertionCost = Double.MAX_VALUE;
@@ -118,7 +119,10 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
                 for (int i = 0; i < batches.size(); i++) {
                     Future futureIData = completionService.take();
                     Insertion insertion = futureIData.get();
-                    if (insertion == null) continue;
+                    if (insertion.insertionData instanceof NoInsertionFound) {
+                        failedConstraintNames.addAll(insertion.getInsertionData().getFailedConstraintNames());
+                        continue;
+                    }
                     if (insertion.getInsertionData().getInsertionCost() < bestInsertionCost) {
                         bestInsertion = insertion;
                         bestInsertionCost = insertion.getInsertionData().getInsertionCost();
@@ -136,7 +140,10 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
                 vehicleRoutes.add(newRoute);
                 batches.get(random.nextInt(batches.size())).routes.add(newRoute);
             }
-            if (bestInsertion == null) badJobs.add(unassignedJob);
+            if (bestInsertion == null) {
+                badJobs.add(unassignedJob);
+                markUnassigned(unassignedJob, failedConstraintNames);
+            }
             else insertJob(unassignedJob, bestInsertion.getInsertionData(), bestInsertion.getRoute());
         }
         return badJobs;
@@ -155,10 +162,12 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
 
     private Insertion getBestInsertion(Batch batch, Job unassignedJob) {
         Insertion bestInsertion = null;
+        InsertionData empty = new InsertionData.NoInsertionFound();
         double bestInsertionCost = Double.MAX_VALUE;
         for (VehicleRoute vehicleRoute : batch.routes) {
             InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost);
             if (iData instanceof NoInsertionFound) {
+                empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames());
                 continue;
             }
             if (iData.getInsertionCost() < bestInsertionCost) {
@@ -166,6 +175,7 @@ public final class BestInsertionConcurrent extends AbstractInsertionStrategy {
                 bestInsertionCost = iData.getInsertionCost();
             }
         }
+        if (bestInsertion == null) return new Insertion(null, empty);
         return bestInsertion;
     }
 
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 d5b5ef8d..fd78b3e9 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
@@ -31,6 +31,7 @@ import java.util.List;
  */
 public class InsertionData {
 
+
     public static class NoInsertionFound extends InsertionData {
 
         public NoInsertionFound() {
@@ -75,6 +76,8 @@ public class InsertionData {
         return events;
     }
 
+    private List reasons = new ArrayList<>();
+
     /**
      * @return the additionalTime
      */
@@ -82,6 +85,14 @@ public class InsertionData {
         return additionalTime;
     }
 
+    public void addFailedConstrainName(String name) {
+        reasons.add(name);
+    }
+
+    public List getFailedConstraintNames() {
+        return reasons;
+    }
+
     /**
      * @param additionalTime the additionalTime to set
      */
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 ced87182..8d019e2c 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
@@ -33,7 +33,7 @@ class InsertionDataUpdater {
 
     static boolean update(boolean addAllAvailable, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, TreeSet insertionDataSet, int updateRound, Job unassignedJob, Collection routes) {
         for(VehicleRoute route : routes) {
-            Collection relevantVehicles = new ArrayList();
+            Collection relevantVehicles = new ArrayList<>();
             if (!(route.getVehicle() instanceof VehicleImpl.NoVehicle)) {
                 relevantVehicles.add(route.getVehicle());
                 if(addAllAvailable && !initialVehicleIds.contains(route.getVehicle().getId())){
@@ -71,7 +71,7 @@ class InsertionDataUpdater {
         };
     }
 
-    static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction, TreeSet[] priorityQueues, Map updates, List unassignedJobList, List badJobs) {
+    static ScoredJob getBest(boolean switchAllowed, Set initialVehicleIds, VehicleFleetManager fleetManager, JobInsertionCostsCalculator insertionCostsCalculator, ScoringFunction scoringFunction, TreeSet[] priorityQueues, Map updates, List unassignedJobList, List badJobs) {
         ScoredJob bestScoredJob = null;
         for(Job j : unassignedJobList){
             VehicleRoute bestRoute = null;
@@ -79,6 +79,7 @@ class InsertionDataUpdater {
             InsertionData secondBest = null;
             TreeSet priorityQueue = priorityQueues[j.getIndex()];
             Iterator iterator = priorityQueue.iterator();
+            List failedConstraintNames = new ArrayList<>();
             while(iterator.hasNext()){
                 VersionedInsertionData versionedIData = iterator.next();
                 if(bestRoute != null){
@@ -86,7 +87,10 @@ class InsertionDataUpdater {
                         continue;
                     }
                 }
-                if(versionedIData.getiData() instanceof InsertionData.NoInsertionFound) continue;
+                if (versionedIData.getiData() instanceof InsertionData.NoInsertionFound) {
+                    failedConstraintNames.addAll(versionedIData.getiData().getFailedConstraintNames());
+                    continue;
+                }
                 if(!(versionedIData.getRoute().getVehicle() instanceof VehicleImpl.NoVehicle)) {
                     if (versionedIData.getiData().getSelectedVehicle() != versionedIData.getRoute().getVehicle()) {
                         if (!switchAllowed) continue;
@@ -136,9 +140,9 @@ class InsertionDataUpdater {
                 } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
                     secondBest = iData;
                 }
-            }
+            } else failedConstraintNames.addAll(iData.getFailedConstraintNames());
             if (best == null) {
-                badJobs.add(j);
+                badJobs.add(new ScoredJob.BadJob(j, failedConstraintNames));
                 continue;
             }
             double score = score(j, best, secondBest, scoringFunction);
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 6e405dec..30a7a98a 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
@@ -105,10 +105,10 @@ public class RegretInsertion extends AbstractInsertionStrategy {
             }
         }
 
-        List jobs = new ArrayList(unassignedJobs);
+        List jobs = new ArrayList<>(unassignedJobs);
         while (!jobs.isEmpty()) {
-            List unassignedJobList = new ArrayList(jobs);
-            List badJobList = new ArrayList();
+            List unassignedJobList = new ArrayList<>(jobs);
+            List badJobList = new ArrayList<>();
             ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
             if (bestScoredJob != null) {
                 if (bestScoredJob.isNewRoute()) {
@@ -117,9 +117,11 @@ public class RegretInsertion extends AbstractInsertionStrategy {
                 insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
                 jobs.remove(bestScoredJob.getJob());
             }
-            for (Job bad : badJobList) {
-                jobs.remove(bad);
-                badJobs.add(bad);
+            for (ScoredJob bad : badJobList) {
+                Job unassigned = bad.getJob();
+                jobs.remove(unassigned);
+                badJobs.add(unassigned);
+                markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames());
             }
         }
         return badJobs;
@@ -132,12 +134,12 @@ public class RegretInsertion extends AbstractInsertionStrategy {
         return null;
     }
 
-    private ScoredJob nextJob(Collection routes, Collection unassignedJobList, List badJobs) {
+    private ScoredJob nextJob(Collection routes, Collection unassignedJobList, List badJobs) {
         ScoredJob bestScoredJob = null;
         for (Job unassignedJob : unassignedJobList) {
             ScoredJob scoredJob = getScoredJob(routes, unassignedJob, insertionCostsCalculator, scoringFunction);
             if (scoredJob instanceof ScoredJob.BadJob) {
-                badJobs.add(unassignedJob);
+                badJobs.add(scoredJob);
                 continue;
             }
             if (bestScoredJob == null) bestScoredJob = scoredJob;
@@ -158,14 +160,17 @@ public class RegretInsertion extends AbstractInsertionStrategy {
         InsertionData best = null;
         InsertionData secondBest = null;
         VehicleRoute bestRoute = null;
-
+        List failedConstraintNames = new ArrayList<>();
         double benchmark = Double.MAX_VALUE;
         for (VehicleRoute route : routes) {
             if (secondBest != null) {
                 benchmark = secondBest.getInsertionCost();
             }
             InsertionData iData = insertionCostsCalculator.getInsertionData(route, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, benchmark);
-            if (iData instanceof InsertionData.NoInsertionFound) continue;
+            if (iData instanceof InsertionData.NoInsertionFound) {
+                failedConstraintNames.addAll(iData.getFailedConstraintNames());
+                continue;
+            }
             if (best == null) {
                 best = iData;
                 bestRoute = route;
@@ -191,9 +196,10 @@ public class RegretInsertion extends AbstractInsertionStrategy {
             } else if (secondBest == null || (iData.getInsertionCost() < secondBest.getInsertionCost())) {
                 secondBest = iData;
             }
-        }
+        } else failedConstraintNames.addAll(iData.getFailedConstraintNames());
         if (best == null) {
-            return new ScoredJob.BadJob(unassignedJob);
+            ScoredJob.BadJob badJob = new ScoredJob.BadJob(unassignedJob, failedConstraintNames);
+            return badJob;
         }
         double score = score(unassignedJob, best, secondBest, scoringFunction);
         ScoredJob scoredJob;
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 fe2146ba..b71ac5ab 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
@@ -109,10 +109,10 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
             }
         }
 
-        List jobs = new ArrayList(unassignedJobs);
+        List jobs = new ArrayList<>(unassignedJobs);
         while (!jobs.isEmpty()) {
-            List unassignedJobList = new ArrayList(jobs);
-            List badJobList = new ArrayList();
+            List unassignedJobList = new ArrayList<>(jobs);
+            List badJobList = new ArrayList<>();
             ScoredJob bestScoredJob = nextJob(routes, unassignedJobList, badJobList);
             if (bestScoredJob != null) {
                 if (bestScoredJob.isNewRoute()) {
@@ -121,15 +121,17 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
                 insertJob(bestScoredJob.getJob(), bestScoredJob.getInsertionData(), bestScoredJob.getRoute());
                 jobs.remove(bestScoredJob.getJob());
             }
-            for (Job j : badJobList) {
-                jobs.remove(j);
-                badJobs.add(j);
+            for (ScoredJob bad : badJobList) {
+                Job unassigned = bad.getJob();
+                jobs.remove(unassigned);
+                badJobs.add(unassigned);
+                markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames());
             }
         }
         return badJobs;
     }
 
-    private ScoredJob nextJob(final Collection routes, List unassignedJobList, List badJobList) {
+    private ScoredJob nextJob(final Collection routes, List unassignedJobList, List badJobList) {
         ScoredJob bestScoredJob = null;
 
         for (final Job unassignedJob : unassignedJobList) {
@@ -148,7 +150,7 @@ public class RegretInsertionConcurrent extends AbstractInsertionStrategy {
                 Future fsj = completionService.take();
                 ScoredJob sJob = fsj.get();
                 if (sJob instanceof ScoredJob.BadJob) {
-                    badJobList.add(sJob.getJob());
+                    badJobList.add(sJob);
                     continue;
                 }
                 if (bestScoredJob == null) {
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 187145f6..be4f5d11 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
@@ -143,8 +143,8 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
         int updateRound = 0;
         Map updates = new HashMap();
         while (!jobs.isEmpty()) {
-            List unassignedJobList = new ArrayList(jobs);
-            List badJobList = new ArrayList();
+            List unassignedJobList = new ArrayList<>(jobs);
+            List badJobList = new ArrayList<>();
             if(!firstRun && lastModified == null) throw new IllegalStateException("ho. this must not be.");
             updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound,firstRun,lastModified,updates);
             if(firstRun) firstRun = false;
@@ -159,9 +159,11 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
                 lastModified = bestScoredJob.getRoute();
             }
             else lastModified = null;
-            for (Job bad : badJobList) {
-                jobs.remove(bad);
-                badJobs.add(bad);
+            for (ScoredJob bad : badJobList) {
+                Job unassigned = bad.getJob();
+                jobs.remove(unassigned);
+                badJobs.add(unassigned);
+                markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames());
             }
         }
         return badJobs;
@@ -172,7 +174,7 @@ public class RegretInsertionConcurrentFast extends AbstractInsertionStrategy {
         boolean updatedAllRoutes = false;
         for (final Job unassignedJob : unassignedJobList) {
             if(priorityQueues[unassignedJob.getIndex()] == null){
-                priorityQueues[unassignedJob.getIndex()] = new TreeSet(InsertionDataUpdater.getComparator());
+                priorityQueues[unassignedJob.getIndex()] = new TreeSet<>(InsertionDataUpdater.getComparator());
             }
             if(firstRun) {
                 updatedAllRoutes = true;
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 4369dd01..d805e55d 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
@@ -131,10 +131,10 @@ public class RegretInsertionFast extends AbstractInsertionStrategy {
         VehicleRoute lastModified = null;
         boolean firstRun = true;
         int updateRound = 0;
-        Map updates = new HashMap();
+        Map updates = new HashMap<>();
         while (!jobs.isEmpty()) {
-            List unassignedJobList = new ArrayList(jobs);
-            List badJobList = new ArrayList();
+            List unassignedJobList = new ArrayList<>(jobs);
+            List badJobList = new ArrayList<>();
             if(!firstRun && lastModified == null) throw new IllegalStateException("last modified route is null. this should not be.");
             if(firstRun){
                 updateInsertionData(priorityQueues, routes, unassignedJobList, updateRound, firstRun, lastModified, updates);
@@ -156,9 +156,11 @@ public class RegretInsertionFast extends AbstractInsertionStrategy {
                 lastModified = bestScoredJob.getRoute();
             }
             else lastModified = null;
-            for (Job bad : badJobList) {
-                jobs.remove(bad);
-                badJobs.add(bad);
+            for (ScoredJob bad : badJobList) {
+                Job unassigned = bad.getJob();
+                jobs.remove(unassigned);
+                badJobs.add(unassigned);
+                markUnassigned(unassigned, bad.getInsertionData().getFailedConstraintNames());
             }
         }
         return badJobs;
@@ -167,7 +169,7 @@ public class RegretInsertionFast extends AbstractInsertionStrategy {
     private void updateInsertionData(TreeSet[] priorityQueues, Collection routes, List unassignedJobList, int updateRound, boolean firstRun, VehicleRoute lastModified, Map updates) {
         for (Job unassignedJob : unassignedJobList) {
             if(priorityQueues[unassignedJob.getIndex()] == null){
-                priorityQueues[unassignedJob.getIndex()] = new TreeSet(InsertionDataUpdater.getComparator());
+                priorityQueues[unassignedJob.getIndex()] = new TreeSet<>(InsertionDataUpdater.getComparator());
             }
             if(firstRun) {
                 InsertionDataUpdater.update(switchAllowed, initialVehicleIds, fleetManager, insertionCostsCalculator, priorityQueues[unassignedJob.getIndex()], updateRound, unassignedJob, routes);
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 48582800..f0f8950f 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
@@ -21,6 +21,8 @@ package com.graphhopper.jsprit.core.algorithm.recreate;
 import com.graphhopper.jsprit.core.problem.job.Job;
 import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
 
+import java.util.List;
+
 /**
  * Created by schroeder on 15/10/15.
  */
@@ -28,8 +30,14 @@ class ScoredJob {
 
     static class BadJob extends ScoredJob {
 
-        BadJob(Job job) {
-            super(job, 0., null, null, false);
+        BadJob(Job job, List failedConstraintNames) {
+            super(job, 0., getEmptyInsertion(failedConstraintNames), null, false);
+        }
+
+        private static InsertionData getEmptyInsertion(List failedConstraintNames) {
+            InsertionData empty = new InsertionData.NoInsertionFound();
+            empty.getFailedConstraintNames().addAll(failedConstraintNames);
+            return empty;
         }
     }
 
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Scorer.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Scorer.java
index 6aabb134..a6607d26 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Scorer.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/Scorer.java
@@ -33,9 +33,9 @@ class Scorer {
         if (secondBest == null) { //either there is only one vehicle or there are more vehicles, but they cannot load unassignedJob
             //if only one vehicle, I want the job to be inserted with min iCosts
             //if there are more vehicles, I want this job to be prioritized since there are no alternatives
-            score = (4 - unassignedJob.getPriority()) * (Integer.MAX_VALUE - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
+            score = (11 - unassignedJob.getPriority()) * (Integer.MAX_VALUE - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
         } else {
-            score = (4 - unassignedJob.getPriority()) * (secondBest.getInsertionCost() - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
+            score = (11 - unassignedJob.getPriority()) * (secondBest.getInsertionCost() - best.getInsertionCost()) + scoringFunction.score(best, unassignedJob);
         }
         return score;
     }
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 977ba6ab..9f91ff9b 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
@@ -18,8 +18,10 @@
 package com.graphhopper.jsprit.core.algorithm.recreate;
 
 import com.graphhopper.jsprit.core.problem.JobActivityFactory;
-import com.graphhopper.jsprit.core.problem.constraint.*;
+import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
 import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus;
+import com.graphhopper.jsprit.core.problem.constraint.SoftActivityConstraint;
+import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint;
 import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
 import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
 import com.graphhopper.jsprit.core.problem.driver.Driver;
@@ -36,6 +38,8 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 
 /**
@@ -43,13 +47,13 @@ import java.util.Iterator;
  *
  * @author schroeder
  */
-final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
+final class ServiceInsertionCalculator extends AbstractInsertionCalculator {
 
     private static final Logger logger = LoggerFactory.getLogger(ServiceInsertionCalculator.class);
 
-    private HardRouteConstraint hardRouteLevelConstraint;
+//    private HardRouteConstraint hardRouteLevelConstraint;
 
-    private HardActivityConstraint hardActivityLevelConstraint;
+//    private HardActivityConstraint hardActivityLevelConstraint;
 
     private SoftRouteConstraint softRouteConstraint;
 
@@ -65,12 +69,13 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
 
     private AdditionalAccessEgressCalculator additionalAccessEgressCalculator;
 
+    private ConstraintManager constraintManager;
+
     public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager) {
         super();
         this.transportCosts = routingCosts;
         this.activityCosts = activityCosts;
-        hardRouteLevelConstraint = constraintManager;
-        hardActivityLevelConstraint = constraintManager;
+        this.constraintManager = constraintManager;
         softActivityConstraint = constraintManager;
         softRouteConstraint = constraintManager;
         this.additionalTransportCostsCalculator = additionalTransportCostsCalculator;
@@ -103,9 +108,10 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
         /*
         check hard constraints at route level
          */
-        if (!hardRouteLevelConstraint.fulfilled(insertionContext)) {
-            return InsertionData.createEmptyInsertionData();
-        }
+        InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager);
+        if (noInsertion != null) return noInsertion;
+
+        Collection failedActivityConstraints = new ArrayList<>();
 
         /*
         check soft constraints at route level
@@ -142,7 +148,7 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
                 ActivityContext activityContext = new ActivityContext();
                 activityContext.setInsertionIndex(actIndex);
                 insertionContext.setActivityContext(activityContext);
-                ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime);
+                ConstraintsStatus status = fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime, failedActivityConstraints, constraintManager);
                 if (status.equals(ConstraintsStatus.FULFILLED)) {
                     double additionalICostsAtActLevel = softActivityConstraint.getCosts(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime);
                     double additionalTransportationCosts = additionalTransportCostsCalculator.getCosts(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActStartTime);
@@ -163,7 +169,9 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
             actIndex++;
         }
         if(insertionIndex == InsertionData.NO_INDEX) {
-            return InsertionData.createEmptyInsertionData();
+            InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
+            emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints);
+            return emptyInsertionData;
         }
         InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver);
         deliveryAct2Insert.setTheoreticalEarliestOperationStartTime(bestTimeWindow.getStart());
@@ -173,4 +181,6 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator {
         insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
         return insertionData;
     }
+
+
 }
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 87c29c0e..e8698483 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
@@ -18,8 +18,10 @@
 package com.graphhopper.jsprit.core.algorithm.recreate;
 
 import com.graphhopper.jsprit.core.problem.JobActivityFactory;
-import com.graphhopper.jsprit.core.problem.constraint.*;
+import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager;
 import com.graphhopper.jsprit.core.problem.constraint.HardActivityConstraint.ConstraintsStatus;
+import com.graphhopper.jsprit.core.problem.constraint.SoftActivityConstraint;
+import com.graphhopper.jsprit.core.problem.constraint.SoftRouteConstraint;
 import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts;
 import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts;
 import com.graphhopper.jsprit.core.problem.driver.Driver;
@@ -36,16 +38,19 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
 import java.util.List;
 
 
-final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
+final class ShipmentInsertionCalculator extends AbstractInsertionCalculator {
 
     private static final Logger logger = LoggerFactory.getLogger(ShipmentInsertionCalculator.class);
 
-    private HardRouteConstraint hardRouteLevelConstraint;
+    private final ConstraintManager constraintManager;
 
-    private HardActivityConstraint hardActivityLevelConstraint;
+//    private HardRouteConstraint hardRouteLevelConstraint;
+//
+//    private HardActivityConstraint hardActivityLevelConstraint;
 
     private SoftRouteConstraint softRouteConstraint;
 
@@ -64,8 +69,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
     public ShipmentInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager) {
         super();
         this.activityInsertionCostsCalculator = activityInsertionCostsCalculator;
-        this.hardRouteLevelConstraint = constraintManager;
-        this.hardActivityLevelConstraint = constraintManager;
+        this.constraintManager = constraintManager;
         this.softActivityConstraint = constraintManager;
         this.softRouteConstraint = constraintManager;
         this.transportCosts = routingCosts;
@@ -99,9 +103,8 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
         /*
         check hard route constraints
          */
-        if (!hardRouteLevelConstraint.fulfilled(insertionContext)) {
-            return InsertionData.createEmptyInsertionData();
-        }
+        InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager);
+        if (noInsertion != null) return noInsertion;
         /*
         check soft route constraints
          */
@@ -132,6 +135,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
         //pickupShipmentLoop
         List activities = currentRoute.getTourActivities().getActivities();
 
+        List failedActivityConstraints = new ArrayList<>();
         while (!tourEnd) {
             TourActivity nextAct;
             if (i < activities.size()) {
@@ -148,7 +152,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
                 ActivityContext activityContext = new ActivityContext();
                 activityContext.setInsertionIndex(i);
                 insertionContext.setActivityContext(activityContext);
-                ConstraintsStatus pickupShipmentConstraintStatus = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime);
+                ConstraintsStatus pickupShipmentConstraintStatus = fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime, failedActivityConstraints, constraintManager);
                 if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) {
                     pickupInsertionNotFulfilledBreak = false;
                     continue;
@@ -194,7 +198,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
                         ActivityContext activityContext_ = new ActivityContext();
                         activityContext_.setInsertionIndex(j);
                         insertionContext.setActivityContext(activityContext_);
-                        ConstraintsStatus deliverShipmentConstraintStatus = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop);
+                        ConstraintsStatus deliverShipmentConstraintStatus = fulfilled(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop, failedActivityConstraints, constraintManager);
                         if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) {
                             double additionalDeliveryICosts = softActivityConstraint.getCosts(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop);
                             double deliveryAIC = calculate(insertionContext, prevAct_deliveryLoop, deliverShipment, nextAct_deliveryLoop, prevActEndTime_deliveryLoop);
@@ -230,7 +234,9 @@ final class ShipmentInsertionCalculator implements JobInsertionCostsCalculator {
             i++;
         }
         if (pickupInsertionIndex == InsertionData.NO_INDEX) {
-            return InsertionData.createEmptyInsertionData();
+            InsertionData emptyInsertionData = new InsertionData.NoInsertionFound();
+            emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints);
+            return emptyInsertionData;
         }
         InsertionData insertionData = new InsertionData(bestCost, pickupInsertionIndex, deliveryInsertionIndex, newVehicle, newDriver);
         pickupShipment.setTheoreticalEarliestOperationStartTime(bestPickupTimeWindow.getStart());
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 9190a2f2..ad522012 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
@@ -98,7 +98,7 @@ final class VehicleTypeDependentJobInsertionCalculator implements JobInsertionCo
         }
         Vehicle selectedVehicle = currentRoute.getVehicle();
         Driver selectedDriver = currentRoute.getDriver();
-        InsertionData bestIData = InsertionData.createEmptyInsertionData();
+        InsertionData bestIData = new InsertionData.NoInsertionFound();
         double bestKnownCost_ = bestKnownCost;
         Collection relevantVehicles = new ArrayList();
         if (!(selectedVehicle instanceof VehicleImpl.NoVehicle)) {
@@ -115,6 +115,7 @@ final class VehicleTypeDependentJobInsertionCalculator implements JobInsertionCo
             else depTime = v.getEarliestDeparture();
             InsertionData iData = insertionCalculator.getInsertionData(currentRoute, jobToInsert, v, depTime, selectedDriver, bestKnownCost_);
             if (iData instanceof InsertionData.NoInsertionFound) {
+                bestIData.getFailedConstraintNames().addAll(iData.getFailedConstraintNames());
                 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 37ccd7bb..f0c5e9a8 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
@@ -24,6 +24,7 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.List;
 
 
 public class InsertionListeners {
@@ -74,6 +75,14 @@ public class InsertionListeners {
         }
     }
 
+    public void informJobUnassignedListeners(Job unassigned, List reasons) {
+        for (InsertionListener l : listeners) {
+            if (l instanceof JobUnassignedListener) {
+                ((JobUnassignedListener) l).informJobUnassigned(unassigned, reasons);
+            }
+        }
+    }
+
     public void addListener(InsertionListener insertionListener) {
         listeners.add(insertionListener);
     }
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
new file mode 100644
index 00000000..55a2921d
--- /dev/null
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to GraphHopper GmbH under one or more contributor
+ * license agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * GraphHopper GmbH licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.graphhopper.jsprit.core.algorithm.recreate.listener;
+
+import com.graphhopper.jsprit.core.problem.job.Job;
+
+import java.util.Collection;
+
+/**
+ * Created by schroeder on 06/02/17.
+ */
+public interface JobUnassignedListener extends InsertionListener {
+
+    void informJobUnassigned(Job unassigned, Collection failedConstraintNames);
+
+}
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/ConstraintManager.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/ConstraintManager.java
index dcd19cd6..1275f825 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/ConstraintManager.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/ConstraintManager.java
@@ -37,6 +37,7 @@ import java.util.List;
  */
 public class ConstraintManager implements HardActivityConstraint, HardRouteConstraint, SoftActivityConstraint, SoftRouteConstraint {
 
+
     public static enum Priority {
         CRITICAL, HIGH, LOW
     }
@@ -45,7 +46,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
 
     private HardActivityLevelConstraintManager actLevelConstraintManager = new HardActivityLevelConstraintManager();
 
-    private HardRouteLevelConstraintManager routeLevelConstraintManager = new HardRouteLevelConstraintManager();
+    private HardRouteLevelConstraintManager hardRouteConstraintManager = new HardRouteLevelConstraintManager();
 
     private SoftActivityConstraintManager softActivityConstraintManager = new SoftActivityConstraintManager();
 
@@ -76,6 +77,25 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
         resolveConstraints(constraints);
     }
 
+    public Collection getHardRouteConstraints() {
+        return hardRouteConstraintManager.getConstraints();
+    }
+
+    public Collection getCriticalHardActivityConstraints() {
+        return actLevelConstraintManager.getCriticalConstraints();
+    }
+
+    public Collection getHighPrioHardActivityConstraints() {
+        return actLevelConstraintManager.getHighPrioConstraints();
+    }
+
+    public Collection getLowPrioHardActivityConstraints() {
+        return actLevelConstraintManager.getLowPrioConstraints();
+    }
+//    public Collection getHardActivityConstraints() {
+//        return actLevelConstraintManager.g;
+//    }
+
     public DependencyType[] getDependencyTypes() {
         return dependencyTypes;
     }
@@ -103,7 +123,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
                 constraintTypeKnown = true;
             }
             if (c instanceof HardRouteConstraint) {
-                routeLevelConstraintManager.addConstraint((HardRouteConstraint) c);
+                hardRouteConstraintManager.addConstraint((HardRouteConstraint) c);
                 constraintTypeKnown = true;
             }
             if (c instanceof SoftRouteConstraint) {
@@ -152,7 +172,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
     }
 
     public void addConstraint(HardRouteConstraint routeLevelConstraint) {
-        routeLevelConstraintManager.addConstraint(routeLevelConstraint);
+        hardRouteConstraintManager.addConstraint(routeLevelConstraint);
     }
 
     public void addConstraint(SoftActivityConstraint softActivityConstraint) {
@@ -165,7 +185,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
 
     @Override
     public boolean fulfilled(JobInsertionContext insertionContext) {
-        return routeLevelConstraintManager.fulfilled(insertionContext);
+        return hardRouteConstraintManager.fulfilled(insertionContext);
     }
 
     @Override
@@ -176,7 +196,7 @@ public class ConstraintManager implements HardActivityConstraint, HardRouteConst
     public Collection getConstraints() {
         List constraints = new ArrayList();
         constraints.addAll(actLevelConstraintManager.getAllConstraints());
-        constraints.addAll(routeLevelConstraintManager.getConstraints());
+        constraints.addAll(hardRouteConstraintManager.getConstraints());
         constraints.addAll(softActivityConstraintManager.getConstraints());
         constraints.addAll(softRouteConstraintManager.getConstraints());
         return Collections.unmodifiableCollection(constraints);
diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java
index 38a496b6..11be0994 100644
--- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java
+++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java
@@ -212,15 +212,16 @@ public class Service extends AbstractJob {
         }
 
         /**
-         * Set priority to service. Only 1 = high priority, 2 = medium and 3 = low are allowed.
+         * Set priority to service. Only 1 (very high) to 10 (very low) are allowed.
          * 

- * Default is 2 = medium. + * Default is 2. * * @param priority * @return builder */ public Builder setPriority(int priority) { - if(priority < 1 || priority > 3) throw new IllegalArgumentException("incorrect priority. only 1 = high, 2 = medium and 3 = low is allowed"); + if (priority < 1 || priority > 10) + throw new IllegalArgumentException("incorrect priority. only priority values from 1 to 10 are allowed where 1 = high and 10 is low"); this.priority = priority; return this; } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java index 3275e901..6b8bde5d 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Shipment.java @@ -269,7 +269,7 @@ public class Shipment extends AbstractJob { } /** - * Set priority to shipment. Only 1 = high priority, 2 = medium and 3 = low are allowed. + * Set priority to shipment. Only 1 (high) to 10 (low) are allowed. *

* Default is 2 = medium. * @@ -277,7 +277,8 @@ public class Shipment extends AbstractJob { * @return builder */ public Builder setPriority(int priority) { - if(priority < 1 || priority > 3) throw new IllegalArgumentException("incorrect priority. only 1 = high, 2 = medium and 3 = low is allowed"); + if (priority < 1 || priority > 10) + throw new IllegalArgumentException("incorrect priority. only 1 (very high) to 10 (very low) are allowed"); this.priority = priority; return this; } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrix.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrix.java index 80742c4c..5e6a5d1d 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrix.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/FastVehicleRoutingTransportCostsMatrix.java @@ -45,6 +45,8 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic private double[][][] matrix; + private final int noLocations; + /** * Creates a new builder returning the matrix-builder. *

If you want to consider symmetric matrices, set isSymmetric to true. @@ -59,6 +61,7 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic private Builder(int noLocations, boolean isSymmetric) { this.isSymmetric = isSymmetric; matrix = new double[noLocations][noLocations][2]; + this.noLocations = noLocations; } /** @@ -74,11 +77,11 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic return this; } - private void add(int fromIndex, int toIndex, int indicatorIndex, double distance) { + private void add(int fromIndex, int toIndex, int indicatorIndex, double value) { if (isSymmetric) { - if (fromIndex < toIndex) matrix[fromIndex][toIndex][indicatorIndex] = distance; - else matrix[toIndex][fromIndex][indicatorIndex] = distance; - } else matrix[fromIndex][toIndex][indicatorIndex] = distance; + if (fromIndex < toIndex) matrix[fromIndex][toIndex][indicatorIndex] = value; + else matrix[toIndex][fromIndex][indicatorIndex] = value; + } else matrix[fromIndex][toIndex][indicatorIndex] = value; } /** @@ -115,9 +118,12 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic private final double[][][] matrix; + private int noLocations; + private FastVehicleRoutingTransportCostsMatrix(Builder builder) { this.isSymmetric = builder.isSymmetric; matrix = builder.matrix; + noLocations = builder.noLocations; } /** @@ -174,4 +180,9 @@ public class FastVehicleRoutingTransportCostsMatrix extends AbstractForwardVehic return costParams.perDistanceUnit * getDistance(from.getIndex(), to.getIndex()) + costParams.perTransportTimeUnit * getTransportTime(from, to, departureTime, driver, vehicle); } + public int getNoLocations() { + return noLocations; + } + + } 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 new file mode 100644 index 00000000..34873590 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java @@ -0,0 +1,157 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jsprit.core.util; + +import com.graphhopper.jsprit.core.algorithm.recreate.listener.JobUnassignedListener; +import com.graphhopper.jsprit.core.problem.job.Job; +import org.apache.commons.math3.stat.Frequency; + +import java.util.*; + +/** + * Created by schroeder on 06/02/17. + */ +public class UnassignedJobReasonTracker implements JobUnassignedListener { + + Map reasons = new HashMap<>(); + + Map codesToReason = new HashMap<>(); + + Map failedConstraintNamesToCode = new HashMap<>(); + + Set constraintsToBeIgnored = new HashSet<>(); + + public UnassignedJobReasonTracker() { + codesToReason.put(1, "cannot serve required skill"); + codesToReason.put(2, "cannot be visited within time window"); + codesToReason.put(3, "does not fit into any vehicle due to capacity"); + codesToReason.put(4, "cannot be assigned due to max distance constraint of vehicle"); + + failedConstraintNamesToCode.put("HardSkillConstraint", 1); + failedConstraintNamesToCode.put("VehicleDependentTimeWindowConstraints", 2); + failedConstraintNamesToCode.put("ServiceLoadRouteLevelConstraint", 3); + failedConstraintNamesToCode.put("PickupAndDeliverShipmentLoadActivityLevelConstraint", 3); + failedConstraintNamesToCode.put("ServiceLoadActivityLevelConstraint", 3); + failedConstraintNamesToCode.put("MaxDistanceConstraint", 4); + } + + public void ignore(String simpleNameOfConstraint) { + constraintsToBeIgnored.add(simpleNameOfConstraint); + } + + @Override + public void informJobUnassigned(Job unassigned, Collection failedConstraintNames) { + if (!this.reasons.containsKey(unassigned.getId())) { + this.reasons.put(unassigned.getId(), new Frequency()); + } + for (String r : failedConstraintNames) { + if (constraintsToBeIgnored.contains(r)) continue; + this.reasons.get(unassigned.getId()).addValue(r); + } + } + + public void put(String simpleNameOfFailedConstraint, int code, String reason) { + if (code <= 20) + throw new IllegalArgumentException("first 20 codes are reserved internally. choose a code > 20"); + codesToReason.put(code, reason); + if (failedConstraintNamesToCode.containsKey(simpleNameOfFailedConstraint)) { + throw new IllegalArgumentException(simpleNameOfFailedConstraint + " already assigned to code and reason"); + } else failedConstraintNamesToCode.put(simpleNameOfFailedConstraint, code); + } + + /** + * For each job id, it returns frequency distribution of failed constraints (simple name of constraint) in an unmodifiable map. + * + * @return + */ + public Map getReasons() { + return Collections.unmodifiableMap(reasons); + } + + /** + * Returns an unmodifiable map of codes and reason pairs. + * + * @return + */ + public Map getCodesToReason() { + return Collections.unmodifiableMap(codesToReason); + } + + /** + * Returns an unmodifiable map of constraint names (simple name of constraint) and reason code pairs. + * + * @return + */ + public Map getFailedConstraintNamesToCode() { + return Collections.unmodifiableMap(failedConstraintNamesToCode); + } + + /** + * Returns the most likely reason code i.e. the reason (failed constraint) being observed most often. + * + * 1 --> "cannot serve required skill + * 2 --> "cannot be visited within time window" + * 3 --> "does not fit into any vehicle due to capacity" + * 4 --> "cannot be assigned due to max distance constraint of vehicle" + * + * @param jobId + * @return + */ + public int getMostLikelyReasonCode(String jobId) { + Frequency reasons = this.reasons.get(jobId); + String mostLikelyReason = getMostLikely(reasons); + return toCode(mostLikelyReason); + } + + /** + * Returns the most likely reason i.e. the reason (failed constraint) being observed most often. + * + * @param jobId + * @return + */ + public String getMostLikelyReason(String jobId) { + Frequency reasons = this.reasons.get(jobId); + String mostLikelyReason = getMostLikely(reasons); + int code = toCode(mostLikelyReason); + if (code == -1) return mostLikelyReason; + else return codesToReason.get(code); + } + + private int toCode(String mostLikelyReason) { + if (failedConstraintNamesToCode.containsKey(mostLikelyReason)) + return failedConstraintNamesToCode.get(mostLikelyReason); + else return -1; + } + + private String getMostLikely(Frequency reasons) { + Iterator, Long>> entryIterator = reasons.entrySetIterator(); + int maxCount = 0; + String mostLikely = null; + while (entryIterator.hasNext()) { + Map.Entry, Long> entry = entryIterator.next(); + if (entry.getValue() > maxCount) { + Comparable key = entry.getKey(); + mostLikely = key.toString(); + } + } + return mostLikely; + } + + +} diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java index 70dd2ee3..9713744a 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java @@ -178,7 +178,7 @@ public class ShipmentInsertionCalculatorTest { insertionCalculator.setJobActivityFactory(activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); - assertEquals(InsertionData.createEmptyInsertionData(), iData); + assertTrue(iData instanceof InsertionData.NoInsertionFound); } diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ServiceTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ServiceTest.java index bec514e2..f1efce37 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ServiceTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ServiceTest.java @@ -250,6 +250,13 @@ public class ServiceTest { Assert.assertEquals(3, s.getPriority()); } + @Test + public void whenSettingPriorities_itShouldBeSetCorrectly3() { + Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) + .setPriority(10).build(); + Assert.assertEquals(10, s.getPriority()); + } + @Test public void whenNotSettingPriorities_defaultShouldBe2(){ Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc")) diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ShipmentTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ShipmentTest.java index 6c463d04..cbfa1f26 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ShipmentTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/job/ShipmentTest.java @@ -401,6 +401,14 @@ public class ShipmentTest { Assert.assertEquals(3, s.getPriority()); } + @Test + public void whenSettingPriorities_itShouldBeSetCorrectly3() { + Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) + .setDeliveryLocation(Location.newInstance("loc")) + .setPriority(10).build(); + Assert.assertEquals(10, s.getPriority()); + } + @Test public void whenNotSettingPriorities_defaultShouldBe2(){ Shipment s = Shipment.Builder.newInstance("s").setPickupLocation(Location.newInstance("loc")) 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 new file mode 100644 index 00000000..aee8a95d --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java @@ -0,0 +1,156 @@ +/* + * Licensed to GraphHopper GmbH under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. + * + * GraphHopper GmbH licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.graphhopper.jsprit.core.util; + +import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; +import com.graphhopper.jsprit.core.algorithm.box.Jsprit; +import com.graphhopper.jsprit.core.algorithm.state.StateId; +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.MaxDistanceConstraint; +import com.graphhopper.jsprit.core.problem.cost.TransportDistance; +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; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleType; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; +import org.apache.commons.math3.stat.Frequency; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Created by schroeder on 06/02/17. + */ +public class UnassignedJobReasonTrackerTest { + + Vehicle vehicle; + + @Before + public void doBefore() { + VehicleTypeImpl.Builder vehicleTypeBuilder = VehicleTypeImpl.Builder.newInstance("vehicleType").addCapacityDimension(0, 1); + VehicleType vehicleType = vehicleTypeBuilder.build(); + VehicleImpl.Builder vehicleBuilder = VehicleImpl.Builder.newInstance("vehicle"); + vehicleBuilder.setStartLocation(Location.newInstance(10, 10)); + vehicleBuilder.setType(vehicleType); + vehicleBuilder.setEarliestStart(0).setLatestArrival(100); + vehicle = vehicleBuilder.build(); + } + + @Test + public void shouldReturnCorrectCapacityReasonCode() { + Service service = Service.Builder.newInstance("1").addSizeDimension(0, 5).setLocation(Location.newInstance(5, 7)).build(); + + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE).addVehicle(vehicle).addJob(service) + .build(); + + VehicleRoutingAlgorithm vra = Jsprit.createAlgorithm(vrp); + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + vra.addListener(reasonTracker); + + VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); + Assert.assertEquals(1, solution.getUnassignedJobs().size()); + Assert.assertEquals(3, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId())); + } + + @Test + public void shouldReturnCorrectSkillReasonCode() { + Service service = Service.Builder.newInstance("1").addSizeDimension(0, 1).addRequiredSkill("ice").setLocation(Location.newInstance(5, 7)).build(); + + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE).addVehicle(vehicle).addJob(service) + .build(); + + VehicleRoutingAlgorithm vra = Jsprit.createAlgorithm(vrp); + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + vra.addListener(reasonTracker); + + VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); + Assert.assertEquals(1, solution.getUnassignedJobs().size()); + Assert.assertEquals(1, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId())); + } + + @Test + public void shouldReturnCorrectTWReasonCode() { + Service service = Service.Builder.newInstance("1").addSizeDimension(0, 1).setTimeWindow(TimeWindow.newInstance(110, 200)).setLocation(Location.newInstance(5, 7)).build(); + + VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE).addVehicle(vehicle).addJob(service) + .build(); + + VehicleRoutingAlgorithm vra = Jsprit.createAlgorithm(vrp); + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + vra.addListener(reasonTracker); + + VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); + Assert.assertEquals(1, solution.getUnassignedJobs().size()); + Assert.assertEquals(2, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId())); + } + + @Test + public void shouldReturnCorrectMaxDistanceReasonCode() { + Service service = Service.Builder.newInstance("1").setLocation(Location.newInstance(51, 0)).build(); + + Vehicle vehicle = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance(0, 0)).build(); + + final VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE).addVehicle(vehicle).addJob(service).build(); + + StateManager stateManager = new StateManager(vrp); + ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); + StateId maxDistance = stateManager.createStateId("max-distance"); + Map distMap = new HashMap<>(); + distMap.put(vehicle, 100d); + MaxDistanceConstraint distanceConstraint = new MaxDistanceConstraint(stateManager, maxDistance, new TransportDistance() { + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return vrp.getTransportCosts().getTransportCost(from, to, departureTime, null, vehicle); + } + }, distMap); + constraintManager.addConstraint(distanceConstraint, ConstraintManager.Priority.CRITICAL); + + VehicleRoutingAlgorithm vra = Jsprit.Builder.newInstance(vrp).setStateAndConstraintManager(stateManager, constraintManager) + .buildAlgorithm(); + UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker(); + vra.addListener(reasonTracker); + + VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions()); + Assert.assertEquals(1, solution.getUnassignedJobs().size()); + Assert.assertEquals(4, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId())); + } + + @Test + public void testFreq() { + Frequency frequency = new Frequency(); + frequency.addValue("VehicleDependentTimeWindowHardActivityConstraint"); + frequency.addValue("b"); + frequency.addValue("VehicleDependentTimeWindowHardActivityConstraint"); + + Iterator, Long>> entryIterator = frequency.entrySetIterator(); + while (entryIterator.hasNext()) { + Map.Entry, Long> e = entryIterator.next(); + System.out.println(e.getKey().toString() + " " + e.getValue()); + } + } +} diff --git a/jsprit-examples/pom.xml b/jsprit-examples/pom.xml index 3680ba94..23457bb6 100644 --- a/jsprit-examples/pom.xml +++ b/jsprit-examples/pom.xml @@ -20,7 +20,7 @@ com.graphhopper jsprit - 1.7-SNAPSHOT + 1.7.1-SNAPSHOT 4.0.0 diff --git a/jsprit-instances/pom.xml b/jsprit-instances/pom.xml index 04a8e8f4..7d31f92e 100644 --- a/jsprit-instances/pom.xml +++ b/jsprit-instances/pom.xml @@ -20,7 +20,7 @@ com.graphhopper jsprit - 1.7-SNAPSHOT + 1.7.1-SNAPSHOT 4.0.0 diff --git a/jsprit-io/pom.xml b/jsprit-io/pom.xml index 8487e20a..257aed74 100644 --- a/jsprit-io/pom.xml +++ b/jsprit-io/pom.xml @@ -23,7 +23,7 @@ com.graphhopper jsprit - 1.7-SNAPSHOT + 1.7.1-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index ed207968..773d477a 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ com.graphhopper jsprit - 1.7-SNAPSHOT + 1.7.1-SNAPSHOT pom