From f7088b8ba2348362a1aece9ac746975b55f32dc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Schr=C3=B6der?= Date: Thu, 12 Jan 2017 11:28:43 +0100 Subject: [PATCH 01/22] v1.7 --- WHATS_NEW.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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** From bbde9f47516924c58f264a0580cf287934376be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Schr=C3=B6der?= Date: Thu, 12 Jan 2017 11:32:16 +0100 Subject: [PATCH 02/22] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) 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: From 332009e3bc2d59dc3526cd2448ab32cf8cf486d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Schr=C3=B6der?= Date: Fri, 13 Jan 2017 00:31:32 +0100 Subject: [PATCH 03/22] Update Getting-Started.md --- docs/Getting-Started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index c49fe090..3efb5586 100644 --- a/docs/Getting-Started.md +++ b/docs/Getting-Started.md @@ -15,7 +15,7 @@ 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>1.7</version>
 </dependency>
 
From 6d4f26c5ae5934ac986530e9420f391f1f467fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Schr=C3=B6der?= Date: Fri, 13 Jan 2017 09:22:03 +0100 Subject: [PATCH 04/22] Update Getting-Started.md --- docs/Getting-Started.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index 3efb5586..f57de61d 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</version>
+   <version>{version}</version>
 </dependency>
 
+Find the latest versions here: [maven search](https://search.maven.org/#search%7Cga%7C1%7Cjsprit). + ####Build yourself If you want to build the master branch yourself, do this: From 0e4b8ed5d79ece5807847453734a74b94d2ea5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Schr=C3=B6der?= Date: Fri, 13 Jan 2017 09:24:03 +0100 Subject: [PATCH 05/22] Update Getting-Started.md --- docs/Getting-Started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index f57de61d..de791933 100644 --- a/docs/Getting-Started.md +++ b/docs/Getting-Started.md @@ -19,7 +19,7 @@ If you want to use the latest release of jsprit-core, add the following lines to </dependency> -Find the latest versions here: [maven search](https://search.maven.org/#search%7Cga%7C1%7Cjsprit). +Find the latest versions here: [maven search](https://search.maven.org/#search%7Cga%7C1%7Cjsprit) or [mvn repository](https://mvnrepository.com/artifact/com.graphhopper/jsprit-core). ####Build yourself If you want to build the master branch yourself, do this: From 9c85f674924fa932d1184fcc96ce249dc9e1ea4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Schr=C3=B6der?= Date: Sat, 14 Jan 2017 11:37:36 +0100 Subject: [PATCH 06/22] Update Getting-Started.md --- docs/Getting-Started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index de791933..44528c2b 100644 --- a/docs/Getting-Started.md +++ b/docs/Getting-Started.md @@ -19,7 +19,7 @@ If you want to use the latest release of jsprit-core, add the following lines to </dependency> -Find the latest versions here: [maven search](https://search.maven.org/#search%7Cga%7C1%7Cjsprit) or [mvn repository](https://mvnrepository.com/artifact/com.graphhopper/jsprit-core). +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: From 802a20a90c9fd0361e55a9d114eef60cc61f7698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Schr=C3=B6der?= Date: Sat, 14 Jan 2017 11:37:57 +0100 Subject: [PATCH 07/22] Update Getting-Started.md --- docs/Getting-Started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Getting-Started.md b/docs/Getting-Started.md index 44528c2b..ac5af692 100644 --- a/docs/Getting-Started.md +++ b/docs/Getting-Started.md @@ -19,7 +19,7 @@ If you want to use the latest release of jsprit-core, add the following lines to </dependency> -Find the latest versions here:[mvn repository](https://mvnrepository.com/artifact/com.graphhopper/jsprit-core). +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: From 7d2881f4c17347409dc6745aa07113226e4251fb Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 30 Jan 2017 20:02:34 +0100 Subject: [PATCH 08/22] allow more priority levels - up to 10 --- .../com/graphhopper/jsprit/core/problem/job/Service.java | 7 ++++--- .../com/graphhopper/jsprit/core/problem/job/Shipment.java | 5 +++-- .../graphhopper/jsprit/core/problem/job/ServiceTest.java | 7 +++++++ .../graphhopper/jsprit/core/problem/job/ShipmentTest.java | 8 ++++++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Service.java index 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/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")) From d7b4122e10c7df4b7d3aabce0be7a721f187bcc5 Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 30 Jan 2017 20:05:49 +0100 Subject: [PATCH 09/22] account for more prio levels in scorer and obj function --- .../com/graphhopper/jsprit/core/algorithm/box/Jsprit.java | 2 +- .../graphhopper/jsprit/core/algorithm/recreate/Scorer.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) 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 3ad56495..472bb792 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 @@ -720,7 +720,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/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; } From a2787ca3908e250c628dc2ee4270892ce97aea0a Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 6 Feb 2017 21:28:44 +0100 Subject: [PATCH 10/22] add google guava - related to #180 --- jsprit-core/pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jsprit-core/pom.xml b/jsprit-core/pom.xml index f3d79fc0..3ae778a0 100644 --- a/jsprit-core/pom.xml +++ b/jsprit-core/pom.xml @@ -42,6 +42,12 @@ ${logger.version} + + com.google.guava + guava + 21.0 + + From ba6dbfae456edd55a34b0262b49e5db91089944f Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 6 Feb 2017 21:29:22 +0100 Subject: [PATCH 11/22] add unassigned job listener - related to #180 --- .../recreate/listener/InsertionListeners.java | 9 ++++++ .../listener/JobUnassignedListener.java | 32 +++++++++++++++++++ .../problem/constraint/ConstraintManager.java | 30 ++++++++++++++--- .../ShipmentInsertionCalculatorTest.java | 2 +- 4 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/listener/JobUnassignedListener.java 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/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); } From 8da37cf4b382efd6041a825d6bdd0640a5543398 Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 6 Feb 2017 21:30:16 +0100 Subject: [PATCH 12/22] memorize failed constraint names - related to #180 --- .../recreate/AbstractInsertionCalculator.java | 95 +++++++++++++++++++ .../recreate/AbstractInsertionStrategy.java | 5 + .../algorithm/recreate/BestInsertion.java | 11 ++- .../recreate/BestInsertionConcurrent.java | 14 ++- .../algorithm/recreate/InsertionData.java | 11 +++ .../recreate/InsertionDataUpdater.java | 14 ++- .../algorithm/recreate/RegretInsertion.java | 30 +++--- .../recreate/RegretInsertionConcurrent.java | 18 ++-- .../RegretInsertionConcurrentFast.java | 14 +-- .../recreate/RegretInsertionFast.java | 16 ++-- .../core/algorithm/recreate/ScoredJob.java | 12 ++- .../recreate/ServiceInsertionCalculator.java | 32 ++++--- .../recreate/ShipmentInsertionCalculator.java | 30 +++--- ...leTypeDependentJobInsertionCalculator.java | 3 +- 14 files changed, 237 insertions(+), 68 deletions(-) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/AbstractInsertionCalculator.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 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/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_) { From 79ac22f7f4daa80c1cdae05532ff292f928f617f Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 6 Feb 2017 21:31:56 +0100 Subject: [PATCH 13/22] memorize reasons and translates constraint names into code and reason - related to #180 --- .../core/util/UnassignedJobReasonTracker.java | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java 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..49eb8cd4 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTracker.java @@ -0,0 +1,111 @@ +/* + * 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.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import com.graphhopper.jsprit.core.algorithm.recreate.listener.JobUnassignedListener; +import com.graphhopper.jsprit.core.problem.job.Job; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Created by schroeder on 06/02/17. + */ +public class UnassignedJobReasonTracker implements JobUnassignedListener { + + Map> reasons = new HashMap<>(); + + Map codesToReason = new HashMap<>(); + + Map failedConstraintNamesToCode = new HashMap<>(); + + 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); + } + + @Override + public void informJobUnassigned(Job unassigned, Collection failedConstraintNames) { + if (!this.reasons.containsKey(unassigned.getId())) { + Multiset ms = HashMultiset.create(); + this.reasons.put(unassigned.getId(), ms); + } + for (String r : failedConstraintNames) { + this.reasons.get(unassigned.getId()).add(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); + } + + public int getCode(Job job) { + Multiset reasons = this.reasons.get(job.getId()); + String mostLikelyReason = getMostLikely(reasons); + return toCode(mostLikelyReason); + } + + public String getReason(Job job) { + Multiset reasons = this.reasons.get(job.getId()); + 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(Multiset reasons) { + Set set = reasons.elementSet(); + int maxCount = 0; + String mostLikely = null; + for (String r : set) { + int count = reasons.count(r); + if (count > maxCount) { + mostLikely = r; + maxCount = count; + } + } + return mostLikely; + } + + +} From 0a8061845dee80c02041181321ce8340e87c012a Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 6 Feb 2017 21:48:47 +0100 Subject: [PATCH 14/22] use job id instead of job - related to #180 --- .../jsprit/core/util/UnassignedJobReasonTracker.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 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 49eb8cd4..137ab402 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 @@ -73,14 +73,14 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener { } else failedConstraintNamesToCode.put(simpleNameOfFailedConstraint, code); } - public int getCode(Job job) { - Multiset reasons = this.reasons.get(job.getId()); + public int getCode(String jobId) { + Multiset reasons = this.reasons.get(jobId); String mostLikelyReason = getMostLikely(reasons); return toCode(mostLikelyReason); } - public String getReason(Job job) { - Multiset reasons = this.reasons.get(job.getId()); + public String getReason(String jobId) { + Multiset reasons = this.reasons.get(jobId); String mostLikelyReason = getMostLikely(reasons); int code = toCode(mostLikelyReason); if (code == -1) return mostLikelyReason; From 9e9436e1abfe806d87b8b3af1e7c4c9b1df8e860 Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 6 Feb 2017 21:54:59 +0100 Subject: [PATCH 15/22] doc codes - related to #180 --- .../jsprit/core/util/UnassignedJobReasonTracker.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 137ab402..c558725e 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 @@ -73,6 +73,15 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener { } else failedConstraintNamesToCode.put(simpleNameOfFailedConstraint, code); } + /** + * 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 getCode(String jobId) { Multiset reasons = this.reasons.get(jobId); String mostLikelyReason = getMostLikely(reasons); From 6dbc35e3504a74594b19f5a224686ba152ab78cf Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 6 Feb 2017 22:15:53 +0100 Subject: [PATCH 16/22] test reason tracker - related to #180 --- .../util/UnassignedJobReasonTrackerTest.java | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java 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..1e43908b --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java @@ -0,0 +1,140 @@ +/* + * 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.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +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.getCode(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.getCode(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.getCode(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.getCode(solution.getUnassignedJobs().iterator().next().getId())); + } +} From ab8741a33513d52a5334e92e1c05fdf0101b05df Mon Sep 17 00:00:00 2001 From: oblonski Date: Tue, 7 Feb 2017 09:53:46 +0100 Subject: [PATCH 17/22] get rid of google guava dep --- jsprit-core/pom.xml | 6 ---- .../core/util/UnassignedJobReasonTracker.java | 30 +++++++++---------- .../util/UnassignedJobReasonTrackerTest.java | 16 ++++++++++ 3 files changed, 30 insertions(+), 22 deletions(-) diff --git a/jsprit-core/pom.xml b/jsprit-core/pom.xml index 3ae778a0..f3d79fc0 100644 --- a/jsprit-core/pom.xml +++ b/jsprit-core/pom.xml @@ -42,12 +42,6 @@ ${logger.version} - - com.google.guava - guava - 21.0 - - 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 c558725e..1a07c12b 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,22 +18,21 @@ package com.graphhopper.jsprit.core.util; -import com.google.common.collect.HashMultiset; -import com.google.common.collect.Multiset; 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.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; -import java.util.Set; /** * Created by schroeder on 06/02/17. */ public class UnassignedJobReasonTracker implements JobUnassignedListener { - Map> reasons = new HashMap<>(); + Map reasons = new HashMap<>(); Map codesToReason = new HashMap<>(); @@ -56,11 +55,10 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener { @Override public void informJobUnassigned(Job unassigned, Collection failedConstraintNames) { if (!this.reasons.containsKey(unassigned.getId())) { - Multiset ms = HashMultiset.create(); - this.reasons.put(unassigned.getId(), ms); + this.reasons.put(unassigned.getId(), new Frequency()); } for (String r : failedConstraintNames) { - this.reasons.get(unassigned.getId()).add(r); + this.reasons.get(unassigned.getId()).addValue(r); } } @@ -83,13 +81,13 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener { * @return */ public int getCode(String jobId) { - Multiset reasons = this.reasons.get(jobId); + Frequency reasons = this.reasons.get(jobId); String mostLikelyReason = getMostLikely(reasons); return toCode(mostLikelyReason); } public String getReason(String jobId) { - Multiset reasons = this.reasons.get(jobId); + Frequency reasons = this.reasons.get(jobId); String mostLikelyReason = getMostLikely(reasons); int code = toCode(mostLikelyReason); if (code == -1) return mostLikelyReason; @@ -102,15 +100,15 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener { else return -1; } - private String getMostLikely(Multiset reasons) { - Set set = reasons.elementSet(); + private String getMostLikely(Frequency reasons) { + Iterator, Long>> entryIterator = reasons.entrySetIterator(); int maxCount = 0; String mostLikely = null; - for (String r : set) { - int count = reasons.count(r); - if (count > maxCount) { - mostLikely = r; - maxCount = count; + 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/util/UnassignedJobReasonTrackerTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/util/UnassignedJobReasonTrackerTest.java index 1e43908b..3981eed3 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 @@ -34,11 +34,13 @@ 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; /** @@ -137,4 +139,18 @@ public class UnassignedJobReasonTrackerTest { Assert.assertEquals(1, solution.getUnassignedJobs().size()); Assert.assertEquals(4, reasonTracker.getCode(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()); + } + } } From f9e000c3ff76b843f94708a945d995a9399aa1bf Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 13 Feb 2017 12:51:27 +0100 Subject: [PATCH 18/22] rename current dev to 1.7.1-SNAPSHOT --- jsprit-analysis/pom.xml | 2 +- jsprit-core/pom.xml | 2 +- jsprit-examples/pom.xml | 2 +- jsprit-instances/pom.xml | 2 +- jsprit-io/pom.xml | 2 +- pom.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) 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-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 From 2953513e0fd1b1aa9353cd8bad1526ed2e3ff65f Mon Sep 17 00:00:00 2001 From: Felipe Zavan Date: Sat, 18 Feb 2017 01:30:39 -0200 Subject: [PATCH 19/22] Fix typo --- docs/Simple-Example.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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);

From 9453f0eb06fe46e39c15dc7ccc7bb930593a533e Mon Sep 17 00:00:00 2001
From: oblonski 
Date: Tue, 14 Mar 2017 09:55:35 +0100
Subject: [PATCH 20/22] make reason frequency accessible

---
 .../core/util/UnassignedJobReasonTracker.java | 44 ++++++++++++++++---
 .../util/UnassignedJobReasonTrackerTest.java  |  8 ++--
 2 files changed, 42 insertions(+), 10 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 1a07c12b..630d45ba 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
@@ -22,10 +22,7 @@ import com.graphhopper.jsprit.core.algorithm.recreate.listener.JobUnassignedList
 import com.graphhopper.jsprit.core.problem.job.Job;
 import org.apache.commons.math3.stat.Frequency;
 
-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.
@@ -72,6 +69,35 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener {
     }
 
     /**
+     * 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"
@@ -80,13 +106,19 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener {
      * @param jobId
      * @return
      */
-    public int getCode(String jobId) {
+    public int getMostLikelyReasonCode(String jobId) {
         Frequency reasons = this.reasons.get(jobId);
         String mostLikelyReason = getMostLikely(reasons);
         return toCode(mostLikelyReason);
     }
 
-    public String getReason(String jobId) {
+    /**
+     * 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);
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 3981eed3..aee8a95d 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
@@ -74,7 +74,7 @@ public class UnassignedJobReasonTrackerTest {
 
         VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions());
         Assert.assertEquals(1, solution.getUnassignedJobs().size());
-        Assert.assertEquals(3, reasonTracker.getCode(solution.getUnassignedJobs().iterator().next().getId()));
+        Assert.assertEquals(3, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId()));
     }
 
     @Test
@@ -90,7 +90,7 @@ public class UnassignedJobReasonTrackerTest {
 
         VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions());
         Assert.assertEquals(1, solution.getUnassignedJobs().size());
-        Assert.assertEquals(1, reasonTracker.getCode(solution.getUnassignedJobs().iterator().next().getId()));
+        Assert.assertEquals(1, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId()));
     }
 
     @Test
@@ -106,7 +106,7 @@ public class UnassignedJobReasonTrackerTest {
 
         VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions());
         Assert.assertEquals(1, solution.getUnassignedJobs().size());
-        Assert.assertEquals(2, reasonTracker.getCode(solution.getUnassignedJobs().iterator().next().getId()));
+        Assert.assertEquals(2, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId()));
     }
 
     @Test
@@ -137,7 +137,7 @@ public class UnassignedJobReasonTrackerTest {
 
         VehicleRoutingProblemSolution solution = Solutions.bestOf(vra.searchSolutions());
         Assert.assertEquals(1, solution.getUnassignedJobs().size());
-        Assert.assertEquals(4, reasonTracker.getCode(solution.getUnassignedJobs().iterator().next().getId()));
+        Assert.assertEquals(4, reasonTracker.getMostLikelyReasonCode(solution.getUnassignedJobs().iterator().next().getId()));
     }
 
     @Test

From 185980d8c0523c58e0f2205b371c249f0c72646c Mon Sep 17 00:00:00 2001
From: oblonski 
Date: Tue, 14 Mar 2017 13:09:03 +0100
Subject: [PATCH 21/22] allow constraints to be ignored

---
 .../jsprit/core/util/UnassignedJobReasonTracker.java       | 7 +++++++
 1 file changed, 7 insertions(+)

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 630d45ba..34873590 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
@@ -35,6 +35,8 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener {
 
     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");
@@ -49,12 +51,17 @@ public class UnassignedJobReasonTracker implements JobUnassignedListener {
         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);
         }
     }

From 562be55ae6fc5820449561cb17c3a310ea2c76a5 Mon Sep 17 00:00:00 2001
From: oblonski 
Date: Wed, 15 Mar 2017 15:20:57 +0100
Subject: [PATCH 22/22] get no. locations

---
 ...astVehicleRoutingTransportCostsMatrix.java | 19 +++++++++++++++----
 1 file changed, 15 insertions(+), 4 deletions(-)

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; + } + + }