diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertion.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertion.java index 00955c7e..3e6c8b57 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertion.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertion.java @@ -1,7 +1,6 @@ package com.graphhopper.jsprit.core.algorithm.recreate; import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; -import com.graphhopper.jsprit.core.problem.job.Break; import com.graphhopper.jsprit.core.problem.job.Job; import com.graphhopper.jsprit.core.problem.job.Service; import com.graphhopper.jsprit.core.problem.job.Shipment; @@ -17,7 +16,15 @@ public class RandomInsertion extends AbstractInsertionStrategy { private static Logger logger = LoggerFactory.getLogger(BestInsertion.class); private JobInsertionCostsCalculator bestInsertionCostCalculator; - final Map jobCanBeServedByDriversCount = new HashMap<>(); + final Map jobCanBeServedByDriversCount = new HashMap() { + @Override + public Integer get(Object key) { + if (!super.containsKey(key)) { + return 1; + } + return super.get(key); + } + }; public RandomInsertion(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem) { super(vehicleRoutingProblem); @@ -37,12 +44,6 @@ public class RandomInsertion extends AbstractInsertionStrategy { jobCanBeServedByDriversCount.put(job.getId(), count); } - - for (Vehicle vehicle : vrp.getVehicles()) { - final Break aBreak = vehicle.getBreak(); - if (aBreak != null) - jobCanBeServedByDriversCount.put(aBreak.getId(), 1); - } } @Override diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/StateManager.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/StateManager.java index 9d9fbb63..a8b1b1cb 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/StateManager.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/StateManager.java @@ -68,7 +68,7 @@ public class StateManager implements RouteAndActivityStateGetter, IterationStart private Map createdStateIds = new HashMap(); - private int nuActivities; + protected int nuActivities; private int nuVehicleTypeKeys; @@ -130,26 +130,31 @@ public class StateManager implements RouteAndActivityStateGetter, IterationStart * @param vehicleRoutingProblem the corresponding VehicleRoutingProblem */ public StateManager(VehicleRoutingProblem vehicleRoutingProblem) { + this(vehicleRoutingProblem, Math.max(10, vehicleRoutingProblem.getNuActivities() + 1)); + } + + public StateManager(VehicleRoutingProblem vehicleRoutingProblem, int nuActivities) { stateIndexCounter = initialNoStates; int initialStateArrayLength = 30; this.vrp = vehicleRoutingProblem; - nuActivities = Math.max(10, vrp.getNuActivities() + 1); + this.nuActivities = Math.max(10, nuActivities); nuVehicleTypeKeys = Math.max(3, getNuVehicleTypes(vrp) + 2); - activityStates = new Object[nuActivities][initialStateArrayLength]; - vehicleDependentActivityStates = new Object[nuActivities][nuVehicleTypeKeys][initialStateArrayLength]; + activityStates = new Object[this.nuActivities][initialStateArrayLength]; + vehicleDependentActivityStates = new Object[this.nuActivities][nuVehicleTypeKeys][initialStateArrayLength]; // if(vehicleRoutingProblem.getFleetSize().equals(VehicleRoutingProblem.FleetSize.FINITE)){ // isIndexedBased = true; // routeStatesArr = new Object[vrp.getVehicles().size() + 2][initialStateArrayLength]; // vehicleDependentRouteStatesArr = new Object[vrp.getVehicles().size() + 2][nuVehicleTypeKeys][initialStateArrayLength]; // } // else { - isIndexedBased = false; - routeStateMap = new HashMap(); - vehicleDependentRouteStateMap = new HashMap(); + isIndexedBased = false; + routeStateMap = new HashMap(); + vehicleDependentRouteStateMap = new HashMap(); // } problemStates = new Object[initialStateArrayLength]; } + private int getNuVehicleTypes(VehicleRoutingProblem vrp) { int maxIndex = 0; for (Vehicle v : vrp.getVehicles()) { 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 b345a05f..ab23ea7e 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 @@ -534,7 +534,7 @@ public class VehicleRoutingProblem { * @author sschroeder */ public static enum FleetSize { - FINITE, INFINITE + FINITE, INFINITE, INFINITE_WITH_BREAKS } /** @@ -582,6 +582,10 @@ public class VehicleRoutingProblem { private int nuActivities; + private int vehicleIndexCounter; + + private final JobActivityFactory originalJobActivityFactory; + private final JobActivityFactory jobActivityFactory = new JobActivityFactory() { @Override @@ -603,6 +607,8 @@ public class VehicleRoutingProblem { this.nuActivities = builder.activityIndexCounter; this.allLocations = builder.allLocations; this.allJobs = builder.tentativeJobs; + this.originalJobActivityFactory = builder.jobActivityFactory; + this.vehicleIndexCounter = builder.vehicleIndexCounter; logger.info("setup problem: {}", this); } @@ -726,4 +732,25 @@ public class VehicleRoutingProblem { return acts; } + private void addBreak(Break aBreak) { + if (activityMap.containsKey(aBreak)) + return; + + List breakActivities = originalJobActivityFactory.createActivities(aBreak); + if (breakActivities.isEmpty()) + throw new IllegalArgumentException("At least one activity for break needs to be created by activityFactory!"); + for(AbstractActivity act : breakActivities) { + act.setIndex(nuActivities); + ++nuActivities; + } + activityMap.put(aBreak, breakActivities); + } + + public void addVehicle(AbstractVehicle vehicle) { + vehicle.setIndex(vehicleIndexCounter++); + if (vehicle.getBreak() != null) + addBreak(vehicle.getBreak()); + vehicles.add(vehicle); + } + } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java index df2fdf23..9fd0df54 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java @@ -23,6 +23,8 @@ import com.graphhopper.jsprit.core.problem.Location; import com.graphhopper.jsprit.core.problem.Skills; import com.graphhopper.jsprit.core.problem.job.Break; +import java.util.Collection; + /** * Basic interface for vehicle-data. * @@ -83,7 +85,9 @@ public interface Vehicle extends HasId, HasIndex { // default Object getUserData() { // return null; // }; - public abstract boolean isTaskPermited(String taskId); + boolean isTaskPermited(String taskId); - public abstract void addProhibitedTask(String taskId); + void addProhibitedTask(String taskId); + + Collection getProhibitedTasks(); } diff --git a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java index e113867b..f473ab40 100644 --- a/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImpl.java @@ -107,6 +107,10 @@ public class VehicleImpl extends AbstractVehicle { throw new IllegalArgumentException("NoVehicle should not have prohibited tasks"); } + @Override + public Collection getProhibitedTasks() { + return Collections.EMPTY_LIST; + } } /** @@ -443,6 +447,11 @@ public class VehicleImpl extends AbstractVehicle { prohibitedTasks.add(taskId); } + @Override + public Collection getProhibitedTasks() { + return Collections.unmodifiableSet(prohibitedTasks); + } + /* (non-Javadoc) * @see java.lang.Object#hashCode() */ diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertionTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertionTest.java index 21a88755..7db89c62 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertionTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertionTest.java @@ -44,16 +44,8 @@ public class RandomInsertionTest { final RandomInsertion randomInsertion = new RandomInsertion(null, builder.build()); final Map jobCanBeServedByDriversCount = randomInsertion.jobCanBeServedByDriversCount; - - int numBreaks = 0; - for (Map.Entry entry : jobCanBeServedByDriversCount.entrySet()) { - if (entry.getKey().contains("break_")) { - ++numBreaks; - assertEquals((int) entry.getValue(), 1); - } - } - - assertEquals(2, numBreaks); + assertEquals(1, jobCanBeServedByDriversCount.get("break_v1"), .001); + assertEquals(1, jobCanBeServedByDriversCount.get("break_v2"), .001); } @Test @@ -176,7 +168,9 @@ public class RandomInsertionTest { }; final List unassigned = new ArrayList<>(); - unassigned.add(service1); unassigned.add(service2); + unassigned.add(service1); + unassigned.add(service2); + Collections.shuffle(unassigned); randomInsertion.sortJobs(unassigned); assertEquals(1, (int) randomInsertion.jobCanBeServedByDriversCount.get(service2.getId())); @@ -185,6 +179,50 @@ public class RandomInsertionTest { assertEquals(service1, unassigned.get(1)); } + + @Test + public void sortTestWithTaskThatNotExist() { + final HashSet skillsService1 = new HashSet<>(); + skillsService1.add("a"); skillsService1.add("b"); + final HashSet skillsDriver1 = new HashSet<>(); + skillsDriver1.add("a"); skillsDriver1.add("b"); + final HashSet skillsDriver2 = new HashSet<>(); + skillsDriver2.add("c"); skillsDriver2.add("b"); + final HashSet skillsDriver3 = new HashSet<>(); + skillsDriver3.add("c"); skillsDriver3.add("b"); skillsDriver3.add("a"); + final Service service1 = getService(Location.newInstance(0, 5), 0, 20, skillsService1, 1); + final Vehicle v1 = getVehicle("v1", Location.newInstance(0, 0), 0, 100, 20, skillsDriver1, false, 1, 1, false, null); + final Vehicle v2 = getVehicle("v2", Location.newInstance(0, 14), 0, 100, 20, skillsDriver2, false, 1, 1, false, service1.getId()); + final Vehicle v3 = getVehicle("v3", Location.newInstance(0, 14), 0, 100, 20, skillsDriver3, false, 1, 1, false, null); + + + final VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE) + .addVehicle(v1) + .addVehicle(v2) + .addVehicle(v3) + .addJob(service1); + final RandomInsertion randomInsertion = new RandomInsertion(null, builder.build()); + + randomInsertion.random = new Random() { + @Override + public double nextDouble() { + return 0.5; + } + }; + + final Break aBreak = Break.Builder.newInstance(UUID.randomUUID().toString()).setServiceTime(60).addTimeWindow(0, 160).build(); + final List unassigned = new ArrayList<>(); + unassigned.add(service1); + unassigned.add(aBreak); + + Collections.shuffle(unassigned); + randomInsertion.sortJobs(unassigned); + assertEquals(2, (int) randomInsertion.jobCanBeServedByDriversCount.get(service1.getId())); + assertEquals(1, (int) randomInsertion.jobCanBeServedByDriversCount.get(aBreak.getId())); + assertEquals(aBreak, unassigned.get(0)); + assertEquals(service1, unassigned.get(1)); + } + private Shipment getShipment(int pStart, int pEnd, int dStart, int dEnd) { return Shipment.Builder.newInstance(UUID.randomUUID().toString()) .setPickupLocation(Location.newInstance(UUID.randomUUID().toString())) diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/state/StateManagerTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/state/StateManagerTest.java index 40c89edc..b4428ba8 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/state/StateManagerTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/state/StateManagerTest.java @@ -33,6 +33,7 @@ import org.junit.Test; import java.util.ArrayList; import java.util.List; +import java.util.Random; import static org.junit.Assert.*; import static org.mockito.Mockito.mock; @@ -356,4 +357,12 @@ public class StateManagerTest { } stateManager.putTypedInternalRouteState(route,myState,1.); } + + + @Test + public void shouldSetPassedNuActivities() { + final int nuActivities = 10 + new Random().nextInt(1_000); + final StateManager stateManager = new StateManager(vrpMock, nuActivities); + assertEquals(nuActivities, stateManager.nuActivities); + } } 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 ac7e4c97..50fdccc4 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 @@ -36,6 +36,7 @@ import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.UUID; import static org.junit.Assert.*; import static org.junit.matchers.JUnitMatchers.hasItem; @@ -559,4 +560,30 @@ public class VehicleRoutingProblemTest { assertEquals(2, veh2.getVehicleTypeIdentifier().getIndex()); } + + @Test + public void shouldAddBreakIfNotExist() { + VehicleImpl veh1 = VehicleImpl.Builder.newInstance("v1") + .setStartLocation(TestUtils.loc("start", Coordinate.newInstance(0, 1))) + .setBreak(Break.Builder.newInstance(UUID.randomUUID().toString()).setServiceTime(60).build()) + .setEndLocation(Location.newInstance("end")).build(); + final Service service = Service.Builder.newInstance("myService").setLocation(Location.newInstance("loc")).build(); + + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance().setFleetSize(FleetSize.INFINITE_WITH_BREAKS); + vrpBuilder.addVehicle(veh1); + vrpBuilder.addJob(service); + final VehicleRoutingProblem vrp = vrpBuilder.build(); + + VehicleImpl veh2 = VehicleImpl.Builder.newInstance("v2") + .setStartLocation(TestUtils.loc("start", Coordinate.newInstance(0, 1))) + .setBreak(Break.Builder.newInstance(UUID.randomUUID().toString()).setServiceTime(20).build()) + .setEndLocation(Location.newInstance("end")).build(); + + assertEquals(0, veh2.getIndex()); + vrp.addVehicle(veh2); + assertEquals(1, veh1.getIndex()); + assertEquals(2, veh2.getIndex()); + assertEquals(2, vrp.getActivities(veh1.getBreak()).get(0).getIndex()); + assertEquals(3, vrp.getActivities(veh2.getBreak()).get(0).getIndex()); + } } diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImplTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImplTest.java index f85a029d..9109c2da 100644 --- a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImplTest.java +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/vehicle/VehicleImplTest.java @@ -23,9 +23,7 @@ import com.graphhopper.jsprit.core.problem.job.Break; import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; import org.junit.Test; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; import static org.junit.Assert.*; @@ -256,6 +254,24 @@ public class VehicleImplTest { assertNull(three.getUserData()); } + @Test + public void getProhibitedTasks() { + Set prohibitedTasks = new HashSet<>(); + for (int i = 0; i < 10; ++i) { + prohibitedTasks.add(UUID.randomUUID().toString()); + } + VehicleTypeImpl type1 = VehicleTypeImpl.Builder.newInstance("type").build(); + VehicleImpl.Builder vehicleBuilder = VehicleImpl.Builder.newInstance("v").setType(type1) + .setStartLocation(Location.newInstance("start")); + + for (String task : prohibitedTasks) { + vehicleBuilder.addExcludedTask(task); + } + + final Collection prohibitedTasksReturned = vehicleBuilder.build().getProhibitedTasks(); + assertEquals(prohibitedTasks, prohibitedTasksReturned); + } + @Test public void testBreakTimesIncludedInKey() { VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type").build();