From 2835d15dd33674c383f2d4066c7e63e55ec7fc8f Mon Sep 17 00:00:00 2001 From: oblonski <4sschroeder@gmail.com> Date: Mon, 21 Oct 2013 07:53:09 +0200 Subject: [PATCH] add shipment based activities, modify inserter and implement insertionAlgo for shipments --- .../src/main/java/algorithms/Inserter.java | 85 ++++++- ...LocalActivityInsertionCostsCalculator.java | 11 +- .../ShipmentInsertionCalculator.java | 40 +--- .../java/basics/VehicleRoutingProblem.java | 15 +- .../java/basics/route/DeliverShipment.java | 4 + .../java/basics/route/PickupShipment.java | 5 + .../java/basics/route/TourActivities.java | 7 + .../main/java/basics/route/VehicleRoute.java | 5 + .../algorithms/AverageJobDistanceTest.java | 68 ++++++ ...PDVRPWithShipmentsAlgoFromScratchTest.java | 219 ++++++++++++++++++ .../ShipmentInsertionCalculatorTest.java | 160 +++++++++++++ .../src/test/java/basics/route/TestTour.java | 2 + 12 files changed, 570 insertions(+), 51 deletions(-) create mode 100644 jsprit-core/src/test/java/algorithms/AverageJobDistanceTest.java create mode 100644 jsprit-core/src/test/java/algorithms/BuildPDVRPWithShipmentsAlgoFromScratchTest.java create mode 100644 jsprit-core/src/test/java/algorithms/ShipmentInsertionCalculatorTest.java diff --git a/jsprit-core/src/main/java/algorithms/Inserter.java b/jsprit-core/src/main/java/algorithms/Inserter.java index a7dd5721..3e70125f 100644 --- a/jsprit-core/src/main/java/algorithms/Inserter.java +++ b/jsprit-core/src/main/java/algorithms/Inserter.java @@ -19,19 +19,91 @@ package algorithms; import algorithms.InsertionData.NoInsertionFound; import basics.Job; import basics.Service; +import basics.Shipment; +import basics.route.DefaultShipmentActivityFactory; import basics.route.DefaultTourActivityFactory; +import basics.route.TourActivity; import basics.route.TourActivityFactory; +import basics.route.TourShipmentActivityFactory; import basics.route.VehicleRoute; class Inserter { + + interface JobInsertionHandler { + void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route); + + void setNextHandler(JobInsertionHandler handler); + } + + class JobExceptionHandler implements JobInsertionHandler{ + + @Override + public void handleJobInsertion(Job job, InsertionData iData,VehicleRoute route) { + throw new IllegalStateException("job insertion is not supported. Do not know job type."); + } + + @Override + public void setNextHandler(JobInsertionHandler handler) { + // TODO Auto-generated method stub + + } + + } + + class ServiceInsertionHandler implements JobInsertionHandler{ + + private TourActivityFactory activityFactory = new DefaultTourActivityFactory(); + + private JobInsertionHandler delegator = new JobExceptionHandler(); + + @Override + public void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route) { + if(job instanceof Service){ + route.getTourActivities().addActivity(iData.getDeliveryInsertionIndex(), this.activityFactory.createActivity((Service)job)); + route.setDepartureTime(iData.getVehicleDepartureTime()); + } + else delegator.handleJobInsertion(job, iData, route); + } + + public void setNextHandler(JobInsertionHandler jobInsertionHandler){ + this.delegator = jobInsertionHandler; + } + + } + + class ShipmentInsertionHandler implements JobInsertionHandler { + + private TourShipmentActivityFactory activityFactory = new DefaultShipmentActivityFactory(); + + private JobInsertionHandler delegator = new JobExceptionHandler(); + + @Override + public void handleJobInsertion(Job job, InsertionData iData, VehicleRoute route) { + if(job instanceof Shipment){ + TourActivity pickupShipment = this.activityFactory.createPickup((Shipment)job); + TourActivity deliverShipment = this.activityFactory.createDelivery((Shipment)job); + route.getTourActivities().addActivity(iData.getDeliveryInsertionIndex(), deliverShipment); + route.getTourActivities().addActivity(iData.getPickupInsertionIndex(), pickupShipment); + route.setDepartureTime(iData.getVehicleDepartureTime()); + } + else delegator.handleJobInsertion(job, iData, route); + } + + public void setNextHandler(JobInsertionHandler jobInsertionHandler){ + this.delegator = jobInsertionHandler; + } + + } private InsertionListeners insertionListeners; - private TourActivityFactory activityFactory; + private JobInsertionHandler jobInsertionHandler; public Inserter(InsertionListeners insertionListeners) { this.insertionListeners = insertionListeners; - activityFactory = new DefaultTourActivityFactory(); + new DefaultTourActivityFactory(); + jobInsertionHandler = new ServiceInsertionHandler(); + jobInsertionHandler.setNextHandler(new ShipmentInsertionHandler()); } public void insertJob(Job job, InsertionData insertionData, VehicleRoute vehicleRoute){ @@ -43,14 +115,7 @@ class Inserter { insertionListeners.informVehicleSwitched(vehicleRoute, vehicleRoute.getVehicle(), insertionData.getSelectedVehicle()); vehicleRoute.setVehicle(insertionData.getSelectedVehicle(), insertionData.getVehicleDepartureTime()); } -// if(vehicleRoute.getDepartureTime() != vehicleRoute.g) - if(job instanceof Service) { - vehicleRoute.getTourActivities().addActivity(insertionData.getDeliveryInsertionIndex(), activityFactory.createActivity((Service)job)); - vehicleRoute.setDepartureTime(insertionData.getVehicleDepartureTime()); - } - else throw new IllegalStateException("neither service nor shipment. this is not supported."); - + jobInsertionHandler.handleJobInsertion(job, insertionData, vehicleRoute); insertionListeners.informJobInserted(job, vehicleRoute, insertionData.getInsertionCost(), insertionData.getAdditionalTime()); -// updateTour(vehicleRoute); } } diff --git a/jsprit-core/src/main/java/algorithms/LocalActivityInsertionCostsCalculator.java b/jsprit-core/src/main/java/algorithms/LocalActivityInsertionCostsCalculator.java index f845fd33..9dd9a20b 100644 --- a/jsprit-core/src/main/java/algorithms/LocalActivityInsertionCostsCalculator.java +++ b/jsprit-core/src/main/java/algorithms/LocalActivityInsertionCostsCalculator.java @@ -30,6 +30,7 @@ class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCal private HardActivityLevelConstraint hardConstraint; private VehicleRoutingTransportCosts routingCosts; + private VehicleRoutingActivityCosts activityCosts; public LocalActivityInsertionCostsCalculator(VehicleRoutingTransportCosts routingCosts, VehicleRoutingActivityCosts actCosts, HardActivityLevelConstraint hardActivityLevelConstraint) { @@ -66,12 +67,16 @@ class LocalActivityInsertionCostsCalculator implements ActivityInsertionCostsCal double oldCosts; double oldTime; if(iFacts.getRoute().isEmpty()){ - oldCosts = 0.0; - oldTime = 0.0; + double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle()); + double arrTime_nextAct = routingCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle()); + + double actCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct, iFacts.getNewDriver(), iFacts.getNewVehicle()); + oldCosts = tp_costs_prevAct_nextAct + actCost_nextAct; + oldTime = (nextAct.getArrTime() - depTimeAtPrevAct); } else{ double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle()); - double arrTime_nextAct = routingCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevAct.getEndTime(), iFacts.getNewDriver(), iFacts.getNewVehicle()); + double arrTime_nextAct = routingCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle()); double actCost_nextAct = activityCosts.getActivityCost(nextAct, arrTime_nextAct, iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle()); oldCosts = tp_costs_prevAct_nextAct + actCost_nextAct; diff --git a/jsprit-core/src/main/java/algorithms/ShipmentInsertionCalculator.java b/jsprit-core/src/main/java/algorithms/ShipmentInsertionCalculator.java index 1f2cb998..a2d91f8c 100644 --- a/jsprit-core/src/main/java/algorithms/ShipmentInsertionCalculator.java +++ b/jsprit-core/src/main/java/algorithms/ShipmentInsertionCalculator.java @@ -145,7 +145,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCalculator{ if(totalActivityInsertionCosts < bestCost){ bestCost = totalActivityInsertionCosts; pickupInsertionIndex = i; - deliveryInsertionIndex = activities.size() - 1; + deliveryInsertionIndex = activities.size(); } } //update prevAct and endTime @@ -166,43 +166,11 @@ final class ShipmentInsertionCalculator implements JobInsertionCalculator{ double totalActivityInsertionCosts = pickupAIC.getAdditionalCosts() + deliveryAIC.getAdditionalCosts(); if(totalActivityInsertionCosts < bestCost){ bestCost = totalActivityInsertionCosts; - pickupInsertionIndex = activities.size() - 1; - deliveryInsertionIndex = activities.size() - 1; + pickupInsertionIndex = activities.size(); + deliveryInsertionIndex = activities.size(); } } } - -// for(TourActivity nextAct : activities){ -// if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){ -// ActivityInsertionCosts mc = calculate(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActEndTime); -// if(mc != null){ -// if(mc.getAdditionalCosts() < bestCost){ -// bestCost = mc.getAdditionalCosts(); -// bestMarginals = mc; -// insertionIndex = actIndex; -// } -// } -// } -// double nextActArrTime = prevActEndTime + transportCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevActEndTime, newDriver, newVehicle); -// double nextActEndTime = CalcUtils.getActivityEndTime(nextActArrTime, nextAct); -// -// prevActEndTime = nextActEndTime; -// -// prevAct = nextAct; -// actIndex++; -// } -// End nextAct = end; -// if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){ -// ActivityInsertionCosts mc = calculate(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActEndTime); -// if(mc != null) { -// if(mc.getAdditionalCosts() < bestCost){ -// bestCost = mc.getAdditionalCosts(); -// bestMarginals = mc; -// insertionIndex = actIndex; -// } -// } -// } - if(pickupInsertionIndex == InsertionData.NO_INDEX) { return InsertionData.noInsertionFound(); } @@ -211,7 +179,7 @@ final class ShipmentInsertionCalculator implements JobInsertionCalculator{ return insertionData; } - public ActivityInsertionCosts calculate(InsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double departureTimeAtPrevAct) { + private ActivityInsertionCosts calculate(InsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double departureTimeAtPrevAct) { return activityInsertionCostsCalculator.calculate(iFacts, prevAct, nextAct, newAct, departureTimeAtPrevAct); } diff --git a/jsprit-core/src/main/java/basics/VehicleRoutingProblem.java b/jsprit-core/src/main/java/basics/VehicleRoutingProblem.java index dad31f6a..ae020b00 100644 --- a/jsprit-core/src/main/java/basics/VehicleRoutingProblem.java +++ b/jsprit-core/src/main/java/basics/VehicleRoutingProblem.java @@ -236,14 +236,25 @@ public class VehicleRoutingProblem { if(job instanceof Service) { addService((Service) job); } + else if(job instanceof Shipment){ + addShipment((Shipment)job); + } else{ - if(jobs.containsKey(job.getId())){ logger.warn("job " + job + " already in job list. overrides existing job."); } - jobs.put(job.getId(),job); +// if(jobs.containsKey(job.getId())){ logger.warn("job " + job + " already in job list. overrides existing job."); } +// coordinates.put(job.getLocationId(), job.getCoord()); +// jobs.put(job.getId(),job); } return this; } + private void addShipment(Shipment job) { + if(jobs.containsKey(job.getId())){ logger.warn("job " + job + " already in job list. overrides existing job."); } + coordinates.put(job.getPickupLocation(), job.getPickupCoord()); + coordinates.put(job.getDeliveryLocation(), job.getDeliveryCoord()); + jobs.put(job.getId(),job); + } + /** * Adds a vehicle. * diff --git a/jsprit-core/src/main/java/basics/route/DeliverShipment.java b/jsprit-core/src/main/java/basics/route/DeliverShipment.java index 0813bc9e..95c4a572 100644 --- a/jsprit-core/src/main/java/basics/route/DeliverShipment.java +++ b/jsprit-core/src/main/java/basics/route/DeliverShipment.java @@ -80,4 +80,8 @@ public final class DeliverShipment implements DeliveryActivity{ return new DeliverShipment(this); } + @Override + public String toString() { + return "[act="+getName()+"][loc="+getLocationId()+"]"; + } } diff --git a/jsprit-core/src/main/java/basics/route/PickupShipment.java b/jsprit-core/src/main/java/basics/route/PickupShipment.java index 60c108d2..c509cf78 100644 --- a/jsprit-core/src/main/java/basics/route/PickupShipment.java +++ b/jsprit-core/src/main/java/basics/route/PickupShipment.java @@ -79,5 +79,10 @@ public final class PickupShipment implements PickupActivity{ public TourActivity duplicate() { return new PickupShipment(this); } + + @Override + public String toString() { + return "[act="+getName()+"][loc="+getLocationId()+"]"; + } } diff --git a/jsprit-core/src/main/java/basics/route/TourActivities.java b/jsprit-core/src/main/java/basics/route/TourActivities.java index 1625b683..c7a1eaf5 100644 --- a/jsprit-core/src/main/java/basics/route/TourActivities.java +++ b/jsprit-core/src/main/java/basics/route/TourActivities.java @@ -152,6 +152,7 @@ public class TourActivities { public void addActivity(int insertionIndex, TourActivity act) { assert insertionIndex >= 0 : "insertionIndex == 0, this cannot be"; + if(tourActivities.contains(act)) throw new IllegalStateException("act " + act + " already in tour. cannot add act twice."); /* * if 1 --> between start and act(0) --> act(0) * if 2 && 2 <= acts.size --> between act(0) and act(1) --> act(1) @@ -164,6 +165,12 @@ public class TourActivities { addJob(act); } + /** + * adds activity. + * + * @throw {@link IllegalStateException} if same activity is added twice. + * @param act + */ public void addActivity(TourActivity act){ if(tourActivities.contains(act)) throw new IllegalStateException("act " + act + " already in tour. cannot add act twice."); tourActivities.add(act); diff --git a/jsprit-core/src/main/java/basics/route/VehicleRoute.java b/jsprit-core/src/main/java/basics/route/VehicleRoute.java index 3f6f41bc..865f3aa3 100644 --- a/jsprit-core/src/main/java/basics/route/VehicleRoute.java +++ b/jsprit-core/src/main/java/basics/route/VehicleRoute.java @@ -187,6 +187,11 @@ public class VehicleRoute { public End getEnd() { return end; } + + @Override + public String toString() { + return "[start="+start+"][end=" + end + "][departureTime=" + start.getEndTime() + "][vehicle=" + vehicle + "][driver=" + driver + "][nuOfActs="+tourActivities.getActivities().size()+"]"; + } @Deprecated public void setVehicleRouteCostCalculator(VehicleRouteCostCalculator costAccumulator){ diff --git a/jsprit-core/src/test/java/algorithms/AverageJobDistanceTest.java b/jsprit-core/src/test/java/algorithms/AverageJobDistanceTest.java new file mode 100644 index 00000000..ec375f72 --- /dev/null +++ b/jsprit-core/src/test/java/algorithms/AverageJobDistanceTest.java @@ -0,0 +1,68 @@ +package algorithms; + +import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; + +import java.awt.image.CropImageFilter; + +import org.junit.Before; +import org.junit.Test; + +import util.Coordinate; +import util.CrowFlyCosts; +import util.Locations; +import util.ManhattanCosts; +import basics.Service; +import basics.Shipment; + +public class AverageJobDistanceTest { + + + private CrowFlyCosts routingCosts; + + @Before + public void doBefore(){ + Locations locations = new Locations(){ + + @Override + public Coordinate getCoord(String id) { + //assume: locationId="x,y" + String[] splitted = id.split(","); + return Coordinate.newInstance(Double.parseDouble(splitted[0]), + Double.parseDouble(splitted[1])); + } + + }; + routingCosts = new CrowFlyCosts(locations); + + } + + @Test + public void distanceOfTwoEqualShipmentsShouldBeSmallerThanAnyOtherDistance(){ + Shipment s1 = Shipment.Builder.newInstance("s1", 1).setPickupLocation("0,0").setDeliveryLocation("10,10").build(); + Shipment s2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("0,0").setDeliveryLocation("10,10").build(); + + double dist = new AvgJobDistance(routingCosts).calculateDistance(s1, s2); + + for(int i=0;i<10;i++){ + for(int j=0;j<10;j++){ + Shipment other1 = Shipment.Builder.newInstance("s1", 1).setPickupLocation("0,0").setDeliveryLocation(i+","+j).build(); + Shipment other2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("0,0").setDeliveryLocation("10,10").build(); + double dist2 = new AvgJobDistance(routingCosts).calculateDistance(other1, other2); + System.out.println("("+i+","+j+"), dist=" + dist + ", dist2=" + dist2); + assertTrue(dist<=dist2+dist2*0.001); + } + } + } + + + + @Test + public void whenServicesHaveSameLocation_distanceShouldBeZero(){ + Service s1 = Service.Builder.newInstance("s1", 1).setLocationId("10,0").build(); + Service s2 = Service.Builder.newInstance("s2", 1).setLocationId("10,0").build(); + + double dist = new AvgJobDistance(routingCosts).calculateDistance(s1, s2); + assertEquals(0.0,dist,0.01); + } +} diff --git a/jsprit-core/src/test/java/algorithms/BuildPDVRPWithShipmentsAlgoFromScratchTest.java b/jsprit-core/src/test/java/algorithms/BuildPDVRPWithShipmentsAlgoFromScratchTest.java new file mode 100644 index 00000000..598f54a7 --- /dev/null +++ b/jsprit-core/src/test/java/algorithms/BuildPDVRPWithShipmentsAlgoFromScratchTest.java @@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (C) 2013 Stefan Schroeder + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + ******************************************************************************/ +package algorithms; + +import java.util.Collection; + +import org.apache.log4j.Logger; +import org.junit.Before; +import org.junit.Test; + +import util.Coordinate; +import util.Solutions; +import algorithms.HardConstraints.HardActivityLevelConstraintManager; +import algorithms.StateManager.StateImpl; +import algorithms.StateUpdates.UpdateActivityTimes; +import algorithms.StateUpdates.UpdateCostsAtAllLevels; +import algorithms.StateUpdates.UpdateEarliestStartTimeWindowAtActLocations; +import algorithms.StateUpdates.UpdateLatestOperationStartTimeAtActLocations; +import algorithms.acceptors.AcceptNewIfBetterThanWorst; +import algorithms.selectors.SelectBest; +import basics.Delivery; +import basics.Job; +import basics.Pickup; +import basics.Shipment; +import basics.VehicleRoutingAlgorithm; +import basics.VehicleRoutingProblem; +import basics.VehicleRoutingProblemSolution; +import basics.algo.InsertionStartsListener; +import basics.algo.JobInsertedListener; +import basics.algo.SearchStrategy; +import basics.algo.SearchStrategyManager; +import basics.algo.SolutionCostCalculator; +import basics.route.TourActivity; +import basics.route.Vehicle; +import basics.route.VehicleImpl; +import basics.route.VehicleRoute; +import basics.route.VehicleType; +import basics.route.VehicleTypeImpl; + +public class BuildPDVRPWithShipmentsAlgoFromScratchTest { + + VehicleRoutingProblem vrp; + + VehicleRoutingAlgorithm vra; + + static Logger log = Logger.getLogger(BuildPDVRPWithShipmentsAlgoFromScratchTest.class); + + @Before + public void setup(){ + + VehicleRoutingProblem.Builder builder = VehicleRoutingProblem.Builder.newInstance(); + + VehicleType type = VehicleTypeImpl.Builder.newInstance("t", 2).setCostPerDistance(1.0).build(); + Vehicle v = VehicleImpl.Builder.newInstance("v").setLocationCoord(Coordinate.newInstance(-1, -1)).setType(type).build(); + + Shipment s1 = Shipment.Builder.newInstance("s1", 1).setPickupCoord(Coordinate.newInstance(0, 0)).setDeliveryCoord(Coordinate.newInstance(10, 10)).build(); + Shipment s2 = Shipment.Builder.newInstance("s2", 1).setPickupCoord(Coordinate.newInstance(1, 1)).setDeliveryCoord(Coordinate.newInstance(10, 10)).build(); + builder.addJob(s1).addJob(s2); + builder.addVehicle(v); + + vrp = builder.build(); + + final StateManagerImpl stateManager = new StateManagerImpl(); + + HardActivityLevelConstraintManager actLevelConstraintAccumulator = new HardActivityLevelConstraintManager(); + actLevelConstraintAccumulator.addConstraint(new HardConstraints.HardPickupAndDeliveryActivityLevelConstraint(stateManager)); + actLevelConstraintAccumulator.addConstraint(new HardConstraints.HardTimeWindowActivityLevelConstraint(stateManager, vrp.getTransportCosts())); + + ActivityInsertionCostsCalculator marginalCalculus = new LocalActivityInsertionCostsCalculator(vrp.getTransportCosts(), vrp.getActivityCosts(), actLevelConstraintAccumulator); + + ShipmentInsertionCalculator serviceInsertion = new ShipmentInsertionCalculator(vrp.getTransportCosts(), marginalCalculus, new HardConstraints.HardPickupAndDeliveryLoadConstraint(stateManager)); +// CalculatesServiceInsertion serviceInsertion = new CalculatesServiceInsertion(vrp.getTransportCosts(), marginalCalculus, new HardConstraints.HardLoadConstraint(stateManager)); + + VehicleFleetManager fleetManager = new InfiniteVehicles(vrp.getVehicles()); + JobInsertionCalculator finalServiceInsertion = new CalculatesVehTypeDepServiceInsertion(fleetManager, serviceInsertion); + + BestInsertion bestInsertion = new BestInsertion(finalServiceInsertion); + + RuinRadial radial = new RuinRadial(vrp, 0.15, new AvgJobDistance(vrp.getTransportCosts())); + RuinRandom random = new RuinRandom(vrp, 0.25); + + SolutionCostCalculator solutionCostCalculator = new SolutionCostCalculator() { + + @Override + public void calculateCosts(VehicleRoutingProblemSolution solution) { + double costs = 0.0; + for(VehicleRoute route : solution.getRoutes()){ + costs += stateManager.getRouteState(route, StateTypes.COSTS).toDouble(); + } + solution.setCost(costs); + } + }; + + SearchStrategy randomStrategy = new SearchStrategy(new SelectBest(), new AcceptNewIfBetterThanWorst(1), solutionCostCalculator); + RuinAndRecreateModule randomModule = new RuinAndRecreateModule("randomRuin_bestInsertion", bestInsertion, random); + randomStrategy.addModule(randomModule); + + SearchStrategy radialStrategy = new SearchStrategy(new SelectBest(), new AcceptNewIfBetterThanWorst(1), solutionCostCalculator); + RuinAndRecreateModule radialModule = new RuinAndRecreateModule("radialRuin_bestInsertion", bestInsertion, radial); + radialStrategy.addModule(radialModule); + + SearchStrategyManager strategyManager = new SearchStrategyManager(); + strategyManager.addStrategy(radialStrategy, 0.5); + strategyManager.addStrategy(randomStrategy, 0.5); + + vra = new VehicleRoutingAlgorithm(vrp, strategyManager); + + + vra.getAlgorithmListeners().addListener(new StateUpdates.ResetStateManager(stateManager)); + + final RouteActivityVisitor iterateForward = new RouteActivityVisitor(); + + iterateForward.addActivityVisitor(new UpdateActivityTimes(vrp.getTransportCosts())); + iterateForward.addActivityVisitor(new UpdateEarliestStartTimeWindowAtActLocations(stateManager, vrp.getTransportCosts())); + iterateForward.addActivityVisitor(new UpdateCostsAtAllLevels(vrp.getActivityCosts(), vrp.getTransportCosts(), stateManager)); + + iterateForward.addActivityVisitor(new StateUpdates.UpdateOccuredDeliveriesAtActivityLevel(stateManager)); + iterateForward.addActivityVisitor(new StateUpdates.UpdateLoadAtActivityLevel(stateManager)); + + final ReverseRouteActivityVisitor iterateBackward = new ReverseRouteActivityVisitor(); + iterateBackward.addActivityVisitor(new UpdateLatestOperationStartTimeAtActLocations(stateManager, vrp.getTransportCosts())); + iterateBackward.addActivityVisitor(new StateUpdates.UpdateFuturePickupsAtActivityLevel(stateManager)); + + + InsertionStartsListener loadVehicleInDepot = new InsertionStartsListener() { + + @Override + public void informInsertionStarts(Collection vehicleRoutes, Collection unassignedJobs) { + for(VehicleRoute route : vehicleRoutes){ + int loadAtDepot = 0; + int loadAtEnd = 0; + for(Job j : route.getTourActivities().getJobs()){ + if(j instanceof Delivery){ + loadAtDepot += j.getCapacityDemand(); + } + if(j instanceof Pickup){ + loadAtEnd += j.getCapacityDemand(); + } + } + stateManager.putRouteState(route, StateTypes.LOAD_AT_DEPOT, new StateImpl(loadAtDepot)); + stateManager.putRouteState(route, StateTypes.LOAD, new StateImpl(loadAtEnd)); + iterateForward.visit(route); + iterateBackward.visit(route); + } + } + + }; + + vra.getSearchStrategyManager().addSearchStrategyModuleListener(new RemoveEmptyVehicles(fleetManager)); + + JobInsertedListener updateLoadAfterJobHasBeenInserted = new JobInsertedListener() { + + @Override + public void informJobInserted(Job job2insert, VehicleRoute inRoute, double additionalCosts, double additionalTime) { +// log.info("insert job " + job2insert.getClass().toString() + " job " + job2insert + "" + job2insert.getCapacityDemand() + " in route " + inRoute.getTourActivities()); + + if(job2insert instanceof Delivery){ + int loadAtDepot = (int) stateManager.getRouteState(inRoute, StateTypes.LOAD_AT_DEPOT).toDouble(); +// log.info("loadAtDepot="+loadAtDepot); + stateManager.putRouteState(inRoute, StateTypes.LOAD_AT_DEPOT, new StateImpl(loadAtDepot + job2insert.getCapacityDemand())); + } + if(job2insert instanceof Pickup){ + int loadAtEnd = (int) stateManager.getRouteState(inRoute, StateTypes.LOAD).toDouble(); +// log.info("loadAtEnd="+loadAtEnd); + stateManager.putRouteState(inRoute, StateTypes.LOAD, new StateImpl(loadAtEnd + job2insert.getCapacityDemand())); + } + iterateForward.visit(inRoute); + iterateBackward.visit(inRoute); + } + }; + + bestInsertion.addListener(loadVehicleInDepot); + bestInsertion.addListener(updateLoadAfterJobHasBeenInserted); + + VehicleRoutingProblemSolution iniSolution = new CreateInitialSolution(bestInsertion, solutionCostCalculator).createInitialSolution(vrp); +// System.out.println("ini: costs="+iniSolution.getCost()+";#routes="+iniSolution.getRoutes().size()); + vra.addInitialSolution(iniSolution); + + vra.setNuOfIterations(10000); + vra.setPrematureBreak(1000); + + } + + @Test + public void test(){ + Collection solutions = vra.searchSolutions(); + VehicleRoutingProblemSolution best = Solutions.getBest(solutions); + System.out.println(best.getCost()); + for(VehicleRoute r : best.getRoutes()){ + System.out.println(r); + System.out.println("#jobs="+r.getTourActivities().jobSize()); + System.out.println(r.getStart()); + for(TourActivity act : r.getTourActivities().getActivities()){ + System.out.println(act); + } + System.out.println(r.getEnd()); + } + +// for() + +// new VrpXMLWriter(vrp, solutions).write("output/pd_solomon_r101.xml"); + + } + +} diff --git a/jsprit-core/src/test/java/algorithms/ShipmentInsertionCalculatorTest.java b/jsprit-core/src/test/java/algorithms/ShipmentInsertionCalculatorTest.java new file mode 100644 index 00000000..5dac938a --- /dev/null +++ b/jsprit-core/src/test/java/algorithms/ShipmentInsertionCalculatorTest.java @@ -0,0 +1,160 @@ +package algorithms; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import util.Coordinate; +import util.Locations; +import util.ManhattanCosts; +import algorithms.HardConstraints.HardActivityLevelConstraint; +import algorithms.HardConstraints.HardRouteLevelConstraint; +import basics.Shipment; +import basics.costs.VehicleRoutingActivityCosts; +import basics.costs.VehicleRoutingTransportCosts; +import basics.route.Driver; +import basics.route.TourActivity; +import basics.route.Vehicle; +import basics.route.VehicleImpl; +import basics.route.VehicleRoute; +import basics.route.VehicleType; +import basics.route.VehicleTypeImpl; + +public class ShipmentInsertionCalculatorTest { + + VehicleRoutingTransportCosts routingCosts; + + VehicleRoutingActivityCosts activityCosts = new VehicleRoutingActivityCosts(){ + + @Override + public double getActivityCost(TourActivity tourAct, double arrivalTime,Driver driver, Vehicle vehicle) { + return 0; + } + + }; + + HardActivityLevelConstraint hardActivityLevelConstraint = new HardActivityLevelConstraint() { + + @Override + public boolean fulfilled(InsertionContext iFacts, TourActivity prevAct,TourActivity newAct, TourActivity nextAct, double prevActDepTime) { + return true; + } + }; + + HardRouteLevelConstraint hardRouteLevelConstraint = new HardRouteLevelConstraint(){ + + @Override + public boolean fulfilled(InsertionContext insertionContext) { + return true; + } + + }; + + ActivityInsertionCostsCalculator activityInsertionCostsCalculator; + + ShipmentInsertionCalculator insertionCalculator; + + Vehicle vehicle; + + @Before + public void doBefore(){ + Locations locations = new Locations(){ + + @Override + public Coordinate getCoord(String id) { + //assume: locationId="x,y" + String[] splitted = id.split(","); + return Coordinate.newInstance(Double.parseDouble(splitted[0]), + Double.parseDouble(splitted[1])); + } + + }; + routingCosts = new ManhattanCosts(locations); + VehicleType type = VehicleTypeImpl.Builder.newInstance("t", 1).setCostPerDistance(1).build(); + vehicle = VehicleImpl.Builder.newInstance("v").setLocationId("0,0").setType(type).build(); + activityInsertionCostsCalculator = new LocalActivityInsertionCostsCalculator(routingCosts, activityCosts, hardActivityLevelConstraint); + createInsertionCalculator(hardRouteLevelConstraint); + } + + private void createInsertionCalculator(HardRouteLevelConstraint hardRouteLevelConstraint) { + insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityInsertionCostsCalculator, hardRouteLevelConstraint); + } + + @Test + public void whenCalculatingInsertionCostsOfShipment_itShouldReturnCorrectCostValue(){ + Shipment shipment = Shipment.Builder.newInstance("s", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + + InsertionData iData = insertionCalculator.calculate(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", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("10,10").setDeliveryLocation("0,0").build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + new Inserter(new InsertionListeners()).insertJob(shipment, new InsertionData(0,0,0,vehicle,null), route); + + InsertionData iData = insertionCalculator.calculate(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(0.0,iData.getInsertionCost(),0.05); + assertEquals(1,iData.getPickupInsertionIndex()); + assertEquals(2,iData.getDeliveryInsertionIndex()); + } + + @Test + public void whenInsertingShipmentInRouteWithNotEnoughCapacity_itShouldReturnNoInsertion(){ + Shipment shipment = Shipment.Builder.newInstance("s", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("10,10").setDeliveryLocation("0,0").build(); + VehicleRoute route = VehicleRoute.emptyRoute(); + new Inserter(new InsertionListeners()).insertJob(shipment, new InsertionData(0,0,0,vehicle,null), route); + createInsertionCalculator(new HardRouteLevelConstraint() { + + @Override + public boolean fulfilled(InsertionContext insertionContext) { + return false; + } + + }); + InsertionData iData = insertionCalculator.calculate(route, shipment2, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(InsertionData.noInsertionFound(),iData); + + } + + + @Test + public void whenInsertingThirdShipment_itShouldCalcCorrectVal(){ + Shipment shipment = Shipment.Builder.newInstance("s", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("10,10").setDeliveryLocation("0,0").build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3", 1).setPickupLocation("0,0").setDeliveryLocation("9,10").build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + Inserter inserter = new Inserter(new InsertionListeners()); + inserter.insertJob(shipment, new InsertionData(0,0,0,vehicle,null), route); + inserter.insertJob(shipment2, new InsertionData(0,1,2,vehicle,null),route); + + InsertionData iData = insertionCalculator.calculate(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", 1).setPickupLocation("0,10").setDeliveryLocation("10,0").build(); + Shipment shipment2 = Shipment.Builder.newInstance("s2", 1).setPickupLocation("10,10").setDeliveryLocation("0,0").build(); + Shipment shipment3 = Shipment.Builder.newInstance("s3", 1).setPickupLocation("0,0").setDeliveryLocation("9,9").build(); + + VehicleRoute route = VehicleRoute.emptyRoute(); + Inserter inserter = new Inserter(new InsertionListeners()); + inserter.insertJob(shipment, new InsertionData(0,0,0,vehicle,null), route); + inserter.insertJob(shipment2, new InsertionData(0,1,2,vehicle,null),route); + + InsertionData iData = insertionCalculator.calculate(route, shipment3, vehicle, 0.0, null, Double.MAX_VALUE); + assertEquals(2.0,iData.getInsertionCost(),0.05); + assertEquals(0,iData.getPickupInsertionIndex()); + assertEquals(1,iData.getDeliveryInsertionIndex()); + } + +} diff --git a/jsprit-core/src/test/java/basics/route/TestTour.java b/jsprit-core/src/test/java/basics/route/TestTour.java index e006f15c..6afcc5a5 100644 --- a/jsprit-core/src/test/java/basics/route/TestTour.java +++ b/jsprit-core/src/test/java/basics/route/TestTour.java @@ -87,6 +87,8 @@ public class TestTour { assertEquals(2,tour.getActivities().size()); } + + @Test public void whenRemovingShipment_tourShouldNotServiceItAnymore(){ Shipment s = Shipment.Builder.newInstance("s", 1).setDeliveryLocation("delLoc").setPickupLocation("pickLoc").build();