From 994b97b48b315275acd13b8d39b9d0bca1189a2e Mon Sep 17 00:00:00 2001 From: oblonski Date: Fri, 23 Feb 2018 09:56:11 +0100 Subject: [PATCH 01/17] fixed issue #411 --- .../core/problem/VehicleRoutingProblem.java | 15 ++-- .../core/problem/vehicle/VehicleTypeImpl.java | 75 +++++++++++++------ .../problem/VehicleRoutingProblemTest.java | 11 +++ .../problem/vehicle/VehicleTypeImplTest.java | 29 +++++++ 4 files changed, 102 insertions(+), 28 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java index c08e05cc..27574a3f 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblem.java @@ -87,7 +87,7 @@ public class VehicleRoutingProblem { private FleetSize fleetSize = FleetSize.INFINITE; - private Collection vehicleTypes = new ArrayList(); + private Map vehicleTypes = new HashMap<>(); private Collection initialRoutes = new ArrayList(); @@ -395,8 +395,13 @@ public class VehicleRoutingProblem { incVehicleTypeIdIndexCounter(); } uniqueVehicles.add(vehicle); - if (!vehicleTypes.contains(vehicle.getType())) { - vehicleTypes.add(vehicle.getType()); + if (!vehicleTypes.containsKey(vehicle.getType().getTypeId())) { + vehicleTypes.put(vehicle.getType().getTypeId(), vehicle.getType()); + } else { + VehicleType existingType = vehicleTypes.get(vehicle.getType().getTypeId()); + if (!vehicle.getType().equals(existingType)) { + throw new IllegalArgumentException("A type with type id " + vehicle.getType().getTypeId() + " already exists. However, types are different. Please use unique vehicle types only."); + } } String startLocationId = vehicle.getStartLocation().getId(); addLocationToTentativeLocations(vehicle.getStartLocation()); @@ -495,7 +500,7 @@ public class VehicleRoutingProblem { * @return collection of vehicle-types */ public Collection getAddedVehicleTypes() { - return Collections.unmodifiableCollection(vehicleTypes); + return Collections.unmodifiableCollection(vehicleTypes.values()); } /** @@ -587,7 +592,7 @@ public class VehicleRoutingProblem { this.jobs = builder.jobs; this.fleetSize = builder.fleetSize; this.vehicles = builder.uniqueVehicles; - this.vehicleTypes = builder.vehicleTypes; + this.vehicleTypes = builder.vehicleTypes.values(); this.initialVehicleRoutes = builder.initialRoutes; this.transportCosts = builder.transportCosts; this.activityCosts = builder.activityCosts; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java index 112c137c..931d6187 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImpl.java @@ -81,6 +81,37 @@ public class VehicleTypeImpl implements VehicleType { public String toString() { return "[fixed=" + fix + "][perTime=" + perTransportTimeUnit + "][perDistance=" + perDistanceUnit + "][perWaitingTimeUnit=" + perWaitingTimeUnit + "]"; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VehicleCostParams)) return false; + + VehicleCostParams that = (VehicleCostParams) o; + + if (Double.compare(that.fix, fix) != 0) return false; + if (Double.compare(that.perTransportTimeUnit, perTransportTimeUnit) != 0) return false; + if (Double.compare(that.perDistanceUnit, perDistanceUnit) != 0) return false; + if (Double.compare(that.perWaitingTimeUnit, perWaitingTimeUnit) != 0) return false; + return Double.compare(that.perServiceTimeUnit, perServiceTimeUnit) == 0; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(fix); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perTransportTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perDistanceUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perWaitingTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(perServiceTimeUnit); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } } /** @@ -299,32 +330,30 @@ public class VehicleTypeImpl implements VehicleType { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((typeId == null) ? 0 : typeId.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof VehicleTypeImpl)) return false; + + VehicleTypeImpl that = (VehicleTypeImpl) o; + + if (Double.compare(that.maxVelocity, maxVelocity) != 0) return false; + if (!typeId.equals(that.typeId)) return false; + if (profile != null ? !profile.equals(that.profile) : that.profile != null) return false; + if (!vehicleCostParams.equals(that.vehicleCostParams)) return false; + return capacityDimensions.equals(that.capacityDimensions); } - /** - * Two vehicle-types are equal if they have the same vehicleId. - */ @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - VehicleTypeImpl other = (VehicleTypeImpl) obj; - if (typeId == null) { - if (other.typeId != null) - return false; - } else if (!typeId.equals(other.typeId)) - return false; - return true; + public int hashCode() { + int result; + long temp; + result = typeId.hashCode(); + result = 31 * result + (profile != null ? profile.hashCode() : 0); + result = 31 * result + vehicleCostParams.hashCode(); + result = 31 * result + capacityDimensions.hashCode(); + temp = Double.doubleToLongBits(maxVelocity); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; } private final String typeId; diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java index 7e4dbec2..046f1934 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/VehicleRoutingProblemTest.java @@ -316,6 +316,17 @@ public class VehicleRoutingProblemTest { builder.addVehicle(vehicle2); } + @Test(expected = IllegalArgumentException.class) + public void whenAddingVehicleTypesWithSameIdButDifferentCosts_itShouldThrowException() { + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + VehicleType type1 = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleType type2 = VehicleTypeImpl.Builder.newInstance("type").setCostPerServiceTime(2d).build(); + VehicleImpl vehicle1 = VehicleImpl.Builder.newInstance("v1").setStartLocation(Location.newInstance("loc")).setType(type1).build(); + VehicleImpl vehicle2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(Location.newInstance("loc")).setType(type2).build(); + builder.addVehicle(vehicle1); + builder.addVehicle(vehicle2); + } + @Test(expected = IllegalArgumentException.class) public void whenBuildingProblemWithSameBreakId_itShouldThrowException(){ VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java index 0c85b96a..bd145479 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleTypeImplTest.java @@ -124,6 +124,7 @@ public class VehicleTypeImplTest { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").setCostPerDistance(-10).build(); } + @Test public void whenSettingPerDistanceCosts_itShouldBeSetCorrectly() { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").setCostPerDistance(10).build(); assertEquals(10.0, type.getVehicleCostParams().perDistanceUnit, 0.0); @@ -166,4 +167,32 @@ public class VehicleTypeImplTest { assertEquals(42, two.getUserData()); assertNull(three.getUserData()); } + + @Test + public void typesShouldBeEqual() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + assertTrue(one.equals(two)); + } + + @Test + public void typesShouldBeNotEqual() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").setFixedCost(100).build(); + assertFalse(one.equals(two)); + } + + @Test + public void typesShouldBeNotEqual2() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 20).build(); + assertFalse(one.equals(two)); + } + + @Test + public void typesShouldBeEqual2() { + VehicleType one = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + VehicleType two = VehicleTypeImpl.Builder.newInstance("type").addCapacityDimension(0, 10).build(); + assertTrue(one.equals(two)); + } } From b7aabbbda1e3a056a057baeb929c781d17fd5492 Mon Sep 17 00:00:00 2001 From: Alastair Firth Date: Thu, 19 Apr 2018 10:09:35 +1000 Subject: [PATCH 02/17] Add OR-Tools --- docs/Other-Projects.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/Other-Projects.md b/docs/Other-Projects.md index 20e9a196..7447133b 100644 --- a/docs/Other-Projects.md +++ b/docs/Other-Projects.md @@ -18,6 +18,9 @@ Hipster is an easy to use yet powerful and flexible type-safe library for heuris #### [OscaR](https://bitbucket.org/oscarlib/oscar/wiki/Home) OscaR, an Open Source Toolbox for Optimising Logistics and Supply Chain Systems. +#### [OR-Tools](https://developers.google.com/optimization/) +Google Optimization Tools (OR-Tools) is a fast and portable software suite for solving combinatorial optimization problems, including the VRP. Open-source, written in C++ and available through SWIG for Python, Java, and .NET (using Mono on non-Windows platforms). You can compile OR-Tools on Linux, Mac OS X, and Windows (with Visual Studio). + ### Territory Design #### [OpenDoorLogistics](http://www.opendoorlogistics.com) From 6b716779cf5d93452f478ab270cc666dbc12e0bb Mon Sep 17 00:00:00 2001 From: Alastair Firth Date: Thu, 19 Apr 2018 10:18:53 +1000 Subject: [PATCH 03/17] clean up doc and add new link for VRPH --- docs/Other-Projects.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/Other-Projects.md b/docs/Other-Projects.md index 7447133b..705f5f63 100644 --- a/docs/Other-Projects.md +++ b/docs/Other-Projects.md @@ -1,7 +1,10 @@ +## Other Related Open Source Implementations + ### VRP -#### [Chris Groer's VRPH library](https://sites.google.com/site/vrphlibrary/) -An open source library for solving the capacitated vehicle routing problem written in C++. +#### [VRPH library](https://sites.google.com/site/vrphlibrary/) +An open source library by Chris Groer for solving the capacitated vehicle routing problem written in C++. +Now hosted by [coin-or](https://projects.coin-or.org/VRPH) #### [OptaPlanner](https://www.optaplanner.org/) OptaPlanner is a lightweight, embeddable planning engine written in Java™. It can be used to solve the capacitated vehicle routing problem (with time windows). @@ -27,6 +30,5 @@ Google Optimization Tools (OR-Tools) is a fast and portable software suite for s Open Door Logistics Studio is an easy-to-use standalone open source application for performing geographic analysis of your customer base and sales territory design, mapping and management. - - +### Other implementations If you know another promising open source implementation, report it. From 11b649a806a5596e505e0924e8f50a74d54bff35 Mon Sep 17 00:00:00 2001 From: Alastair Firth Date: Thu, 19 Apr 2018 10:24:49 +1000 Subject: [PATCH 04/17] alphabetize other projects --- docs/Other-Projects.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/Other-Projects.md b/docs/Other-Projects.md index 705f5f63..aa2dee26 100644 --- a/docs/Other-Projects.md +++ b/docs/Other-Projects.md @@ -2,27 +2,27 @@ ### VRP -#### [VRPH library](https://sites.google.com/site/vrphlibrary/) -An open source library by Chris Groer for solving the capacitated vehicle routing problem written in C++. -Now hosted by [coin-or](https://projects.coin-or.org/VRPH) - -#### [OptaPlanner](https://www.optaplanner.org/) -OptaPlanner is a lightweight, embeddable planning engine written in Java™. It can be used to solve the capacitated vehicle routing problem (with time windows). +#### [Hipster4j](http://www.hipster4j.org/) +Hipster is an easy to use yet powerful and flexible type-safe library for heuristic search, written in pure Java. It relies on a flexible model with generic operators to define search problems. So you can also model and solve vehicle routing problems. #### [Open-VRP](https://github.com/mck-/Open-VRP) Open-VRP is a framework to model and solve various vehicle routing problems. -#### [VROOM](https://github.com/jcoupey/vroom) -VROOM is an optimization engine written in C++14 that aim at providing good solutions to various real-life vehicle routing problems within a small computing time. It is free software, distributed under the term of the GNU General Public License V3. +#### [OptaPlanner](https://www.optaplanner.org/) +OptaPlanner is a lightweight, embeddable planning engine written in Java™. It can be used to solve the capacitated vehicle routing problem (with time windows). -#### [Hipster4j](http://www.hipster4j.org/) -Hipster is an easy to use yet powerful and flexible type-safe library for heuristic search, written in pure Java. It relies on a flexible model with generic operators to define search problems. So you can also model and solve vehicle routing problems. +#### [OR-Tools](https://developers.google.com/optimization/) +Google Optimization Tools (OR-Tools) is a fast and portable software suite for solving combinatorial optimization problems, including the VRP. Open-source, written in C++ and available through SWIG for Python, Java, and .NET (using Mono on non-Windows platforms). You can compile OR-Tools on Linux, Mac OS X, and Windows (with Visual Studio). #### [OscaR](https://bitbucket.org/oscarlib/oscar/wiki/Home) OscaR, an Open Source Toolbox for Optimising Logistics and Supply Chain Systems. -#### [OR-Tools](https://developers.google.com/optimization/) -Google Optimization Tools (OR-Tools) is a fast and portable software suite for solving combinatorial optimization problems, including the VRP. Open-source, written in C++ and available through SWIG for Python, Java, and .NET (using Mono on non-Windows platforms). You can compile OR-Tools on Linux, Mac OS X, and Windows (with Visual Studio). +#### [VROOM](https://github.com/jcoupey/vroom) +VROOM is an optimization engine written in C++14 that aim at providing good solutions to various real-life vehicle routing problems within a small computing time. It is free software, distributed under the term of the GNU General Public License V3. + +#### [VRPH library](https://sites.google.com/site/vrphlibrary/) +An open source library by Chris Groer for solving the capacitated vehicle routing problem written in C++. +Now hosted by [coin-or](https://projects.coin-or.org/VRPH) ### Territory Design From 2547c9da1e7f2c5576f052790e810f9b97ea0dea Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 23 Apr 2018 11:40:24 +0200 Subject: [PATCH 05/17] refined javadoc - fix #420 --- .../com/graphhopper/jsprit/core/problem/job/Break.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Break.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Break.java index 0a73b398..367ea7dc 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Break.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/job/Break.java @@ -22,7 +22,7 @@ import com.graphhopper.jsprit.core.problem.Capacity; import com.graphhopper.jsprit.core.problem.Skills; /** - * Pickup extends Service and is intended to model a Service where smth is LOADED (i.e. picked up) to a transport unit. + * Break extends Service and is intended to model a time-window based driver break. * * @author schroeder */ @@ -31,7 +31,7 @@ public class Break extends Service { public static class Builder extends Service.Builder { /** - * Returns a new instance of builder that builds a pickup. + * Returns a new instance of builder that builds a break. * * @param id the id of the pickup * @return the builder @@ -47,9 +47,9 @@ public class Break extends Service { } /** - * Builds Pickup. + * Builds Break. *

