From 8fa3c81983eb90f6b7310d7796da0182aa1747f5 Mon Sep 17 00:00:00 2001 From: kandelirina Date: Tue, 1 May 2018 13:34:59 +0300 Subject: [PATCH] RandomInsertionAdded (#46) * RandomInsertionAdded * fix * oops * wrong algorithm name * add breaks too * test added * containAlll * if --- .../algorithm/recreate/InsertionBuilder.java | 4 +- .../algorithm/recreate/RandomInsertion.java | 102 +++++++++++++++ .../recreate/RandomInsertionTest.java | 106 +++++++++++++++ .../jsprit/examples/SkillsWithPriorities.java | 118 +++++++++++++++++ .../jsprit/io/algorithm/InsertionFactory.java | 4 +- .../algorithm/VehicleRoutingAlgorithms.java | 2 +- .../src/main/resources/algorithm_schema.xsd | 1 + .../jsprit/io/algorithm/SkillsTest.java | 123 ++++++++++++++++++ .../io/algorithm/configWithRandomInsert.xml | 87 +++++++++++++ 9 files changed, 544 insertions(+), 3 deletions(-) create mode 100644 jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertion.java create mode 100644 jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertionTest.java create mode 100644 jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/SkillsWithPriorities.java create mode 100644 jsprit-io/src/test/java/com/graphhopper/jsprit/io/algorithm/SkillsTest.java create mode 100644 jsprit-io/src/test/resources/com/graphhopper/jsprit/io/algorithm/configWithRandomInsert.xml 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..9dd6e2cd 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 @@ -35,7 +35,7 @@ public class InsertionBuilder { public enum Strategy { - REGRET, BEST + REGRET, BEST, RANDOM } private VehicleRoutingProblem vrp; @@ -200,6 +200,8 @@ public class InsertionBuilder { } } + } else if (strategy.equals(Strategy.RANDOM)) { + insertion = new RandomInsertion(costCalculator, vrp); } else throw new IllegalStateException("you should never get here"); for (InsertionListener l : iListeners) insertion.addListener(l); return insertion; 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 new file mode 100644 index 00000000..c002edef --- /dev/null +++ b/jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertion.java @@ -0,0 +1,102 @@ +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.solution.route.VehicleRoute; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class RandomInsertion extends AbstractInsertionStrategy { + private static Logger logger = LoggerFactory.getLogger(BestInsertion.class); + + private JobInsertionCostsCalculator bestInsertionCostCalculator; + final Map jobCanBeServedByDriversCount = new HashMap<>(); + + public RandomInsertion(JobInsertionCostsCalculator jobInsertionCalculator, VehicleRoutingProblem vehicleRoutingProblem) { + super(vehicleRoutingProblem); + bestInsertionCostCalculator = jobInsertionCalculator; + + initJobsCanBeServedByNumDrivers(); + logger.debug("initialise {}", this); + } + + void initJobsCanBeServedByNumDrivers() { + for (Job job : vrp.getJobs().values()) { + int count = 0; + for (Vehicle vehicle : vrp.getVehicles()) + if (vehicle.getSkills().values().containsAll(job.getRequiredSkills().values())) + count++; + + jobCanBeServedByDriversCount.put(job.getId(), count); + } + + for (Vehicle vehicle : vrp.getVehicles()) { + final Break aBreak = vehicle.getBreak(); + if (aBreak != null) + jobCanBeServedByDriversCount.put(aBreak.getId(), 1); + } + } + + @Override + public String toString() { + return "[name=randomInsertion]"; + } + + @Override + public Collection insertUnassignedJobs(Collection vehicleRoutes, Collection unassignedJobs) { + List badJobs = new ArrayList<>(unassignedJobs.size()); + List unassignedJobList = new ArrayList<>(unassignedJobs); + Collections.shuffle(unassignedJobList, random); + + final double p = random.nextDouble(); + if (p < .25) + Collections.sort(unassignedJobList, new AccordingToPriorities()); + else if (p < .5) + Collections.sort(unassignedJobList, new Comparator() { + @Override + public int compare(Job o1, Job o2) {return jobCanBeServedByDriversCount.get(o1.getId()) - jobCanBeServedByDriversCount.get(o2.getId());} + }); + + for (Job unassignedJob : unassignedJobList) { + List routes = new ArrayList<>(vehicleRoutes); + final VehicleRoute newRoute = VehicleRoute.emptyRoute(); + routes.add(newRoute); + Collections.shuffle(routes, random); + + InsertionData empty = new InsertionData.NoInsertionFound(); + double bestInsertionCost = Double.MAX_VALUE; + boolean inserted = false; + for (VehicleRoute vehicleRoute : routes) { + InsertionData iData = bestInsertionCostCalculator.getInsertionData(vehicleRoute, unassignedJob, NO_NEW_VEHICLE_YET, NO_NEW_DEPARTURE_TIME_YET, NO_NEW_DRIVER_YET, bestInsertionCost); + if (iData instanceof InsertionData.NoInsertionFound) { + empty.getFailedConstraintNames().addAll(iData.getFailedConstraintNames()); + continue; + } + + inserted = true; + final boolean isNewRoute = vehicleRoute.getActivities().size() == 0; + if (isNewRoute) { + updateNewRouteInsertionData(iData); + vehicleRoutes.add(vehicleRoute); + } + + final boolean vehicleSwitched = !vehicleRoute.getVehicle().getId().equals(iData.getSelectedVehicle().getId()); + insertJob(unassignedJob, iData, vehicleRoute); + if (vehicleSwitched) + insertBreak(bestInsertionCostCalculator, badJobs, vehicleRoute, iData); + break; + } + + if (!inserted) { + markUnassigned(unassignedJob, empty.getFailedConstraintNames()); + badJobs.add(unassignedJob); + } + } + return badJobs; + } + +} 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 new file mode 100644 index 00000000..8afe4e9e --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/algorithm/recreate/RandomInsertionTest.java @@ -0,0 +1,106 @@ +package com.graphhopper.jsprit.core.algorithm.recreate; + +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.job.Break; +import com.graphhopper.jsprit.core.problem.job.Delivery; +import com.graphhopper.jsprit.core.problem.job.Service; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; + +public class RandomInsertionTest { + + @Test + public void initJobsCanBeServedByNumDrivers1() { + final HashSet first = new HashSet<>(); first.add("C"); + final HashSet second = new HashSet<>(); second.add("A");second.add("B"); + final VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE) + .addVehicle(getVehicle("v1", Location.newInstance(0, 0), 0, 100, 20, first, false, 1, 1, false)) + .addVehicle(getVehicle("v2", Location.newInstance(0, 14), 0, 100, 20, second, false, 1, 1, false)) + .addJob(getService(Location.newInstance(0, 5), 0, 20, new HashSet(), 1)) + .addJob(getService(Location.newInstance(0, 6), 0, 20, new HashSet(), 1)); + + final RandomInsertion randomInsertion = new RandomInsertion(null, builder.build()); + final Map jobCanBeServedByDriversCount = randomInsertion.jobCanBeServedByDriversCount; + + for (int numCanServe : jobCanBeServedByDriversCount.values()) + assertEquals(numCanServe, 2); + } + + @Test + public void initBreaksCanBeServedByOneDriver() { + final HashSet first = new HashSet<>(); first.add("C"); + final HashSet second = new HashSet<>(); second.add("A");second.add("B"); + final VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE) + .addVehicle(getVehicle("v1", Location.newInstance(0, 0), 0, 100, 20, first, false, 1, 1, true)) + .addVehicle(getVehicle("v2", Location.newInstance(0, 14), 0, 100, 20, second, false, 1, 1, true)) + .addJob(getService(Location.newInstance(0, 5), 0, 20, new HashSet(), 1)) + .addJob(getService(Location.newInstance(0, 6), 0, 20, new HashSet(), 1)); + + 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(numBreaks, 2); + } + + @Test + public void initJobsCanBeServedByNumDrivers2() { + final HashSet first = new HashSet<>(); first.add("C"); + final HashSet second = new HashSet<>(); second.add("A");second.add("B"); + final VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE) + .addVehicle(getVehicle("v1", Location.newInstance(0, 0), 0, 100, 20, first, false, 1, 1, false)) + .addVehicle(getVehicle("v2", Location.newInstance(0, 14), 0, 100, 20, second, false, 1, 1, false)) + .addJob(getService(Location.newInstance(0, 5), 0, 20, first, 1)) + .addJob(getService(Location.newInstance(0, 6), 0, 20, second, 1)); + + final RandomInsertion randomInsertion = new RandomInsertion(null, builder.build()); + final Map jobCanBeServedByDriversCount = randomInsertion.jobCanBeServedByDriversCount; + + for (int numCanServe : jobCanBeServedByDriversCount.values()) + assertEquals(numCanServe, 1); + } + + private static Service getService(Location location, int start, int end, Set requiredSkills, int priority) { + return Delivery.Builder.newInstance("service_" + UUID.randomUUID().toString().substring(0,5)) + .setLocation(location) + .setServiceTime(1) + .addTimeWindow(new TimeWindow(start, end)) + .addSizeDimension(0, 1) + .addAllRequiredSkills(requiredSkills) + .setPriority(priority) + .setName(UUID.randomUUID().toString()).build(); + + } + + private static Vehicle getVehicle(String id, Location location, int start, int end, int capacity, Set skills, boolean returnToDepot, int fixedCost, int costPerDistance, boolean aBreak) { + final VehicleImpl.Builder builder = VehicleImpl.Builder.newInstance(id) + .setStartLocation(location).setLatestArrival(end).setEarliestStart(start).setType( + VehicleTypeImpl.Builder.newInstance(UUID.randomUUID().toString()).setFixedCost(fixedCost).setCostPerDistance(costPerDistance).addCapacityDimension(0, capacity).build() + ) + .addAllSkills(skills).setReturnToDepot(returnToDepot); + + if (aBreak) + builder.setBreak(Break.Builder.newInstance("break_" + id).build()); + return builder.build(); + + } + +} diff --git a/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/SkillsWithPriorities.java b/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/SkillsWithPriorities.java new file mode 100644 index 00000000..1f16a2be --- /dev/null +++ b/jsprit-examples/src/main/java/com/graphhopper/jsprit/examples/SkillsWithPriorities.java @@ -0,0 +1,118 @@ +package com.graphhopper.jsprit.examples; + +import com.graphhopper.jsprit.analysis.toolbox.GraphStreamViewer; +import com.graphhopper.jsprit.core.algorithm.VariablePlusFixedSolutionCostCalculatorFactory; +import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; +import com.graphhopper.jsprit.core.algorithm.state.StateManager; +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts; +import com.graphhopper.jsprit.core.problem.driver.Driver; +import com.graphhopper.jsprit.core.problem.job.Delivery; +import com.graphhopper.jsprit.core.problem.job.Service; +import com.graphhopper.jsprit.core.problem.job.Shipment; +import com.graphhopper.jsprit.core.problem.solution.SolutionCostCalculator; +import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; +import com.graphhopper.jsprit.core.reporting.SolutionPrinter; +import com.graphhopper.jsprit.core.util.Solutions; +import com.graphhopper.jsprit.io.algorithm.VehicleRoutingAlgorithms; +import com.graphhopper.jsprit.io.problem.VrpXMLWriter; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +public class SkillsWithPriorities { + public static void main(String[] args) { + final HashSet first = new HashSet<>(); first.add("C"); + final HashSet second = new HashSet<>(); second.add("A");second.add("B"); + final VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE) + .addVehicle(getVehicle("v1", Location.newInstance(0, 0), 0, 100, 20, first, false, 1, 1)) + .addVehicle(getVehicle("v2", Location.newInstance(0, 14), 0, 100, 20, second, false, 1, 1)) + .addJob(getService(Location.newInstance(0, 5), 0, 20, new HashSet(), 1)) + .addJob(getService(Location.newInstance(0, 6), 0, 20, new HashSet(), 1)) + .addJob(getShipment(Location.newInstance(10, 10), Location.newInstance(10, 12), 10, 20, 10, 50, first, 10)); + + builder.setRoutingCost(getTransportCosts()); + VehicleRoutingProblem vrp = builder.build(); + StateManager stateManager = new StateManager(vrp); + final SolutionCostCalculator calculator = new VariablePlusFixedSolutionCostCalculatorFactory(stateManager).createCalculator(); + SolutionCostCalculator solutionCostCalculator = new SolutionCostCalculator() { + @Override + public double getCosts(VehicleRoutingProblemSolution solution) { + return calculator.getCosts(solution) + solution.getUnassignedJobs().size() * 10; + } + }; + final VehicleRoutingAlgorithm algorithm = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, 1,"jsprit-examples/input/algorithmConfig.xml", stateManager, new ConstraintManager(vrp, stateManager), solutionCostCalculator); + + Collection solutions = algorithm.searchSolutions(); + + VehicleRoutingProblemSolution bestSolution = Solutions.bestOf(solutions); + + new VrpXMLWriter(vrp, solutions).write("output/problem-with-solution.xml"); + + SolutionPrinter.print(vrp, bestSolution, SolutionPrinter.Print.VERBOSE); + new GraphStreamViewer(vrp, bestSolution).labelWith(GraphStreamViewer.Label.ID).setRenderDelay(200).display(); + } + + private static VehicleRoutingTransportCosts getTransportCosts() { + return new VehicleRoutingTransportCosts() { + @Override + public double getBackwardTransportCost(Location from, Location to, double arrivalTime, Driver driver, Vehicle vehicle) { return getDistance(from, to, arrivalTime, vehicle); } + + @Override + public double getBackwardTransportTime(Location from, Location to, double arrivalTime, Driver driver, Vehicle vehicle) { return getDistance(from, to, arrivalTime, vehicle); } + + @Override + public double getTransportCost(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) { return getDistance(from, to, departureTime, vehicle); } + + @Override + public double getTransportTime(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) { return getDistance(from, to, departureTime, vehicle); } + + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return Math.abs(from.getCoordinate().getX() - to.getCoordinate().getX()) + Math.abs(from.getCoordinate().getY() - to.getCoordinate().getY()); + } + }; + } + + private static Vehicle getVehicle(String id, Location location, int start, int end, int capacity, Set skills, boolean returnToDepot, int fixedCost, int costPerDistance) { + return VehicleImpl.Builder.newInstance(id) + .setStartLocation(location).setLatestArrival(end).setEarliestStart(start).setType( + VehicleTypeImpl.Builder.newInstance(UUID.randomUUID().toString()).setFixedCost(fixedCost).setCostPerDistance(costPerDistance).addCapacityDimension(0, capacity).build() + ) + .addAllSkills(skills).setReturnToDepot(returnToDepot).build(); + + } + + private static Service getService(Location location, int start, int end, Set requiredSkills, int priority) { + return Delivery.Builder.newInstance("service_" + UUID.randomUUID().toString().substring(0,5)) + .setLocation(location) + .setServiceTime(1) + .addTimeWindow(new TimeWindow(start, end)) + .addSizeDimension(0, 1) + .addAllRequiredSkills(requiredSkills) + .setPriority(priority) + .setName(UUID.randomUUID().toString()).build(); + + } + + public static Shipment getShipment(Location pickupLocation, Location dropoffLocation, int pickupStart, int pickupEnd, int dropoffStart, int dropoffEnd, Set skills, int priority) { + return Shipment.Builder.newInstance("shipment_" + UUID.randomUUID().toString().substring(0,5)) + .setPickupLocation(pickupLocation).setDeliveryLocation(dropoffLocation) + .setDeliveryServiceTime(1).setPickupServiceTime(1) + .addSizeDimension(0, 1) + .setPickupTimeWindow(new TimeWindow(pickupStart, pickupEnd)) + .setDeliveryTimeWindow(new TimeWindow(dropoffStart, dropoffEnd)) + .addAllRequiredSkills(skills) + .setPriority(priority) + .build(); + } + +} diff --git a/jsprit-io/src/main/java/com/graphhopper/jsprit/io/algorithm/InsertionFactory.java b/jsprit-io/src/main/java/com/graphhopper/jsprit/io/algorithm/InsertionFactory.java index 74f81146..80943c05 100644 --- a/jsprit-io/src/main/java/com/graphhopper/jsprit/io/algorithm/InsertionFactory.java +++ b/jsprit-io/src/main/java/com/graphhopper/jsprit/io/algorithm/InsertionFactory.java @@ -42,7 +42,7 @@ class InsertionFactory { if (config.containsKey("[@name]")) { String insertionName = config.getString("[@name]"); - if (!insertionName.equals("bestInsertion") && !insertionName.equals("regretInsertion")) { + if (!insertionName.equals("bestInsertion") && !insertionName.equals("regretInsertion") && !insertionName.equals("randomInsertion")) { throw new IllegalStateException(insertionName + " is not supported. use either \"bestInsertion\" or \"regretInsertion\""); } @@ -104,6 +104,8 @@ class InsertionFactory { if (fastRegret != null) { iBuilder.setFastRegret(Boolean.parseBoolean(fastRegret)); } + } else if (insertionName.equals("randomInsertion")) { + iBuilder.setInsertionStrategy(InsertionBuilder.Strategy.RANDOM); } return iBuilder.build(); } else throw new IllegalStateException("cannot create insertionStrategy, since it has no name."); diff --git a/jsprit-io/src/main/java/com/graphhopper/jsprit/io/algorithm/VehicleRoutingAlgorithms.java b/jsprit-io/src/main/java/com/graphhopper/jsprit/io/algorithm/VehicleRoutingAlgorithms.java index 02f03ff0..dd59548a 100644 --- a/jsprit-io/src/main/java/com/graphhopper/jsprit/io/algorithm/VehicleRoutingAlgorithms.java +++ b/jsprit-io/src/main/java/com/graphhopper/jsprit/io/algorithm/VehicleRoutingAlgorithms.java @@ -886,7 +886,7 @@ public class VehicleRoutingAlgorithms { String insertionName = moduleConfig.getString("insertion[@name]"); if (insertionName == null) - throw new IllegalStateException("module.insertion[@name] is missing. set it to \"regretInsertion\" or \"bestInsertion\""); + throw new IllegalStateException("module.insertion[@name] is missing. set it to \"regretInsertion\" or \"bestInsertion\" or \"randomInsertion\""); String insertionId = moduleConfig.getString("insertion[@id]"); if (insertionId == null) insertionId = "noId"; ModKey insertionKey = makeKey(insertionName, insertionId); diff --git a/jsprit-io/src/main/resources/algorithm_schema.xsd b/jsprit-io/src/main/resources/algorithm_schema.xsd index d8bd7ec2..9e6beb89 100644 --- a/jsprit-io/src/main/resources/algorithm_schema.xsd +++ b/jsprit-io/src/main/resources/algorithm_schema.xsd @@ -275,6 +275,7 @@ + diff --git a/jsprit-io/src/test/java/com/graphhopper/jsprit/io/algorithm/SkillsTest.java b/jsprit-io/src/test/java/com/graphhopper/jsprit/io/algorithm/SkillsTest.java new file mode 100644 index 00000000..cc40d088 --- /dev/null +++ b/jsprit-io/src/test/java/com/graphhopper/jsprit/io/algorithm/SkillsTest.java @@ -0,0 +1,123 @@ +package com.graphhopper.jsprit.io.algorithm; + +import com.graphhopper.jsprit.core.algorithm.VariablePlusFixedSolutionCostCalculatorFactory; +import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; +import com.graphhopper.jsprit.core.algorithm.state.StateManager; +import com.graphhopper.jsprit.core.problem.Location; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; +import com.graphhopper.jsprit.core.problem.constraint.ConstraintManager; +import com.graphhopper.jsprit.core.problem.cost.VehicleRoutingTransportCosts; +import com.graphhopper.jsprit.core.problem.driver.Driver; +import com.graphhopper.jsprit.core.problem.job.Delivery; +import com.graphhopper.jsprit.core.problem.job.Service; +import com.graphhopper.jsprit.core.problem.job.Shipment; +import com.graphhopper.jsprit.core.problem.solution.SolutionCostCalculator; +import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; +import com.graphhopper.jsprit.core.problem.solution.route.activity.TimeWindow; +import com.graphhopper.jsprit.core.problem.vehicle.Vehicle; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; +import com.graphhopper.jsprit.core.util.Solutions; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class SkillsTest { + @Test + public void skillsAdded() { + final HashSet first = new HashSet<>(); first.add("C"); + final HashSet second = new HashSet<>(); second.add("A");second.add("B"); + final VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance().setFleetSize(VehicleRoutingProblem.FleetSize.FINITE) + .addVehicle(getVehicle("v1", Location.newInstance(0, 0), 0, 100, 20, first, false, 1, 1)) + .addVehicle(getVehicle("v2", Location.newInstance(0, 14), 0, 100, 20, second, false, 1, 1)) + .addJob(getService(Location.newInstance(0, 5), 0, 20, new HashSet(), 1)) + .addJob(getService(Location.newInstance(0, 6), 0, 20, new HashSet(), 1)) + .addJob(getShipment(Location.newInstance(10, 10), Location.newInstance(10, 12), 10, 20, 10, 50, first, 10)); + + builder.setRoutingCost(getTransportCosts()); + VehicleRoutingProblem vrp = builder.build(); + + VehicleRoutingProblemSolution bestSolution = getVehicleRoutingProblemSolution(vrp, "com/graphhopper/jsprit/io/algorithm/algorithmConfig.xml"); + assertEquals(bestSolution.getRoutes().size(), 1); + assertFalse(bestSolution.getUnassignedJobs().isEmpty()); + + bestSolution = getVehicleRoutingProblemSolution(vrp, "com/graphhopper/jsprit/io/algorithm/configWithRandomInsert.xml"); + assertEquals(bestSolution.getRoutes().size(), 2); + assertTrue(bestSolution.getUnassignedJobs().isEmpty()); + } + + private VehicleRoutingProblemSolution getVehicleRoutingProblemSolution(VehicleRoutingProblem vrp, String config) { + StateManager stateManager = new StateManager(vrp); + final SolutionCostCalculator calculator = new VariablePlusFixedSolutionCostCalculatorFactory(stateManager).createCalculator(); + SolutionCostCalculator solutionCostCalculator = new SolutionCostCalculator() { + @Override + public double getCosts(VehicleRoutingProblemSolution solution) { + return calculator.getCosts(solution) + solution.getUnassignedJobs().size() * 100; + } + }; + final VehicleRoutingAlgorithm algorithm = VehicleRoutingAlgorithms.readAndCreateAlgorithm(vrp, 1, config, stateManager, new ConstraintManager(vrp, stateManager), solutionCostCalculator); + return Solutions.bestOf(algorithm.searchSolutions()); + } + + + private static VehicleRoutingTransportCosts getTransportCosts() { + return new VehicleRoutingTransportCosts() { + @Override + public double getBackwardTransportCost(Location from, Location to, double arrivalTime, Driver driver, Vehicle vehicle) { return getDistance(from, to, arrivalTime, vehicle); } + + @Override + public double getBackwardTransportTime(Location from, Location to, double arrivalTime, Driver driver, Vehicle vehicle) { return getDistance(from, to, arrivalTime, vehicle); } + + @Override + public double getTransportCost(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) { return getDistance(from, to, departureTime, vehicle); } + + @Override + public double getTransportTime(Location from, Location to, double departureTime, Driver driver, Vehicle vehicle) { return getDistance(from, to, departureTime, vehicle); } + + @Override + public double getDistance(Location from, Location to, double departureTime, Vehicle vehicle) { + return Math.abs(from.getCoordinate().getX() - to.getCoordinate().getX()) + Math.abs(from.getCoordinate().getY() - to.getCoordinate().getY()); + } + }; + } + + private static Vehicle getVehicle(String id, Location location, int start, int end, int capacity, Set skills, boolean returnToDepot, int fixedCost, int costPerDistance) { + return VehicleImpl.Builder.newInstance(id) + .setStartLocation(location).setLatestArrival(end).setEarliestStart(start).setType( + VehicleTypeImpl.Builder.newInstance(UUID.randomUUID().toString()).setFixedCost(fixedCost).setCostPerDistance(costPerDistance).addCapacityDimension(0, capacity).build() + ) + .addAllSkills(skills).setReturnToDepot(returnToDepot).build(); + + } + + private static Service getService(Location location, int start, int end, Set requiredSkills, int priority) { + return Delivery.Builder.newInstance("service_" + UUID.randomUUID().toString().substring(0,5)) + .setLocation(location) + .setServiceTime(1) + .addTimeWindow(new TimeWindow(start, end)) + .addSizeDimension(0, 1) + .addAllRequiredSkills(requiredSkills) + .setPriority(priority) + .setName(UUID.randomUUID().toString()).build(); + + } + + private static Shipment getShipment(Location pickupLocation, Location dropoffLocation, int pickupStart, int pickupEnd, int dropoffStart, int dropoffEnd, Set skills, int priority) { + return Shipment.Builder.newInstance("shipment_" + UUID.randomUUID().toString().substring(0,5)) + .setPickupLocation(pickupLocation).setDeliveryLocation(dropoffLocation) + .setDeliveryServiceTime(1).setPickupServiceTime(1) + .addSizeDimension(0, 1) + .setPickupTimeWindow(new TimeWindow(pickupStart, pickupEnd)) + .setDeliveryTimeWindow(new TimeWindow(dropoffStart, dropoffEnd)) + .addAllRequiredSkills(skills) + .setPriority(priority) + .build(); + } + +} diff --git a/jsprit-io/src/test/resources/com/graphhopper/jsprit/io/algorithm/configWithRandomInsert.xml b/jsprit-io/src/test/resources/com/graphhopper/jsprit/io/algorithm/configWithRandomInsert.xml new file mode 100644 index 00000000..2f462581 --- /dev/null +++ b/jsprit-io/src/test/resources/com/graphhopper/jsprit/io/algorithm/configWithRandomInsert.xml @@ -0,0 +1,87 @@ + + + + + + + 2000 + + + + + + + + + 1 + + + + + 0.2 + 20 + + + + + 0.9 + + + + + + 0.1 + + + + + + + + + + 0.3 + + + + + + 0.4 + + + + + + + + + 0.05 + + + + + + 0.2 + + + + +