From 2e150057161595d84c8f1a8a34147b99589e3083 Mon Sep 17 00:00:00 2001 From: reinterpretcat Date: Tue, 6 Nov 2018 15:40:12 +0100 Subject: [PATCH] Fix Vehicle's break location is ignored #439 --- .../recreate/BreakInsertionCalculator.java | 8 +++ .../recreate/ServiceInsertionCalculator.java | 19 +++--- .../core/problem/BreakAssignmentTest.java | 65 +++++++++++++++++++ 3 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/BreakAssignmentTest.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 e94bad61d..d21bb4d83 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 @@ -67,6 +67,8 @@ final class BreakInsertionCalculator implements JobInsertionCostsCalculator { private final AdditionalAccessEgressCalculator additionalAccessEgressCalculator; + private final ServiceInsertionCalculator serviceInsertionCalculator; + public BreakInsertionCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts activityCosts, ActivityInsertionCostsCalculator additionalTransportCostsCalculator, ConstraintManager constraintManager, JobActivityFactory activityFactory) { super(); this.transportCosts = routingCosts; @@ -78,6 +80,7 @@ public BreakInsertionCalculator(VehicleRoutingTransportCosts routingCosts, Vehic this.additionalTransportCostsCalculator = additionalTransportCostsCalculator; additionalAccessEgressCalculator = new AdditionalAccessEgressCalculator(routingCosts); this.activityFactory = activityFactory; + this.serviceInsertionCalculator = new ServiceInsertionCalculator(routingCosts, activityCosts, additionalTransportCostsCalculator, constraintManager, activityFactory); logger.debug("initialise " + this); } @@ -105,6 +108,11 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job BreakActivity breakAct2Insert = (BreakActivity) activityFactory.createActivities(breakToInsert).get(0); insertionContext.getAssociatedActivities().add(breakAct2Insert); + if (!breakToInsert.hasVariableLocation()) { + breakAct2Insert.setLocation(breakToInsert.getLocation()); + return serviceInsertionCalculator.getInsertionData(insertionContext, breakToInsert, breakAct2Insert, currentRoute, newVehicle, newVehicleDepartureTime, newDriver, bestKnownCosts); + } + /* check hard constraints at route level */ 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 6007decb6..f9b5a799e 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 @@ -31,6 +31,7 @@ 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.ServiceActivity; 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; @@ -97,11 +98,14 @@ public String toString() { 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); Service service = (Service) jobToInsert; - int insertionIndex = InsertionData.NO_INDEX; - - TourActivity deliveryAct2Insert = activityFactory.createActivities(service).get(0); + TourActivity deliveryAct2Insert = activityFactory.createActivities(service).get(0); insertionContext.getAssociatedActivities().add(deliveryAct2Insert); + return getInsertionData(insertionContext, service, deliveryAct2Insert, currentRoute, newVehicle, newVehicleDepartureTime, newDriver, bestKnownCosts); + } + + InsertionData getInsertionData(final JobInsertionContext insertionContext, final Service service, final TourActivity deliveryAct2Insert, + final VehicleRoute currentRoute, final Vehicle newVehicle, double newVehicleDepartureTime, final Driver newDriver, final double bestKnownCosts) { /* check hard constraints at route level */ @@ -117,7 +121,7 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job double bestCost = bestKnownCosts; additionalICostsAtRouteLevel += additionalAccessEgressCalculator.getCosts(insertionContext); - TimeWindow bestTimeWindow = null; + TimeWindow bestTimeWindow = null; /* generate new start and end for new vehicle @@ -129,6 +133,7 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job TourActivity prevAct = start; double prevActStartTime = newVehicleDepartureTime; int actIndex = 0; + int insertionIndex = InsertionData.NO_INDEX; Iterator activityIterator = currentRoute.getActivities().iterator(); boolean tourEnd = false; while(!tourEnd){ @@ -139,7 +144,7 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job tourEnd = true; } boolean not_fulfilled_break = true; - for(TimeWindow timeWindow : service.getTimeWindows()) { + for(TimeWindow timeWindow : service.getTimeWindows()) { deliveryAct2Insert.setTheoreticalEarliestOperationStartTime(timeWindow.getStart()); deliveryAct2Insert.setTheoreticalLatestOperationStartTime(timeWindow.getEnd()); ActivityContext activityContext = new ActivityContext(); @@ -158,7 +163,7 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job } else if (status.equals(ConstraintsStatus.NOT_FULFILLED)) { not_fulfilled_break = false; } - } + } if(not_fulfilled_break) break; double nextActArrTime = prevActStartTime + transportCosts.getTransportTime(prevAct.getLocation(), nextAct.getLocation(), prevActStartTime, newDriver, newVehicle); prevActStartTime = Math.max(nextActArrTime, nextAct.getTheoreticalEarliestOperationStartTime()) + activityCosts.getActivityDuration(nextAct,nextActArrTime,newDriver,newVehicle); @@ -178,6 +183,4 @@ public InsertionData getInsertionData(final VehicleRoute currentRoute, final Job insertionData.setVehicleDepartureTime(newVehicleDepartureTime); return insertionData; } - - } diff --git a/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/BreakAssignmentTest.java b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/BreakAssignmentTest.java new file mode 100644 index 000000000..7f2fbba7d --- /dev/null +++ b/jsprit-core/src/test/java/com/graphhopper/jsprit/core/problem/BreakAssignmentTest.java @@ -0,0 +1,65 @@ +package com.graphhopper.jsprit.core.problem; + +import static org.junit.Assert.assertEquals; + +import com.graphhopper.jsprit.core.algorithm.box.Jsprit; +import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem.FleetSize; +import com.graphhopper.jsprit.core.problem.job.Break; +import com.graphhopper.jsprit.core.problem.job.Service; +import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute; +import com.graphhopper.jsprit.core.problem.solution.route.activity.BreakActivity; +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.VehicleImpl; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleImpl.Builder; +import com.graphhopper.jsprit.core.problem.vehicle.VehicleTypeImpl; +import com.graphhopper.jsprit.core.util.Solutions; +import org.junit.Test; + +public class BreakAssignmentTest { + @Test + public void whenBreakHasFixedLocation_breakActivityHasLocationSpecified() { + VehicleImpl vehicle = Builder.newInstance("vehicle") + .setType(VehicleTypeImpl.Builder.newInstance("vehicleType") + .addCapacityDimension(0, 2) + .build()) + .setStartLocation(Location.newInstance(0, 0)) + .setBreak(Break.Builder.newInstance("break") + .setTimeWindow(TimeWindow.newInstance(5, 8)) + .setLocation(Location.newInstance(6, 0)) + .setServiceTime(1) + .build()) + .build(); + Service service1 = Service.Builder.newInstance("1") + .addSizeDimension(0, 1) + .setLocation(Location.newInstance(5, 0)) + .setServiceTime(1) + .build(); + Service service2 = Service.Builder.newInstance("2") + .addSizeDimension(0, 1) + .setLocation(Location.newInstance(10, 0)) + .setServiceTime(10) + .build(); + VehicleRoutingProblem problem = VehicleRoutingProblem.Builder.newInstance() + .addVehicle(vehicle) + .addJob(service1) + .addJob(service2) + .setFleetSize(FleetSize.FINITE) + .build(); + + Location location = findBreak(Solutions.bestOf(Jsprit.Builder + .newInstance(problem) + .buildAlgorithm().searchSolutions()).getRoutes().iterator().next()) + .getLocation(); + + assertEquals(Location.newInstance(6, 0), location); + } + + private BreakActivity findBreak(VehicleRoute route) { + for (TourActivity tourActivity : route.getTourActivities().getActivities()) { + if (tourActivity instanceof BreakActivity) + return (BreakActivity) tourActivity; + } + throw new IllegalStateException("Break activity is not found."); + } +}