- *

Pickup type is "pickup" + *

Pickup type is "break" * * @return pickup * @throws IllegalStateException if neither locationId nor coordinate has been set From 4d30511d0f9c268dd0ac269b3f580dc6d09e2eea Mon Sep 17 00:00:00 2001 From: oblonski Date: Fri, 20 Jul 2018 10:25:28 +0200 Subject: [PATCH 06/17] change visibility of insertion cost calc --- .../recreate/LocalActivityInsertionCostsCalculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java index 0f91eab1..3e0725fe 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/LocalActivityInsertionCostsCalculator.java @@ -37,7 +37,7 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; * * @author stefan */ -class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCalculator { +public class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCalculator { private VehicleRoutingTransportCosts routingCosts; From 3980340ed21dda1101cfb76401057b356509a063 Mon Sep 17 00:00:00 2001 From: oblonski Date: Fri, 20 Jul 2018 10:25:55 +0200 Subject: [PATCH 07/17] rename InsertionBuilder --- .../jsprit/core/algorithm/box/Jsprit.java | 24 +- .../algorithm/recreate/InsertionBuilder.java | 5 +- .../recreate/InsertionStrategyBuilder.java | 212 ++++++++++++++++++ 3 files changed, 228 insertions(+), 13 deletions(-) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java 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 14e33fd3..92386314 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 @@ -526,8 +526,8 @@ public class Jsprit { boolean fastRegret = Boolean.parseBoolean(getProperty(Parameter.FAST_REGRET.toString())); if (es != null) { if(fastRegret){ - RegretInsertionConcurrentFast regretInsertion = (RegretInsertionConcurrentFast) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.REGRET) + RegretInsertionConcurrentFast regretInsertion = (RegretInsertionConcurrentFast) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET) .setConcurrentMode(es, noThreads) .setFastRegret(true) .considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()))) @@ -540,8 +540,8 @@ public class Jsprit { regret = regretInsertion; } else { - RegretInsertionConcurrent regretInsertion = (RegretInsertionConcurrent) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.REGRET) + RegretInsertionConcurrent regretInsertion = (RegretInsertionConcurrent) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET) .setConcurrentMode(es, noThreads) .considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()))) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) @@ -553,8 +553,8 @@ public class Jsprit { } } else { if(fastRegret) { - RegretInsertionFast regretInsertion = (RegretInsertionFast) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.REGRET) + RegretInsertionFast regretInsertion = (RegretInsertionFast) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET) .setFastRegret(true) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) .considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()))) @@ -566,8 +566,8 @@ public class Jsprit { regret = regretInsertion; } else{ - RegretInsertion regretInsertion = (RegretInsertion) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.REGRET) + RegretInsertion regretInsertion = (RegretInsertion) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.REGRET) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) .considerFixedCosts(toDouble(getProperty(Parameter.FIXED_COST_PARAM.toString()))) .setActivityInsertionCostCalculator(activityInsertion) @@ -581,16 +581,16 @@ public class Jsprit { AbstractInsertionStrategy best; if (vrp.getJobs().size() < 250 || es == null) { - BestInsertion bestInsertion = (BestInsertion) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.BEST) + BestInsertion bestInsertion = (BestInsertion) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.BEST) .considerFixedCosts(Double.valueOf(properties.getProperty(Parameter.FIXED_COST_PARAM.toString()))) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) .setActivityInsertionCostCalculator(activityInsertion) .build(); best = bestInsertion; } else { - BestInsertionConcurrent bestInsertion = (BestInsertionConcurrent) new InsertionBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) - .setInsertionStrategy(InsertionBuilder.Strategy.BEST) + BestInsertionConcurrent bestInsertion = (BestInsertionConcurrent) new InsertionStrategyBuilder(vrp, vehicleFleetManager, stateManager, constraintManager) + .setInsertionStrategy(InsertionStrategyBuilder.Strategy.BEST) .considerFixedCosts(Double.valueOf(properties.getProperty(Parameter.FIXED_COST_PARAM.toString()))) .setAllowVehicleSwitch(toBoolean(getProperty(Parameter.VEHICLE_SWITCH.toString()))) .setConcurrentMode(es, noThreads) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionBuilder.java index 98e30be4..3a30411a 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionBuilder.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionBuilder.java @@ -28,7 +28,10 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; - +/** + * use InsertionStrategyBuilder instead + */ +@Deprecated public class InsertionBuilder { private boolean fastRegret; diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java new file mode 100644 index 00000000..5ad9deb3 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java @@ -0,0 +1,212 @@ +/* + * 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.algorithm.listener.VehicleRoutingAlgorithmListeners; +import com.graphhopper.jsprit.core.algorithm.recreate.listener.InsertionListener; +import com.graphhopper.jsprit.core.algorithm.state.StateManager; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleFleetManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; + + +public class InsertionStrategyBuilder { + + private boolean fastRegret; + + + public enum Strategy { + REGRET, BEST + } + + private VehicleRoutingProblem vrp; + + private StateManager stateManager; + + private boolean local = true; + + private ConstraintManager constraintManager; + + private VehicleFleetManager fleetManager; + + private double weightOfFixedCosts; + + private boolean considerFixedCosts = false; + + private ActivityInsertionCostsCalculator actInsertionCostsCalculator = null; + + private int forwaredLooking; + + private int memory; + + private ExecutorService executor; + + private int nuOfThreads; + + private double timeSlice; + + private int nNeighbors; + + private boolean timeScheduling = false; + + private boolean allowVehicleSwitch = true; + + private boolean addDefaultCostCalc = true; + + private Strategy strategy = Strategy.BEST; + + private boolean isFastRegret = false; + + public InsertionStrategyBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager, ConstraintManager constraintManager) { + super(); + this.vrp = vrp; + this.stateManager = stateManager; + this.constraintManager = constraintManager; + this.fleetManager = vehicleFleetManager; + } + + public InsertionStrategyBuilder setInsertionStrategy(Strategy strategy) { + this.strategy = strategy; + return this; + } + + public InsertionStrategyBuilder setRouteLevel(int forwardLooking, int memory) { + local = false; + this.forwaredLooking = forwardLooking; + this.memory = memory; + return this; + } + + public InsertionStrategyBuilder setRouteLevel(int forwardLooking, int memory, boolean addDefaultMarginalCostCalculation) { + local = false; + this.forwaredLooking = forwardLooking; + this.memory = memory; + this.addDefaultCostCalc = addDefaultMarginalCostCalculation; + return this; + } + + public InsertionStrategyBuilder setFastRegret(boolean fastRegret) { + this.isFastRegret = fastRegret; + return this; + } + + + public InsertionStrategyBuilder setLocalLevel() { + local = true; + return this; + } + + /** + * If addDefaulMarginalCostCalculation is false, no calculator is set which implicitly assumes that marginal cost calculation + * is controlled by your custom soft constraints. + * + * @param addDefaultMarginalCostCalculation + * @return + */ + public InsertionStrategyBuilder setLocalLevel(boolean addDefaultMarginalCostCalculation) { + local = true; + addDefaultCostCalc = addDefaultMarginalCostCalculation; + return this; + } + + public InsertionStrategyBuilder considerFixedCosts(double weightOfFixedCosts) { + this.weightOfFixedCosts = weightOfFixedCosts; + this.considerFixedCosts = true; + return this; + } + + public InsertionStrategyBuilder setActivityInsertionCostCalculator(ActivityInsertionCostsCalculator activityInsertionCostsCalculator) { + this.actInsertionCostsCalculator = activityInsertionCostsCalculator; + return this; + } + + public InsertionStrategyBuilder setConcurrentMode(ExecutorService executor, int nuOfThreads) { + this.executor = executor; + this.nuOfThreads = nuOfThreads; + return this; + } + + + public InsertionStrategy build() { + List iListeners = new ArrayList(); + List algorithmListeners = new ArrayList(); + JobInsertionCostsCalculatorBuilder calcBuilder = new JobInsertionCostsCalculatorBuilder(iListeners, algorithmListeners); + if (local) { + calcBuilder.setLocalLevel(addDefaultCostCalc); + } else { + calcBuilder.setRouteLevel(forwaredLooking, memory, addDefaultCostCalc); + } + calcBuilder.setConstraintManager(constraintManager); + calcBuilder.setStateManager(stateManager); + calcBuilder.setVehicleRoutingProblem(vrp); + calcBuilder.setVehicleFleetManager(fleetManager); + calcBuilder.setActivityInsertionCostsCalculator(actInsertionCostsCalculator); + if (considerFixedCosts) { + calcBuilder.considerFixedCosts(weightOfFixedCosts); + } + if (timeScheduling) { + calcBuilder.experimentalTimeScheduler(timeSlice, nNeighbors); + } + calcBuilder.setAllowVehicleSwitch(allowVehicleSwitch); + JobInsertionCostsCalculator costCalculator = calcBuilder.build(); + + InsertionStrategy insertion; + if (strategy.equals(Strategy.BEST)) { + if (executor == null) { + insertion = new BestInsertion(costCalculator, vrp); + } else { + insertion = new BestInsertionConcurrent(costCalculator, executor, nuOfThreads, vrp); + } + } else if (strategy.equals(Strategy.REGRET)) { + if (executor == null) { + if (isFastRegret) { + RegretInsertionFast regret = new RegretInsertionFast(costCalculator, vrp, fleetManager); + regret.setSwitchAllowed(allowVehicleSwitch); + insertion = regret; + } else { + RegretInsertion regret = new RegretInsertion(costCalculator, vrp); + insertion = regret; + } + + } else { + if (isFastRegret) { + RegretInsertionConcurrentFast regret = new RegretInsertionConcurrentFast(costCalculator, vrp, executor, fleetManager); + regret.setSwitchAllowed(allowVehicleSwitch); + insertion = regret; + } else { + RegretInsertionConcurrent regret = new RegretInsertionConcurrent(costCalculator, vrp, executor); + insertion = regret; + } + + } + } else throw new IllegalStateException("you should never get here"); + for (InsertionListener l : iListeners) insertion.addListener(l); + return insertion; + } + + public InsertionStrategyBuilder setAllowVehicleSwitch(boolean allowVehicleSwitch) { + this.allowVehicleSwitch = allowVehicleSwitch; + return this; + } + + +} From 89f864492f7539221a2ea8d10b78624dfe30dd67 Mon Sep 17 00:00:00 2001 From: oblonski Date: Tue, 24 Jul 2018 10:06:02 +0200 Subject: [PATCH 08/17] make job insertion more flexible --- .../recreate/BreakInsertionCalculator.java | 22 ++++---- .../algorithm/recreate/BreakScheduling.java | 3 +- .../recreate/JobInsertionCostsCalculator.java | 2 +- .../JobInsertionCostsCalculatorBuilder.java | 32 +++++++++--- .../JobInsertionCostsCalculatorFactory.java | 29 +++++++++++ .../recreate/ServiceInsertionCalculator.java | 25 ++++------ .../recreate/ShipmentInsertionCalculator.java | 25 ++++------ ...erviceInsertionAndLoadConstraintsTest.java | 11 ++-- .../ShipmentInsertionCalculatorTest.java | 50 ++++++++++--------- .../TestCalculatesServiceInsertion.java | 4 +- 10 files changed, 119 insertions(+), 84 deletions(-) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorFactory.java diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculator.java index f706f4a0..e94bad61 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculator.java @@ -49,25 +49,25 @@ final class BreakInsertionCalculator implements JobInsertionCostsCalculator { private static final Logger logger = LoggerFactory.getLogger(BreakInsertionCalculator.class); - private HardRouteConstraint hardRouteLevelConstraint; + private final HardRouteConstraint hardRouteLevelConstraint; - private HardActivityConstraint hardActivityLevelConstraint; + private final HardActivityConstraint hardActivityLevelConstraint; - private SoftRouteConstraint softRouteConstraint; + private final SoftRouteConstraint softRouteConstraint; - private SoftActivityConstraint softActivityConstraint; + private final SoftActivityConstraint softActivityConstraint; - private VehicleRoutingTransportCosts transportCosts; + private final VehicleRoutingTransportCosts transportCosts; private final VehicleRoutingActivityCosts activityCosts; - private ActivityInsertionCostsCalculator additionalTransportCostsCalculator; + private final ActivityInsertionCostsCalculator additionalTransportCostsCalculator; - private JobActivityFactory activityFactory; + private final JobActivityFactory activityFactory; - private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; + private final AdditionalAccessEgressCalculator additionalAccessEgressCalculator; - public BreakInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager) { + public BreakInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager, JobActivityFactory activityFactory) { super(); this.transportCosts = routingCosts; this.activityCosts = activityCosts; @@ -77,12 +77,10 @@ final class BreakInsertionCalculator implements JobInsertionCostsCalculator { softRouteConstraint = constraintManager; this.additionalTransportCostsCalculator = additionalTransportCostsCalculator; additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); + this.activityFactory = activityFactory; logger.debug("initialise " + this); } - public void setJobActivityFactory(JobActivityFactory jobActivityFactory) { - this.activityFactory = jobActivityFactory; - } @Override public String toString() { diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakScheduling.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakScheduling.java index 90a7076f..66fbb182 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakScheduling.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakScheduling.java @@ -49,8 +49,7 @@ public class BreakScheduling implements InsertionStartsListener,JobInsertedListe public BreakScheduling(VehicleRoutingProblem vrp, StateManager stateManager, ConstraintManager constraintManager) { this.stateManager = stateManager; - this.breakInsertionCalculator = new BreakInsertionCalculator(vrp.getTransportCosts(),vrp.getActivityCosts(),new LocalActivityInsertionCostsCalculator(vrp.getTransportCosts(),vrp.getActivityCosts(),stateManager),constraintManager); - this.breakInsertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory()); + this.breakInsertionCalculator = new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), new LocalActivityInsertionCostsCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), stateManager), constraintManager, vrp.getJobActivityFactory()); eventListeners = new EventListeners(); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculator.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculator.java index 81d1670c..8a1414e3 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculator.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculator.java @@ -25,6 +25,6 @@ import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; public interface JobInsertionCostsCalculator { - public InsertionData getInsertionData(VehicleRoute currentRoute, Job newJob, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownCosts); + InsertionData getInsertionData(VehicleRoute currentRoute, Job newJob, Vehicle newVehicle, double newVehicleDepartureTime, Driver newDriver, double bestKnownCosts); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java index 1135adb8..011cfe76 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java @@ -94,6 +94,27 @@ public class JobInsertionCostsCalculatorBuilder { private boolean addDefaultCostCalc = true; + private JobInsertionCostsCalculatorFactory shipmentCalculatorFactory = new JobInsertionCostsCalculatorFactory() { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new ShipmentInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } + }; + + private JobInsertionCostsCalculatorFactory serviceCalculatorFactory = new JobInsertionCostsCalculatorFactory() { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new ServiceInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } + }; + + private JobInsertionCostsCalculatorFactory breakCalculatorFactory = new JobInsertionCostsCalculatorFactory() { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } + }; + /** * Constructs the builder. *

@@ -287,20 +308,17 @@ public class JobInsertionCostsCalculatorBuilder { } }; - ShipmentInsertionCalculator shipmentInsertion = new ShipmentInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(),actInsertionCalc, constraintManager); - shipmentInsertion.setJobActivityFactory(activityFactory); - ServiceInsertionCalculator serviceInsertion = new ServiceInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), actInsertionCalc, constraintManager); - serviceInsertion.setJobActivityFactory(activityFactory); - BreakInsertionCalculator breakInsertionCalculator = new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), actInsertionCalc, constraintManager); - breakInsertionCalculator.setJobActivityFactory(activityFactory); + JobInsertionCostsCalculator shipmentInsertion = shipmentCalculatorFactory.create(vrp, actInsertionCalc, activityFactory, constraintManager); + JobInsertionCostsCalculator serviceInsertion = serviceCalculatorFactory.create(vrp, actInsertionCalc, activityFactory, constraintManager); + JobInsertionCostsCalculator breakInsertion = breakCalculatorFactory.create(vrp, actInsertionCalc, activityFactory, constraintManager); JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); switcher.put(Shipment.class, shipmentInsertion); switcher.put(Service.class, serviceInsertion); switcher.put(Pickup.class, serviceInsertion); switcher.put(Delivery.class, serviceInsertion); - switcher.put(Break.class, breakInsertionCalculator); + switcher.put(Break.class, breakInsertion); CalculatorPlusListeners calculatorPlusListeners = new CalculatorPlusListeners(switcher); if (configLocal != null) { diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorFactory.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorFactory.java new file mode 100644 index 00000000..b683b88d --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorFactory.java @@ -0,0 +1,29 @@ +/* + * 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.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; + +public interface JobInsertionCostsCalculatorFactory { + + JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager); + +} 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 9f91ff9b..6007decb 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 @@ -55,38 +55,35 @@ final class ServiceInsertionCalculator extends AbstractInsertionCalculator { // private HardActivityConstraint hardActivityLevelConstraint; - private SoftRouteConstraint softRouteConstraint; + private final SoftRouteConstraint softRouteConstraint; - private SoftActivityConstraint softActivityConstraint; + private final SoftActivityConstraint softActivityConstraint; - private VehicleRoutingTransportCosts transportCosts; + private final VehicleRoutingTransportCosts transportCosts; private final VehicleRoutingActivityCosts activityCosts; - private ActivityInsertionCostsCalculator additionalTransportCostsCalculator; + private final ActivityInsertionCostsCalculator activityInsertionCostsCalculator; - private JobActivityFactory activityFactory; + private final JobActivityFactory activityFactory; - private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; + private final AdditionalAccessEgressCalculator additionalAccessEgressCalculator; - private ConstraintManager constraintManager; + private final ConstraintManager constraintManager; - public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager) { + public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager, JobActivityFactory activityFactory) { super(); this.transportCosts = routingCosts; this.activityCosts = activityCosts; this.constraintManager = constraintManager; softActivityConstraint = constraintManager; softRouteConstraint = constraintManager; - this.additionalTransportCostsCalculator = additionalTransportCostsCalculator; + this.activityInsertionCostsCalculator = activityInsertionCostsCalculator; additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); + this.activityFactory = activityFactory; logger.debug("initialise {}", this); } - public void setJobActivityFactory(JobActivityFactory jobActivityFactory) { - this.activityFactory = jobActivityFactory; - } - @Override public String toString() { return "[name=calculatesServiceInsertion]"; @@ -151,7 +148,7 @@ final class ServiceInsertionCalculator extends AbstractInsertionCalculator { 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); + double additionalTransportationCosts = activityInsertionCostsCalculator.getCosts(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActStartTime); if (additionalICostsAtRouteLevel + additionalICostsAtActLevel + additionalTransportationCosts < bestCost) { bestCost = additionalICostsAtRouteLevel + additionalICostsAtActLevel + additionalTransportationCosts; insertionIndex = actIndex; 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 e8698483..d55ff150 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 @@ -48,25 +48,21 @@ final class ShipmentInsertionCalculator extends AbstractInsertionCalculator { private final ConstraintManager constraintManager; -// private HardRouteConstraint hardRouteLevelConstraint; -// -// private HardActivityConstraint hardActivityLevelConstraint; + private final SoftRouteConstraint softRouteConstraint; - private SoftRouteConstraint softRouteConstraint; + private final SoftActivityConstraint softActivityConstraint; - private SoftActivityConstraint softActivityConstraint; + private final ActivityInsertionCostsCalculator activityInsertionCostsCalculator; - private ActivityInsertionCostsCalculator activityInsertionCostsCalculator; + private final VehicleRoutingTransportCosts transportCosts; - private VehicleRoutingTransportCosts transportCosts; + private final VehicleRoutingActivityCosts activityCosts; - private VehicleRoutingActivityCosts activityCosts; + private final JobActivityFactory activityFactory; - private JobActivityFactory activityFactory; + private final AdditionalAccessEgressCalculator additionalAccessEgressCalculator; - private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; - - public ShipmentInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager) { + public ShipmentInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager, JobActivityFactory jobActivityFactory) { super(); this.activityInsertionCostsCalculator = activityInsertionCostsCalculator; this.constraintManager = constraintManager; @@ -75,13 +71,10 @@ final class ShipmentInsertionCalculator extends AbstractInsertionCalculator { this.transportCosts = routingCosts; this.activityCosts = activityCosts; additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); + this.activityFactory = jobActivityFactory; logger.debug("initialise {}", this); } - public void setJobActivityFactory(JobActivityFactory activityFactory) { - this.activityFactory = activityFactory; - } - @Override public String toString() { return "[name=calculatesServiceInsertion]"; diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java index 5a1a3468..3536558a 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java @@ -90,7 +90,7 @@ public class ServiceInsertionAndLoadConstraintsTest { ActivityInsertionCostsCalculator activityInsertionCostsCalculator; - ShipmentInsertionCalculator insertionCalculator; +// ShipmentInsertionCalculator insertionCalculator; VehicleRoutingProblem vehicleRoutingProblem; @@ -109,7 +109,7 @@ public class ServiceInsertionAndLoadConstraintsTest { private void createInsertionCalculator(HardRouteConstraint hardRouteLevelConstraint) { ConstraintManager constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); constraintManager.addConstraint(hardRouteLevelConstraint); - insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); +// insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, ); } @Test @@ -143,10 +143,9 @@ public class ServiceInsertionAndLoadConstraintsTest { stateManager.informInsertionStarts(Arrays.asList(route), null); JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); - ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - serviceInsertionCalc.setJobActivityFactory(activityFactory); - ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - insertionCalculator.setJobActivityFactory(activityFactory); + ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + switcher.put(Pickup.class, serviceInsertionCalc); switcher.put(Delivery.class, serviceInsertionCalc); 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 9713744a..274bb5fe 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 @@ -94,21 +94,24 @@ public class ShipmentInsertionCalculatorTest { Vehicle vehicle; + ConstraintManager constraintManager; + @Before public void doBefore() { routingCosts = CostFactory.createManhattanCosts(); VehicleType type = VehicleTypeImpl.Builder.newInstance("t").addCapacityDimension(0, 2).setCostPerDistance(1).build(); vehicle = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance("0,0")).setType(type).build(); activityInsertionCostsCalculator = new LocalActivityInsertionCostsCalculator(routingCosts, activityCosts, mock(StateManager.class)); - createInsertionCalculator(hardRouteLevelConstraint); + constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); + constraintManager.addConstraint(hardRouteLevelConstraint); vehicleRoutingProblem = mock(VehicleRoutingProblem.class); } - private void createInsertionCalculator(HardRouteConstraint hardRouteLevelConstraint) { - ConstraintManager constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); - constraintManager.addConstraint(hardRouteLevelConstraint); - insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - } +// private void createInsertionCalculator(HardRouteConstraint hardRouteLevelConstraint) { +// ConstraintManager constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); +// constraintManager.addConstraint(hardRouteLevelConstraint); +// insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, ); +// } @Test public void whenCalculatingInsertionCostsOfShipment_itShouldReturnCorrectCostValue() { @@ -119,7 +122,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment)); activities.add(new DeliverShipment(shipment)); when(activityFactory.createActivities(shipment)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment, vehicle, 0.0, null, Double.MAX_VALUE); assertEquals(40.0, iData.getInsertionCost(), 0.05); } @@ -137,7 +140,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment2)); activities.add(new DeliverShipment(shipment2)); when(activityFactory.createActivities(shipment2)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); assertEquals(0.0, iData.getInsertionCost(), 0.05); @@ -161,7 +164,9 @@ public class ShipmentInsertionCalculatorTest { VehicleRoute route = VehicleRoute.emptyRoute(); when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); new Inserter(new InsertionListeners(), vehicleRoutingProblem).insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); - createInsertionCalculator(new HardRouteConstraint() { + + constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); + constraintManager.addConstraint(new HardRouteConstraint() { @Override public boolean fulfilled(JobInsertionContext insertionContext) { @@ -175,7 +180,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment2)); activities.add(new DeliverShipment(shipment2)); when(activityFactory.createActivities(shipment2)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); assertTrue(iData instanceof InsertionData.NoInsertionFound); @@ -201,7 +206,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment3)); activities.add(new DeliverShipment(shipment3)); when(activityFactory.createActivities(shipment3)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); assertEquals(0.0, iData.getInsertionCost(), 0.05); @@ -226,7 +231,7 @@ public class ShipmentInsertionCalculatorTest { activities.add(new PickupShipment(shipment3)); activities.add(new DeliverShipment(shipment3)); when(activityFactory.createActivities(shipment3)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); @@ -260,8 +265,7 @@ public class ShipmentInsertionCalculatorTest { constraintManager.addConstraint(new ShipmentPickupsFirstConstraint(), ConstraintManager.Priority.CRITICAL); ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, - activityInsertionCostsCalculator, constraintManager); - insertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory()); + activityInsertionCostsCalculator, constraintManager, vrp.getJobActivityFactory()); InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, DriverImpl.noDriver(), Double.MAX_VALUE); assertTrue(iData instanceof InsertionData.NoInsertionFound); @@ -293,22 +297,20 @@ public class ShipmentInsertionCalculatorTest { stateManager.informInsertionStarts(Arrays.asList(route), null); - JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); - ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); - switcher.put(Pickup.class, serviceInsertionCalc); - switcher.put(Service.class, serviceInsertionCalc); - switcher.put(Shipment.class, insertionCalculator); - -// Service service = Service.Builder.newInstance("pick", 1).setLocationId("5,5").build(); Pickup service = (Pickup) Pickup.Builder.newInstance("pick").addSizeDimension(0, 1).setLocation(Location.newInstance("5,5")).build(); JobActivityFactory activityFactory = mock(JobActivityFactory.class); List activities = new ArrayList(); activities.add(new PickupService(service)); when(activityFactory.createActivities(service)).thenReturn(activities); - insertionCalculator.setJobActivityFactory(activityFactory); - serviceInsertionCalc.setJobActivityFactory(activityFactory); + + JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); + ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + switcher.put(Pickup.class, serviceInsertionCalc); + switcher.put(Service.class, serviceInsertionCalc); + switcher.put(Shipment.class, insertionCalculator); + InsertionData iData = switcher.getInsertionData(route, service, vehicle, 0, DriverImpl.noDriver(), Double.MAX_VALUE); // routeActVisitor.visit(route); diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java index 6345e700..fe26084e 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java @@ -136,13 +136,13 @@ public class TestCalculatesServiceInsertion { VehicleRoutingActivityCosts actCosts = mock(VehicleRoutingActivityCosts.class); - serviceInsertion = new ServiceInsertionCalculator(costs, vrp.getActivityCosts(), new LocalActivityInsertionCostsCalculator(costs, actCosts, states), cManager); - serviceInsertion.setJobActivityFactory(new JobActivityFactory() { + serviceInsertion = new ServiceInsertionCalculator(costs, vrp.getActivityCosts(), new LocalActivityInsertionCostsCalculator(costs, actCosts, states), cManager, new JobActivityFactory() { @Override public List createActivities(Job job) { return vrp.copyAndGetActivities(job); } }); + } @Test From d6e5ca38a950abfedb92d8d1cbec1c5bc65628f1 Mon Sep 17 00:00:00 2001 From: oblonski Date: Tue, 24 Jul 2018 10:48:07 +0200 Subject: [PATCH 09/17] make job insertion more flexible --- .../recreate/InsertionStrategyBuilder.java | 24 +++++++++++++++++++ .../JobInsertionCostsCalculatorBuilder.java | 18 ++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java index 5ad9deb3..56c8cd7f 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java @@ -76,6 +76,12 @@ public class InsertionStrategyBuilder { private boolean isFastRegret = false; + private JobInsertionCostsCalculatorFactory shipmentInsertionCalculatorFactory; + + private JobInsertionCostsCalculatorFactory serviceInsertionCalculatorFactory; + + private JobInsertionCostsCalculatorFactory breakInsertionCalculatorFactory; + public InsertionStrategyBuilder(VehicleRoutingProblem vrp, VehicleFleetManager vehicleFleetManager, StateManager stateManager, ConstraintManager constraintManager) { super(); this.vrp = vrp; @@ -84,6 +90,18 @@ public class InsertionStrategyBuilder { this.fleetManager = vehicleFleetManager; } + public void setShipmentInsertionCalculatorFactory(JobInsertionCostsCalculatorFactory shipmentInsertionCalculatorFactory) { + this.shipmentInsertionCalculatorFactory = shipmentInsertionCalculatorFactory; + } + + public void setServiceInsertionCalculator(JobInsertionCostsCalculatorFactory serviceInsertionCalculator) { + this.serviceInsertionCalculatorFactory = serviceInsertionCalculator; + } + + public void setBreakInsertionCalculator(JobInsertionCostsCalculatorFactory breakInsertionCalculator) { + this.breakInsertionCalculatorFactory = breakInsertionCalculator; + } + public InsertionStrategyBuilder setInsertionStrategy(Strategy strategy) { this.strategy = strategy; return this; @@ -155,6 +173,12 @@ public class InsertionStrategyBuilder { } else { calcBuilder.setRouteLevel(forwaredLooking, memory, addDefaultCostCalc); } + if (shipmentInsertionCalculatorFactory != null) + calcBuilder.setShipmentCalculatorFactory(shipmentInsertionCalculatorFactory); + if (serviceInsertionCalculatorFactory != null) + calcBuilder.setServiceCalculatorFactory(serviceInsertionCalculatorFactory); + if (breakInsertionCalculatorFactory != null) + calcBuilder.setBreakCalculatorFactory(breakInsertionCalculatorFactory); calcBuilder.setConstraintManager(constraintManager); calcBuilder.setStateManager(stateManager); calcBuilder.setVehicleRoutingProblem(vrp); diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java index 011cfe76..e6d63ad4 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java @@ -130,6 +130,24 @@ public class JobInsertionCostsCalculatorBuilder { this.algorithmListeners = algorithmListeners; } + public JobInsertionCostsCalculatorBuilder setShipmentCalculatorFactory(JobInsertionCostsCalculatorFactory shipmentCalculatorFactory) { + if (shipmentCalculatorFactory == null) return this; + this.shipmentCalculatorFactory = shipmentCalculatorFactory; + return this; + } + + public JobInsertionCostsCalculatorBuilder setServiceCalculatorFactory(JobInsertionCostsCalculatorFactory serviceCalculatorFactory) { + if (serviceCalculatorFactory == null) return this; + this.serviceCalculatorFactory = serviceCalculatorFactory; + return this; + } + + public JobInsertionCostsCalculatorBuilder setBreakCalculatorFactory(JobInsertionCostsCalculatorFactory breakCalculatorFactory) { + if (breakCalculatorFactory == null) return this; + this.breakCalculatorFactory = breakCalculatorFactory; + return this; + } + /** * Sets activityStates. MUST be set. * From bac6939bd37fc152249047a7759cd414010aace1 Mon Sep 17 00:00:00 2001 From: oblonski Date: Tue, 24 Jul 2018 10:56:19 +0200 Subject: [PATCH 10/17] make job insertion more flexible --- .../algorithm/recreate/InsertionStrategyBuilder.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java index 56c8cd7f..310047fc 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/InsertionStrategyBuilder.java @@ -90,16 +90,19 @@ public class InsertionStrategyBuilder { this.fleetManager = vehicleFleetManager; } - public void setShipmentInsertionCalculatorFactory(JobInsertionCostsCalculatorFactory shipmentInsertionCalculatorFactory) { + public InsertionStrategyBuilder setShipmentInsertionCalculatorFactory(JobInsertionCostsCalculatorFactory shipmentInsertionCalculatorFactory) { this.shipmentInsertionCalculatorFactory = shipmentInsertionCalculatorFactory; + return this; } - public void setServiceInsertionCalculator(JobInsertionCostsCalculatorFactory serviceInsertionCalculator) { + public InsertionStrategyBuilder setServiceInsertionCalculator(JobInsertionCostsCalculatorFactory serviceInsertionCalculator) { this.serviceInsertionCalculatorFactory = serviceInsertionCalculator; + return this; } - public void setBreakInsertionCalculator(JobInsertionCostsCalculatorFactory breakInsertionCalculator) { + public InsertionStrategyBuilder setBreakInsertionCalculator(JobInsertionCostsCalculatorFactory breakInsertionCalculator) { this.breakInsertionCalculatorFactory = breakInsertionCalculator; + return this; } public InsertionStrategyBuilder setInsertionStrategy(Strategy strategy) { From b0f382198ed28ac80cbc16abc6f0103043b44abb Mon Sep 17 00:00:00 2001 From: oblonski Date: Tue, 31 Jul 2018 09:42:15 +0200 Subject: [PATCH 11/17] add flex shipment insertion calculator --- .../ShipmentInsertionCalculatorFlex.java | 274 ++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlex.java diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlex.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlex.java new file mode 100644 index 00000000..b490b246 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlex.java @@ -0,0 +1,274 @@ +/* + * 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.JobActivityFactory; +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; +import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.job.Shipment; +import com.graphhopper.jsprit.core.problem.misc.ActivityContext; +import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; +import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import com.graphhopper.jsprit.core.problem.solution.route.activity.End; +import com.graphhopper.jsprit.core.problem.solution.route.activity.Start; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + + +public final class ShipmentInsertionCalculatorFlex extends AbstractInsertionCalculator { + + private static final Logger logger = LoggerFactory.getLogger(ShipmentInsertionCalculatorFlex.class); + + private final ConstraintManager constraintManager; + +// private HardRouteConstraint hardRouteLevelConstraint; +// +// private HardActivityConstraint hardActivityLevelConstraint; + + private SoftRouteConstraint softRouteConstraint; + + private SoftActivityConstraint softActivityConstraint; + + private ActivityInsertionCostsCalculator activityInsertionCostsCalculator; + + private VehicleRoutingTransportCosts transportCosts; + + private VehicleRoutingActivityCosts activityCosts; + + private JobActivityFactory activityFactory; + + private AdditionalAccessEgressCalculator additionalAccessEgressCalculator; + + private int evalIndexPickup = Integer.MAX_VALUE; + private int evalIndexDelivery = Integer.MAX_VALUE; + + public void setEvalIndexPickup(int evalIndexPickup) { + this.evalIndexPickup = evalIndexPickup; + } + + public void setEvalIndexDelivery(int evalIndexDelivery) { + this.evalIndexDelivery = evalIndexDelivery; + } + + public ShipmentInsertionCalculatorFlex(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, ConstraintManager constraintManager) { + super(); + this.activityInsertionCostsCalculator = activityInsertionCostsCalculator; + this.constraintManager = constraintManager; + this.softActivityConstraint = constraintManager; + this.softRouteConstraint = constraintManager; + this.transportCosts = routingCosts; + this.activityCosts = activityCosts; + additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); + logger.debug("initialise {}", this); + } + + public void setJobActivityFactory(JobActivityFactory activityFactory) { + this.activityFactory = activityFactory; + } + + @Override + public String toString() { + return "[name=calculatesServiceInsertion]"; + } + + /** + * Calculates the marginal cost of inserting job i locally. This is based on the + * assumption that cost changes can entirely covered by only looking at the predecessor i-1 and its successor i+1. + */ + @Override + public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job jobToInsert, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownCosts) { + JobInsertionContext insertionContext = new JobInsertionContext(currentRoute, jobToInsert, newVehicle, newDriver, newVehicleDepartureTime); + Shipment shipment = (Shipment) jobToInsert; + TourActivity pickupShipment = activityFactory.createActivities(shipment).get(0); + TourActivity deliverShipment = activityFactory.createActivities(shipment).get(1); + insertionContext.getAssociatedActivities().add(pickupShipment); + insertionContext.getAssociatedActivities().add(deliverShipment); + + /* + check hard route constraints + */ + InsertionData noInsertion = checkRouteContraints(insertionContext, constraintManager); + if (noInsertion != null) return noInsertion; + /* + check soft route constraints + */ + + + double additionalICostsAtRouteLevel = softRouteConstraint.getCosts(insertionContext); + + double bestCost = bestKnownCosts; + additionalICostsAtRouteLevel += additionalAccessEgressCalculator.getCosts(insertionContext); + + int pickupInsertionIndex = InsertionData.NO_INDEX; + int deliveryInsertionIndex = InsertionData.NO_INDEX; + + TimeWindow bestPickupTimeWindow = null; + TimeWindow bestDeliveryTimeWindow = null; + + + Start start = new Start(newVehicle.getStartLocation(), newVehicle.getEarliestDeparture(), newVehicle.getLatestArrival()); + start.setEndTime(newVehicleDepartureTime); + End end = new End(newVehicle.getEndLocation(), 0.0, newVehicle.getLatestArrival()); + ActivityContext pickupContext = new ActivityContext(); + TourActivity prevAct = start; + double prevActEndTime = newVehicleDepartureTime; + + //loops + int i = 0; + boolean tourEnd = false; + //pickupShipmentLoop + List activities = currentRoute.getTourActivities().getActivities(); + + List failedActivityConstraints = new ArrayList<>(); + while (!tourEnd) { + TourActivity nextAct; + if (i < activities.size()) { + nextAct = activities.get(i); + } else { + nextAct = end; + tourEnd = true; + } + if (i > evalIndexPickup) break; + if (i == evalIndexPickup || evalIndexPickup == Integer.MAX_VALUE) { + boolean pickupInsertionNotFulfilledBreak = true; + for (TimeWindow pickupTimeWindow : shipment.getPickupTimeWindows()) { + pickupShipment.setTheoreticalEarliestOperationStartTime(pickupTimeWindow.getStart()); + pickupShipment.setTheoreticalLatestOperationStartTime(pickupTimeWindow.getEnd()); + ActivityContext activityContext = new ActivityContext(); + activityContext.setInsertionIndex(i); + insertionContext.setActivityContext(activityContext); + ConstraintsStatus pickupShipmentConstraintStatus = fulfilled(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime, failedActivityConstraints, constraintManager); + if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) { + pickupInsertionNotFulfilledBreak = false; + continue; + } else if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)) { + continue; + } else if (pickupShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) { + pickupInsertionNotFulfilledBreak = false; + } + double additionalPickupICosts = softActivityConstraint.getCosts(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime); + double pickupAIC = calculate(insertionContext, prevAct, pickupShipment, nextAct, prevActEndTime); + + double shipmentPickupArrTime = prevActEndTime + transportCosts.getTransportTime(prevAct.getLocation(), pickupShipment.getLocation(), prevActEndTime, newDriver, newVehicle); + double shipmentPickupEndTime = Math.max(shipmentPickupArrTime, pickupShipment.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(pickupShipment, shipmentPickupArrTime, newDriver, newVehicle); + + pickupContext.setArrivalTime(shipmentPickupArrTime); + pickupContext.setEndTime(shipmentPickupEndTime); + pickupContext.setInsertionIndex(i); + insertionContext.setRelatedActivityContext(pickupContext); + + TourActivity prevActForDeliveryLoop = pickupShipment; + double prevActEndTimeForDeliveryLoop = shipmentPickupEndTime; + + /* + -------------------------------- + */ + //deliverShipmentLoop + int j = i; + boolean tourEndInDeliveryLoop = false; + while (!tourEndInDeliveryLoop) { + TourActivity nextActForDeliveryLoop; + if (j < activities.size()) { + nextActForDeliveryLoop = activities.get(j); + } else { + nextActForDeliveryLoop = end; + tourEndInDeliveryLoop = true; + } + if (j > evalIndexDelivery) break; + if (j == evalIndexDelivery || evalIndexDelivery == Integer.MAX_VALUE) { + boolean deliveryInsertionNotFulfilledBreak = true; + for (TimeWindow deliveryTimeWindow : shipment.getDeliveryTimeWindows()) { + deliverShipment.setTheoreticalEarliestOperationStartTime(deliveryTimeWindow.getStart()); + deliverShipment.setTheoreticalLatestOperationStartTime(deliveryTimeWindow.getEnd()); + ActivityContext activityContext_ = new ActivityContext(); + activityContext_.setInsertionIndex(j); + insertionContext.setActivityContext(activityContext_); + ConstraintsStatus deliverShipmentConstraintStatus = fulfilled(insertionContext, prevActForDeliveryLoop, deliverShipment, nextActForDeliveryLoop, prevActEndTimeForDeliveryLoop, failedActivityConstraints, constraintManager); + if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.FULFILLED)) { + double additionalDeliveryICosts = softActivityConstraint.getCosts(insertionContext, prevActForDeliveryLoop, deliverShipment, nextActForDeliveryLoop, prevActEndTimeForDeliveryLoop); + double deliveryAIC = calculate(insertionContext, prevActForDeliveryLoop, deliverShipment, nextActForDeliveryLoop, prevActEndTimeForDeliveryLoop); + double totalActivityInsertionCosts = pickupAIC + deliveryAIC + + additionalICostsAtRouteLevel + additionalPickupICosts + additionalDeliveryICosts; + if (totalActivityInsertionCosts < bestCost) { + bestCost = totalActivityInsertionCosts; + pickupInsertionIndex = i; + deliveryInsertionIndex = j; + bestPickupTimeWindow = pickupTimeWindow; + bestDeliveryTimeWindow = deliveryTimeWindow; + } + deliveryInsertionNotFulfilledBreak = false; + } else if (deliverShipmentConstraintStatus.equals(ConstraintsStatus.NOT_FULFILLED)) { + deliveryInsertionNotFulfilledBreak = false; + } + } + if (deliveryInsertionNotFulfilledBreak) break; + } + //update prevAct and endTime + double nextActArrTime = prevActEndTimeForDeliveryLoop + transportCosts.getTransportTime(prevActForDeliveryLoop.getLocation(), nextActForDeliveryLoop.getLocation(), prevActEndTimeForDeliveryLoop, newDriver, newVehicle); + prevActEndTimeForDeliveryLoop = Math.max(nextActArrTime, nextActForDeliveryLoop.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextActForDeliveryLoop, nextActArrTime, newDriver, newVehicle); + prevActForDeliveryLoop = nextActForDeliveryLoop; + j++; + } + } + if (pickupInsertionNotFulfilledBreak) { + break; + } + } + + //update prevAct and endTime + double nextActArrTime = prevActEndTime + transportCosts.getTransportTime(prevAct.getLocation(), nextAct.getLocation(), prevActEndTime, newDriver, newVehicle); + prevActEndTime = Math.max(nextActArrTime, nextAct.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextAct, nextActArrTime, newDriver, newVehicle); + prevAct = nextAct; + i++; + } + + + if (pickupInsertionIndex == InsertionData.NO_INDEX) { + InsertionData emptyInsertionData = new InsertionData.NoInsertionFound(); + emptyInsertionData.getFailedConstraintNames().addAll(failedActivityConstraints); + return emptyInsertionData; + } + InsertionData insertionData = new InsertionData(bestCost, pickupInsertionIndex, deliveryInsertionIndex, newVehicle, newDriver); + pickupShipment.setTheoreticalEarliestOperationStartTime(bestPickupTimeWindow.getStart()); + pickupShipment.setTheoreticalLatestOperationStartTime(bestPickupTimeWindow.getEnd()); + deliverShipment.setTheoreticalEarliestOperationStartTime(bestDeliveryTimeWindow.getStart()); + deliverShipment.setTheoreticalLatestOperationStartTime(bestDeliveryTimeWindow.getEnd()); + insertionData.setVehicleDepartureTime(newVehicleDepartureTime); + insertionData.getEvents().add(new InsertActivity(currentRoute, newVehicle, deliverShipment, deliveryInsertionIndex)); + insertionData.getEvents().add(new InsertActivity(currentRoute, newVehicle, pickupShipment, pickupInsertionIndex)); + insertionData.getEvents().add(new SwitchVehicle(currentRoute, newVehicle, newVehicleDepartureTime)); + return insertionData; + } + + private double calculate(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double departureTimeAtPrevAct) { + return activityInsertionCostsCalculator.getCosts(iFacts, prevAct, nextAct, newAct, departureTimeAtPrevAct); + + } +} From e36ba0b34f10931beac066ef26bf32f309861d8c Mon Sep 17 00:00:00 2001 From: oblonski Date: Tue, 31 Jul 2018 09:42:51 +0200 Subject: [PATCH 12/17] add factories for job insertion --- .../BreakInsertionCalculatorFactory.java | 30 +++++++++++++++++++ .../JobInsertionCostsCalculatorBuilder.java | 21 ++----------- .../ServiceInsertionCalculatorFactory.java | 30 +++++++++++++++++++ .../recreate/ShipmentInsertionCalculator.java | 2 +- .../ShipmentInsertionCalculatorFactory.java | 30 +++++++++++++++++++ 5 files changed, 94 insertions(+), 19 deletions(-) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculatorFactory.java create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculatorFactory.java create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFactory.java diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculatorFactory.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculatorFactory.java new file mode 100644 index 00000000..0b550a47 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/BreakInsertionCalculatorFactory.java @@ -0,0 +1,30 @@ +/* + * 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.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; + +public class BreakInsertionCalculatorFactory implements JobInsertionCostsCalculatorFactory { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } +} diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java index e6d63ad4..faf760c0 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/JobInsertionCostsCalculatorBuilder.java @@ -94,26 +94,11 @@ public class JobInsertionCostsCalculatorBuilder { private boolean addDefaultCostCalc = true; - private JobInsertionCostsCalculatorFactory shipmentCalculatorFactory = new JobInsertionCostsCalculatorFactory() { - @Override - public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { - return new ShipmentInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); - } - }; + private JobInsertionCostsCalculatorFactory shipmentCalculatorFactory = new ShipmentInsertionCalculatorFactory(); - private JobInsertionCostsCalculatorFactory serviceCalculatorFactory = new JobInsertionCostsCalculatorFactory() { - @Override - public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { - return new ServiceInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); - } - }; + private JobInsertionCostsCalculatorFactory serviceCalculatorFactory = new ServiceInsertionCalculatorFactory(); - private JobInsertionCostsCalculatorFactory breakCalculatorFactory = new JobInsertionCostsCalculatorFactory() { - @Override - public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { - return new BreakInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); - } - }; + private JobInsertionCostsCalculatorFactory breakCalculatorFactory = new BreakInsertionCalculatorFactory(); /** * Constructs the builder. diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculatorFactory.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculatorFactory.java new file mode 100644 index 00000000..ca9252f2 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ServiceInsertionCalculatorFactory.java @@ -0,0 +1,30 @@ +/* + * 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.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; + +public class ServiceInsertionCalculatorFactory implements JobInsertionCostsCalculatorFactory { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new ServiceInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } +} 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 d55ff150..9eb5967c 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 @@ -77,7 +77,7 @@ final class ShipmentInsertionCalculator extends AbstractInsertionCalculator { @Override public String toString() { - return "[name=calculatesServiceInsertion]"; + return "[name=calculatesShipmentInsertion]"; } /** diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFactory.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFactory.java new file mode 100644 index 00000000..540918f1 --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFactory.java @@ -0,0 +1,30 @@ +/* + * 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.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; + +public class ShipmentInsertionCalculatorFactory implements JobInsertionCostsCalculatorFactory { + @Override + public JobInsertionCostsCalculator create(VehicleRoutingProblem vrp, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, JobActivityFactory jobActivityFactory, ConstraintManager constraintManager) { + return new ShipmentInsertionCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), activityInsertionCostsCalculator, constraintManager, jobActivityFactory); + } +} From 93a82d8fc041e7584e03d06bd45e470e64ea3a0f Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 17 Aug 2018 18:27:02 +0200 Subject: [PATCH 13/17] Hide email due to spamming --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 26e49a8f..dbca6aee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,8 +51,8 @@ script: notifications: email: - - github@graphhopper.com - + - $EMAIL + cache: directories: - $HOME/.m2 From 13156f7acc38c0feefcfecc8723794cf4189d479 Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 10 Sep 2018 12:38:13 +0200 Subject: [PATCH 14/17] test shipment insertion flex --- .../ShipmentInsertionCalculatorFlexTest.java | 329 ++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java new file mode 100644 index 00000000..9afc5022 --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java @@ -0,0 +1,329 @@ +/* + * 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.algorithm.recreate.listener.InsertionListeners; +import com.graphhopper.jsprit.core.algorithm.state.StateManager; +import com.graphhopper.jsprit.core.problem.AbstractActivity; +import com.graphhopper.jsprit.core.problem.JobActivityFactory; +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.constraint.HardRouteConstraint; +import com.graphhopper.jsprit.core.problem.constraint.PickupAndDeliverShipmentLoadActivityLevelConstraint; +import com.graphhopper.jsprit.core.problem.constraint.ShipmentPickupsFirstConstraint; +import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingActivityCosts; +import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts; +import com.graphhopper.jsprit.core.problem.driver.Driver; +import com.graphhopper.jsprit.core.problem.driver.DriverImpl; +import com.graphhopper.jsprit.core.problem.job.Pickup; +import com.graphhopper.jsprit.core.problem.job.Service; +import com.graphhopper.jsprit.core.problem.job.Shipment; +import com.graphhopper.jsprit.core.problem.misc.JobInsertionContext; +import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import com.graphhopper.jsprit.core.problem.solution.route.activity.DeliverShipment; +import com.graphhopper.jsprit.core.problem.solution.route.activity.PickupService; +import com.graphhopper.jsprit.core.problem.solution.route.activity.PickupShipment; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity; +import com.graphhopper.jsprit.core.problem.solution.route.state.RouteAndActivityStateGetter; +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 com.graphhopper.jsprit.core.util.CostFactory; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +public class ShipmentInsertionCalculatorFlexTest { + + VehicleRoutingTransportCosts routingCosts; + + VehicleRoutingProblem vehicleRoutingProblem; + + VehicleRoutingActivityCosts activityCosts = new VehicleRoutingActivityCosts() { + + @Override + public double getActivityCost(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle) { + return 0; + } + + @Override + public double getActivityDuration(TourActivity tourAct, double arrivalTime, Driver driver, Vehicle vehicle) { + return tourAct.getOperationTime(); + } + + }; + + HardRouteConstraint hardRouteLevelConstraint = new HardRouteConstraint() { + + @Override + public boolean fulfilled(JobInsertionContext insertionContext) { + return true; + } + + }; + + ActivityInsertionCostsCalculator activityInsertionCostsCalculator; + + ShipmentInsertionCalculatorFlex insertionCalculator; + + Vehicle vehicle; + + ConstraintManager constraintManager; + + @Before + public void doBefore() { + routingCosts = CostFactory.createManhattanCosts(); + VehicleType type = VehicleTypeImpl.Builder.newInstance("t").addCapacityDimension(0, 2).setCostPerDistance(1).build(); + vehicle = VehicleImpl.Builder.newInstance("v").setStartLocation(Location.newInstance("0,0")).setType(type).build(); + activityInsertionCostsCalculator = new LocalActivityInsertionCostsCalculator(routingCosts, activityCosts, mock(StateManager.class)); + constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); + constraintManager.addConstraint(hardRouteLevelConstraint); + vehicleRoutingProblem = mock(VehicleRoutingProblem.class); + } + +// private void createInsertionCalculator(HardRouteConstraint hardRouteLevelConstraint) { +// ConstraintManager constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); +// constraintManager.addConstraint(hardRouteLevelConstraint); +// insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, ); +// } + + @Test + public void whenCalculatingInsertionCostsOfShipment_itShouldReturnCorrectCostValue() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment)); + activities.add(new DeliverShipment(shipment)); + when(activityFactory.createActivities(shipment)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + InsertionData iData = insertionCalculator.getInsertionData(route, shipment, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(40.0, iData.getInsertionCost(), 0.05); + } + + @Test + public void whenCalculatingInsertionIntoExistingRoute_itShouldReturnCorrectCosts() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); + new Inserter(new InsertionListeners(), vehicleRoutingProblem).insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment2)); + activities.add(new DeliverShipment(shipment2)); + when(activityFactory.createActivities(shipment2)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(0.0, iData.getInsertionCost(), 0.05); + assertEquals(1, iData.getPickupInsertionIndex()); + assertEquals(2, iData.getDeliveryInsertionIndex()); + } + + private List getTourActivities(Shipment shipment) { + List acts = new ArrayList(); + PickupShipment pick = new PickupShipment(shipment); + DeliverShipment del = new DeliverShipment(shipment); + acts.add(pick); + acts.add(del); + return acts; + } + + @Test + public void whenInsertingShipmentInRouteWithNotEnoughCapacity_itShouldReturnNoInsertion() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); + new Inserter(new InsertionListeners(), vehicleRoutingProblem).insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + + constraintManager = new ConstraintManager(mock(VehicleRoutingProblem.class), mock(RouteAndActivityStateGetter.class)); + constraintManager.addConstraint(new HardRouteConstraint() { + + @Override + public boolean fulfilled(JobInsertionContext insertionContext) { + return false; + } + + }); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment2)); + activities.add(new DeliverShipment(shipment2)); + when(activityFactory.createActivities(shipment2)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); + assertTrue(iData instanceof InsertionData.NoInsertionFound); + + } + + + @Test + public void whenInsertingThirdShipment_itShouldCalcCorrectVal() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,0").build()).setDeliveryLocation(Location.newInstance("9,10")).build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); + when(vehicleRoutingProblem.copyAndGetActivities(shipment2)).thenReturn(getTourActivities(shipment2)); + Inserter inserter = new Inserter(new InsertionListeners(), vehicleRoutingProblem); + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment3)); + activities.add(new DeliverShipment(shipment3)); + when(activityFactory.createActivities(shipment3)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(0.0, iData.getInsertionCost(), 0.05); + assertEquals(0, iData.getPickupInsertionIndex()); + assertEquals(1, iData.getDeliveryInsertionIndex()); + } + + @Test + public void whenInsertingThirdShipment_itShouldCalcCorrectVal2() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,0").build()).setDeliveryLocation(Location.newInstance("9,9")).build(); + when(vehicleRoutingProblem.copyAndGetActivities(shipment)).thenReturn(getTourActivities(shipment)); + when(vehicleRoutingProblem.copyAndGetActivities(shipment2)).thenReturn(getTourActivities(shipment2)); + VehicleRoute route = VehicleRoute.emptyRoute(); + Inserter inserter = new Inserter(new InsertionListeners(), vehicleRoutingProblem); + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupShipment(shipment3)); + activities.add(new DeliverShipment(shipment3)); + when(activityFactory.createActivities(shipment3)).thenReturn(activities); + insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(2.0, iData.getInsertionCost(), 0.05); + assertEquals(0, iData.getPickupInsertionIndex()); + assertEquals(1, iData.getDeliveryInsertionIndex()); + } + + @Test + public void whenInstertingShipmentWithLoadConstraintWhereCapIsNotSufficient_capConstraintsAreFulfilled() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,0").build()).setDeliveryLocation(Location.newInstance("9,9")).build(); + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + VehicleRoutingProblem vrp = vrpBuilder.addJob(shipment).addJob(shipment2).addJob(shipment3).build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + route.setVehicleAndDepartureTime(vehicle, 0.0); + + Inserter inserter = new Inserter(new InsertionListeners(), vrp); + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + StateManager stateManager = new StateManager(vrp); + stateManager.updateLoadStates(); + stateManager.informInsertionStarts(Arrays.asList(route), null); + + ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); + constraintManager.addConstraint(new PickupAndDeliverShipmentLoadActivityLevelConstraint(stateManager), ConstraintManager.Priority.CRITICAL); + constraintManager.addConstraint(new ShipmentPickupsFirstConstraint(), ConstraintManager.Priority.CRITICAL); + + ShipmentInsertionCalculatorFlex insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, + activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory()); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, DriverImpl.noDriver(), Double.MAX_VALUE); + assertTrue(iData instanceof InsertionData.NoInsertionFound); + + } + + @Test + public void whenInsertingServiceWhileNoCapIsAvailable_itMustReturnNoInsertionData() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + VehicleRoutingProblem vrp = vrpBuilder.addJob(shipment).addJob(shipment2).build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + route.setVehicleAndDepartureTime(vehicle, 0.0); + + Inserter inserter = new Inserter(new InsertionListeners(), vrp); + + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + StateManager stateManager = new StateManager(vrp); + stateManager.updateLoadStates(); + stateManager.informInsertionStarts(Arrays.asList(route), null); + + ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); + constraintManager.addLoadConstraint(); + + stateManager.informInsertionStarts(Arrays.asList(route), null); + + Pickup service = (Pickup) Pickup.Builder.newInstance("pick").addSizeDimension(0, 1).setLocation(Location.newInstance("5,5")).build(); + + JobActivityFactory activityFactory = mock(JobActivityFactory.class); + List activities = new ArrayList(); + activities.add(new PickupService(service)); + when(activityFactory.createActivities(service)).thenReturn(activities); + + JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); + ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager, activityFactory); + ShipmentInsertionCalculatorFlex insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setJobActivityFactory(activityFactory); + switcher.put(Pickup.class, serviceInsertionCalc); + switcher.put(Service.class, serviceInsertionCalc); + switcher.put(Shipment.class, insertionCalculator); + + + InsertionData iData = switcher.getInsertionData(route, service, vehicle, 0, DriverImpl.noDriver(), Double.MAX_VALUE); +// routeActVisitor.visit(route); + + assertEquals(3, iData.getDeliveryInsertionIndex()); + } + + +} From 48611e108564ed2edb3f6a21161d711f39801bd1 Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 17 Sep 2018 10:21:32 +0200 Subject: [PATCH 15/17] make no. unassigned jobs configurable - related to #431 --- .../module/RuinAndRecreateModule.java | 68 ++++++++++++--- .../module/RuinAndRecreateModuleTest.java | 85 +++++++++++++++++++ 2 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModuleTest.java diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModule.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModule.java index 9c6e85cc..71facbc2 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModule.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModule.java @@ -26,9 +26,7 @@ import com.graphhopper.jsprit.core.algorithm.ruin.listener.RuinListener; import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.*; public class RuinAndRecreateModule implements SearchStrategyModule { @@ -39,6 +37,12 @@ public class RuinAndRecreateModule implements SearchStrategyModule { private String moduleName; + private Random random = new Random(4711); + + private int minUnassignedJobsToBeReinserted = Integer.MAX_VALUE; + + private double proportionOfUnassignedJobsToBeReinserted = 1d; + public RuinAndRecreateModule(String moduleName, InsertionStrategy insertion, RuinStrategy ruin) { super(); this.insertion = insertion; @@ -46,16 +50,58 @@ public class RuinAndRecreateModule implements SearchStrategyModule { this.moduleName = moduleName; } + /** + * To make overall results reproducible, make sure this class is provided with the "global" random number generator. + * + * @param random + */ + public void setRandom(Random random) { + this.random = random; + } + + /** + * Minimum number of unassigned jobs that is reinserted in each iteration. + * + * @param minUnassignedJobsToBeReinserted + */ + public void setMinUnassignedJobsToBeReinserted(int minUnassignedJobsToBeReinserted) { + this.minUnassignedJobsToBeReinserted = minUnassignedJobsToBeReinserted; + } + + /** + * Proportion of unassigned jobs that is reinserted in each iteration. + * + * @param proportionOfUnassignedJobsToBeReinserted + */ + public void setProportionOfUnassignedJobsToBeReinserted(double proportionOfUnassignedJobsToBeReinserted) { + this.proportionOfUnassignedJobsToBeReinserted = proportionOfUnassignedJobsToBeReinserted; + } + @Override - public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution vrpSolution) { - Collection ruinedJobs = ruin.ruin(vrpSolution.getRoutes()); - Set ruinedJobSet = new HashSet(); + public VehicleRoutingProblemSolution runAndGetSolution(VehicleRoutingProblemSolution previousVrpSolution) { + Collection ruinedJobs = ruin.ruin(previousVrpSolution.getRoutes()); + Set ruinedJobSet = new HashSet<>(); ruinedJobSet.addAll(ruinedJobs); - ruinedJobSet.addAll(vrpSolution.getUnassignedJobs()); - Collection unassignedJobs = insertion.insertJobs(vrpSolution.getRoutes(), ruinedJobSet); - vrpSolution.getUnassignedJobs().clear(); - vrpSolution.getUnassignedJobs().addAll(unassignedJobs); - return vrpSolution; + List stillUnassignedInThisIteration = new ArrayList<>(); + if (previousVrpSolution.getUnassignedJobs().size() < minUnassignedJobsToBeReinserted) { + ruinedJobSet.addAll(previousVrpSolution.getUnassignedJobs()); + } else { + int noUnassignedToBeInserted = Math.max(minUnassignedJobsToBeReinserted, (int) (previousVrpSolution.getUnassignedJobs().size() * proportionOfUnassignedJobsToBeReinserted)); + List jobList = new ArrayList<>(previousVrpSolution.getUnassignedJobs()); + Collections.shuffle(jobList, random); + for (int i = 0; i < noUnassignedToBeInserted; i++) { + ruinedJobSet.add(jobList.get(i)); + } + for (int i = noUnassignedToBeInserted; i < jobList.size(); i++) { + stillUnassignedInThisIteration.add(jobList.get(i)); + } + } + Collection unassignedJobs = insertion.insertJobs(previousVrpSolution.getRoutes(), ruinedJobSet); + previousVrpSolution.getUnassignedJobs().clear(); + previousVrpSolution.getUnassignedJobs().addAll(unassignedJobs); + previousVrpSolution.getUnassignedJobs().addAll(stillUnassignedInThisIteration); + VehicleRoutingProblemSolution newSolution = previousVrpSolution; + return newSolution; } diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModuleTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModuleTest.java new file mode 100644 index 00000000..ceb6d0cc --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/module/RuinAndRecreateModuleTest.java @@ -0,0 +1,85 @@ +/* + * 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.module; + +import com.graphhopper.jsprit.core.algorithm.recreate.InsertionStrategy; +import com.graphhopper.jsprit.core.algorithm.ruin.RuinStrategy; +import com.graphhopper.jsprit.core.problem.job.Job; +import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; +import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import static org.mockito.Mockito.mock; + +public class RuinAndRecreateModuleTest { + + @Test + public void initialNumOfUnassignedShouldWorkCorrectly() { + InsertionStrategy insertionStrategy = mock(InsertionStrategy.class); + RuinStrategy ruinStrategy = mock(RuinStrategy.class); + RuinAndRecreateModule module = new RuinAndRecreateModule("name", insertionStrategy, ruinStrategy); + Collection routes = new ArrayList<>(); + List unassigned = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + unassigned.add(mock(Job.class)); + } + VehicleRoutingProblemSolution previousSolution = new VehicleRoutingProblemSolution(routes, unassigned, 0); + VehicleRoutingProblemSolution newSolution = module.runAndGetSolution(previousSolution); + Assert.assertEquals(0, newSolution.getUnassignedJobs().size()); + } + + @Test + public void proportionOfUnassignedShouldWorkCorrectly() { + InsertionStrategy insertionStrategy = mock(InsertionStrategy.class); + RuinStrategy ruinStrategy = mock(RuinStrategy.class); + RuinAndRecreateModule module = new RuinAndRecreateModule("name", insertionStrategy, ruinStrategy); + module.setMinUnassignedJobsToBeReinserted(5); + module.setProportionOfUnassignedJobsToBeReinserted(0.01); + Collection routes = new ArrayList<>(); + List unassigned = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + unassigned.add(mock(Job.class)); + } + VehicleRoutingProblemSolution previousSolution = new VehicleRoutingProblemSolution(routes, unassigned, 0); + VehicleRoutingProblemSolution newSolution = module.runAndGetSolution(previousSolution); + Assert.assertEquals(15, newSolution.getUnassignedJobs().size()); + } + + @Test + public void proportionOfUnassignedShouldWorkCorrectly2() { + InsertionStrategy insertionStrategy = mock(InsertionStrategy.class); + RuinStrategy ruinStrategy = mock(RuinStrategy.class); + RuinAndRecreateModule module = new RuinAndRecreateModule("name", insertionStrategy, ruinStrategy); + module.setMinUnassignedJobsToBeReinserted(5); + module.setProportionOfUnassignedJobsToBeReinserted(0.5); + Collection routes = new ArrayList<>(); + List unassigned = new ArrayList<>(); + for (int i = 0; i < 20; i++) { + unassigned.add(mock(Job.class)); + } + VehicleRoutingProblemSolution previousSolution = new VehicleRoutingProblemSolution(routes, unassigned, 0); + VehicleRoutingProblemSolution newSolution = module.runAndGetSolution(previousSolution); + Assert.assertEquals(10, newSolution.getUnassignedJobs().size()); + } +} From e145151407494620f26e58afdc6878394f8b69ac Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 17 Sep 2018 10:40:18 +0200 Subject: [PATCH 16/17] make no. unassigned jobs configurable - related to #431 --- .../jsprit/core/algorithm/box/Jsprit.java | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 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 92386314..7225cf1f 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/box/Jsprit.java @@ -20,6 +20,7 @@ package com.graphhopper.jsprit.core.algorithm.box; import com.graphhopper.jsprit.core.algorithm.PrettyAlgorithmBuilder; import com.graphhopper.jsprit.core.algorithm.SearchStrategy; +import com.graphhopper.jsprit.core.algorithm.SearchStrategyModule; import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; import com.graphhopper.jsprit.core.algorithm.acceptor.SchrimpfAcceptance; import com.graphhopper.jsprit.core.algorithm.acceptor.SolutionAcceptor; @@ -124,7 +125,10 @@ public class Jsprit { STRING_K_MIN("string_kmin"), STRING_K_MAX("string_kmax"), STRING_L_MIN("string_lmin"), - STRING_L_MAX("string_lmax"); + STRING_L_MAX("string_lmax"), + MIN_UNASSIGNED("min_unassigned"), + PROPORTION_UNASSIGNED("proportion_unassigned"); + String paraName; @@ -233,6 +237,9 @@ public class Jsprit { defaults.put(Parameter.FAST_REGRET.toString(), String.valueOf(false)); defaults.put(Parameter.BREAK_SCHEDULING.toString(), String.valueOf(true)); defaults.put(Parameter.CONSTRUCTION.toString(), Construction.REGRET_INSERTION.toString()); + + defaults.put(Parameter.MIN_UNASSIGNED.toString(), String.valueOf(Integer.MAX_VALUE)); + defaults.put(Parameter.PROPORTION_UNASSIGNED.toString(), String.valueOf(1.0)); return defaults; } @@ -621,34 +628,34 @@ public class Jsprit { SolutionCostCalculator objectiveFunction = getObjectiveFunction(vrp, maxCosts); SearchStrategy radial_regret = new SearchStrategy(Strategy.RADIAL_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - radial_regret.addModule(new RuinAndRecreateModule(Strategy.RADIAL_REGRET.toString(), regret, radial)); + radial_regret.addModule(configureModule(new RuinAndRecreateModule(Strategy.RADIAL_REGRET.toString(), regret, radial))); SearchStrategy radial_best = new SearchStrategy(Strategy.RADIAL_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - radial_best.addModule(new RuinAndRecreateModule(Strategy.RADIAL_BEST.toString(), best, radial)); + radial_best.addModule(configureModule(new RuinAndRecreateModule(Strategy.RADIAL_BEST.toString(), best, radial))); SearchStrategy random_best = new SearchStrategy(Strategy.RANDOM_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - random_best.addModule(new RuinAndRecreateModule(Strategy.RANDOM_BEST.toString(), best, random_for_best)); + random_best.addModule(configureModule(new RuinAndRecreateModule(Strategy.RANDOM_BEST.toString(), best, random_for_best))); SearchStrategy random_regret = new SearchStrategy(Strategy.RANDOM_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - random_regret.addModule(new RuinAndRecreateModule(Strategy.RANDOM_REGRET.toString(), regret, random_for_regret)); + random_regret.addModule(configureModule(new RuinAndRecreateModule(Strategy.RANDOM_REGRET.toString(), regret, random_for_regret))); SearchStrategy worst_regret = new SearchStrategy(Strategy.WORST_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - worst_regret.addModule(new RuinAndRecreateModule(Strategy.WORST_REGRET.toString(), regret, worst)); + worst_regret.addModule(configureModule(new RuinAndRecreateModule(Strategy.WORST_REGRET.toString(), regret, worst))); SearchStrategy worst_best = new SearchStrategy(Strategy.WORST_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - worst_best.addModule(new RuinAndRecreateModule(Strategy.WORST_BEST.toString(), best, worst)); + worst_best.addModule(configureModule(new RuinAndRecreateModule(Strategy.WORST_BEST.toString(), best, worst))); final SearchStrategy clusters_regret = new SearchStrategy(Strategy.CLUSTER_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - clusters_regret.addModule(new RuinAndRecreateModule(Strategy.CLUSTER_REGRET.toString(), regret, clusters)); + clusters_regret.addModule(configureModule(new RuinAndRecreateModule(Strategy.CLUSTER_REGRET.toString(), regret, clusters))); final SearchStrategy clusters_best = new SearchStrategy(Strategy.CLUSTER_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - clusters_best.addModule(new RuinAndRecreateModule(Strategy.CLUSTER_BEST.toString(), best, clusters)); + clusters_best.addModule(configureModule(new RuinAndRecreateModule(Strategy.CLUSTER_BEST.toString(), best, clusters))); SearchStrategy stringRegret = new SearchStrategy(Strategy.STRING_REGRET.toString(), new SelectBest(), acceptor, objectiveFunction); - stringRegret.addModule(new RuinAndRecreateModule(Strategy.STRING_REGRET.toString(), regret, stringRuin)); + stringRegret.addModule(configureModule(new RuinAndRecreateModule(Strategy.STRING_REGRET.toString(), regret, stringRuin))); SearchStrategy stringBest = new SearchStrategy(Strategy.STRING_BEST.toString(), new SelectBest(), acceptor, objectiveFunction); - stringBest.addModule(new RuinAndRecreateModule(Strategy.STRING_BEST.toString(), best, stringRuin)); + stringBest.addModule(configureModule(new RuinAndRecreateModule(Strategy.STRING_BEST.toString(), best, stringRuin))); PrettyAlgorithmBuilder prettyBuilder = PrettyAlgorithmBuilder.newInstance(vrp, vehicleFleetManager, stateManager, constraintManager); prettyBuilder.setRandom(random); @@ -697,6 +704,13 @@ public class Jsprit { } + private SearchStrategyModule configureModule(RuinAndRecreateModule ruinAndRecreateModule) { + ruinAndRecreateModule.setRandom(random); + ruinAndRecreateModule.setMinUnassignedJobsToBeReinserted(Integer.valueOf(properties.getProperty(Parameter.MIN_UNASSIGNED.toString()))); + ruinAndRecreateModule.setProportionOfUnassignedJobsToBeReinserted(Double.valueOf(properties.getProperty(Parameter.PROPORTION_UNASSIGNED.toString()))); + return ruinAndRecreateModule; + } + private DefaultScorer getRegretScorer(VehicleRoutingProblem vrp) { DefaultScorer scorer = new DefaultScorer(vrp); scorer.setTimeWindowParam(Double.valueOf(properties.getProperty(Parameter.REGRET_TIME_WINDOW_SCORER.toString()))); From 5e3fd411227744c31831aa2b12bb6901c67c329a Mon Sep 17 00:00:00 2001 From: oblonski Date: Mon, 17 Sep 2018 10:43:30 +0200 Subject: [PATCH 17/17] ignore shipment flex test for now --- .../ShipmentInsertionCalculatorFlexTest.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java index 9afc5022..91657700 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorFlexTest.java @@ -47,6 +47,7 @@ import com.graphhopper.jsprit.core.problem.vehicle.VehicleType; import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; import com.graphhopper.jsprit.core.util.CostFactory; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; @@ -278,6 +279,43 @@ public class ShipmentInsertionCalculatorFlexTest { } + @Ignore + @Test + public void whenInsertingShipmentWithLoadConstraintWhereCapIsNotSufficient_capConstraintsAreFulfilledV2() { + Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("10,0")).build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("10,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,0").build()).setDeliveryLocation(Location.newInstance("9,9")).build(); + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + VehicleRoutingProblem vrp = vrpBuilder.addJob(shipment).addJob(shipment2).addJob(shipment3).build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + route.setVehicleAndDepartureTime(vehicle, 0.0); + + Inserter inserter = new Inserter(new InsertionListeners(), vrp); + inserter.insertJob(shipment, new InsertionData(0, 0, 0, vehicle, null), route); + inserter.insertJob(shipment2, new InsertionData(0, 1, 2, vehicle, null), route); + + StateManager stateManager = new StateManager(vrp); + stateManager.updateLoadStates(); + stateManager.informInsertionStarts(Arrays.asList(route), null); + + ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager); + constraintManager.addConstraint(new PickupAndDeliverShipmentLoadActivityLevelConstraint(stateManager), ConstraintManager.Priority.CRITICAL); +// constraintManager.addConstraint(new ShipmentPickupsFirstConstraint(), ConstraintManager.Priority.CRITICAL); + + ShipmentInsertionCalculatorFlex insertionCalculator = new ShipmentInsertionCalculatorFlex(routingCosts, activityCosts, + activityInsertionCostsCalculator, constraintManager); + insertionCalculator.setEvalIndexPickup(0); + insertionCalculator.setEvalIndexDelivery(3); + insertionCalculator.setJobActivityFactory(vrp.getJobActivityFactory()); + + InsertionData iData = insertionCalculator.getInsertionData(route, shipment3, vehicle, 0.0, DriverImpl.noDriver(), Double.MAX_VALUE); + assertTrue(iData instanceof InsertionData.NoInsertionFound); + + } + + @Test public void whenInsertingServiceWhileNoCapIsAvailable_itMustReturnNoInsertionData() { Shipment shipment = Shipment.Builder.newInstance("s").addSizeDimension(0, 1).setPickupLocation(Location.Builder.newInstance().setId("0,10").build()).setDeliveryLocation(Location.newInstance("0,0")).build();