diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/CalculatorBuilder.java b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/CalculatorBuilder.java index 32ec036a..aedae439 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/CalculatorBuilder.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/CalculatorBuilder.java @@ -253,7 +253,7 @@ class CalculatorBuilder { } ShipmentInsertionCalculator shipmentInsertion = new ShipmentInsertionCalculator(vrp.getTransportCosts(), actInsertionCalc, constraintManager, constraintManager); - ServiceInsertionCalculator serviceInsertion = new ServiceInsertionCalculator(vrp.getTransportCosts(), actInsertionCalc, constraintManager, constraintManager); + ServiceInsertionCalculator serviceInsertion = new ServiceInsertionCalculator(vrp.getTransportCosts(), constraintManager); JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); switcher.put(Shipment.class, shipmentInsertion); @@ -263,8 +263,6 @@ class CalculatorBuilder { PenalyzeInsertionCostsWithPenaltyVehicle penalyzeInsertionCosts = new PenalyzeInsertionCostsWithPenaltyVehicle(switcher); -// JobInsertionCostsCalculator standardServiceInsertion = new ServiceInsertionCalculator(vrp.getTransportCosts(), actInsertionCalc, constraintManager, constraintManager); -// ((ServiceInsertionCalculator) standardServiceInsertion).setNeighborhood(vrp.getNeighborhood()); CalculatorPlusListeners calcPlusListeners = new CalculatorPlusListeners(penalyzeInsertionCosts); return calcPlusListeners; diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java index 1a1ead9c..c8000ac3 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/recreate/ServiceInsertionCalculator.java @@ -16,10 +16,12 @@ ******************************************************************************/ package jsprit.core.algorithm.recreate; -import jsprit.core.algorithm.recreate.ActivityInsertionCostsCalculator.ActivityInsertionCosts; +import jsprit.core.problem.constraint.ConstraintManager; import jsprit.core.problem.constraint.HardActivityStateLevelConstraint; import jsprit.core.problem.constraint.HardActivityStateLevelConstraint.ConstraintsStatus; import jsprit.core.problem.constraint.HardRouteStateLevelConstraint; +import jsprit.core.problem.constraint.SoftActivityConstraint; +import jsprit.core.problem.constraint.SoftRouteConstraint; import jsprit.core.problem.cost.VehicleRoutingTransportCosts; import jsprit.core.problem.driver.Driver; import jsprit.core.problem.job.Job; @@ -34,13 +36,15 @@ import jsprit.core.problem.solution.route.activity.TourActivityFactory; import jsprit.core.problem.vehicle.Vehicle; import jsprit.core.problem.vehicle.VehicleImpl.NoVehicle; import jsprit.core.util.CalculationUtils; -import jsprit.core.util.Neighborhood; import org.apache.log4j.Logger; - - - +/** + * Calculator that calculates the best insertion position for a {@link Service}. + * + * @author schroeder + * + */ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator{ private static final Logger logger = Logger.getLogger(ServiceInsertionCalculator.class); @@ -49,32 +53,21 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator{ private HardActivityStateLevelConstraint hardActivityLevelConstraint; - private Neighborhood neighborhood = new Neighborhood() { - - @Override - public boolean areNeighbors(String location1, String location2) { - return true; - } - }; + private SoftRouteConstraint softRouteConstraint; - private ActivityInsertionCostsCalculator activityInsertionCostsCalculator; + private SoftActivityConstraint softActivityConstraint; private VehicleRoutingTransportCosts transportCosts; private TourActivityFactory activityFactory; - - public void setNeighborhood(Neighborhood neighborhood) { - this.neighborhood = neighborhood; - logger.info("initialise neighborhood " + neighborhood); - } - - public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, ActivityInsertionCostsCalculator activityInsertionCostsCalculator, HardRouteStateLevelConstraint hardRouteLevelConstraint, HardActivityStateLevelConstraint hardActivityLevelConstraint) { + public ServiceInsertionCalculator(VehicleRoutingTransportCosts routingCosts, ConstraintManager constraintManager) { super(); - this.activityInsertionCostsCalculator = activityInsertionCostsCalculator; - this.hardRouteLevelConstraint = hardRouteLevelConstraint; - this.hardActivityLevelConstraint = hardActivityLevelConstraint; this.transportCosts = routingCosts; + hardRouteLevelConstraint = constraintManager; + hardActivityLevelConstraint = constraintManager; + softActivityConstraint = constraintManager; + softRouteConstraint = constraintManager; activityFactory = new DefaultTourActivityFactory(); logger.info("initialise " + this); } @@ -100,7 +93,10 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator{ } double bestCost = bestKnownCosts; - ActivityInsertionCosts bestMarginals = null; + + //from job2insert induced costs at route level + double routeICosts = softRouteConstraint.getCosts(insertionContext); + Service service = (Service)jobToInsert; int insertionIndex = InsertionData.NO_INDEX; @@ -115,21 +111,19 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator{ int actIndex = 0; boolean loopBroken = false; for(TourActivity nextAct : currentRoute.getTourActivities().getActivities()){ - if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){ - ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); - if(status.equals(ConstraintsStatus.FULFILLED)){ - ActivityInsertionCosts mc = calculate(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActStartTime); - if(mc.getAdditionalCosts() < bestCost){ - bestCost = mc.getAdditionalCosts(); - bestMarginals = mc; - insertionIndex = actIndex; - } - } - else if(status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)){ - loopBroken = true; - break; + ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); + if(status.equals(ConstraintsStatus.FULFILLED)){ + //from job2insert induced costs at activity level + double activityICosts = softActivityConstraint.getCosts(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); + if(routeICosts + activityICosts < bestCost){ + bestCost = routeICosts + activityICosts; + insertionIndex = actIndex; } } + else if(status.equals(ConstraintsStatus.NOT_FULFILLED_BREAK)){ + loopBroken = true; + break; + } double nextActArrTime = prevActStartTime + transportCosts.getTransportTime(prevAct.getLocationId(), nextAct.getLocationId(), prevActStartTime, newDriver, newVehicle); double nextActEndTime = CalculationUtils.getActivityEndTime(nextActArrTime, nextAct); prevActStartTime = nextActEndTime; @@ -138,29 +132,21 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator{ } End nextAct = end; if(!loopBroken){ - if(neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), prevAct.getLocationId()) && neighborhood.areNeighbors(deliveryAct2Insert.getLocationId(), nextAct.getLocationId())){ - ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); - if(status.equals(ConstraintsStatus.FULFILLED)){ - ActivityInsertionCosts mc = calculate(insertionContext, prevAct, nextAct, deliveryAct2Insert, prevActStartTime); - if(mc.getAdditionalCosts() < bestCost){ - bestCost = mc.getAdditionalCosts(); - bestMarginals = mc; - insertionIndex = actIndex; - } + ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); + if(status.equals(ConstraintsStatus.FULFILLED)){ + double activityICosts = softActivityConstraint.getCosts(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime); + if(routeICosts + activityICosts < bestCost){ + bestCost = routeICosts + activityICosts; + insertionIndex = actIndex; } } } - if(insertionIndex == InsertionData.NO_INDEX) { return InsertionData.createEmptyInsertionData(); } InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver); insertionData.setVehicleDepartureTime(newVehicleDepartureTime); - insertionData.setAdditionalTime(bestMarginals.getAdditionalTime()); return insertionData; } - public ActivityInsertionCosts calculate(JobInsertionContext iFacts, TourActivity prevAct, TourActivity nextAct, TourActivity newAct, double departureTimeAtPrevAct) { - return activityInsertionCostsCalculator.getCosts(iFacts, prevAct, nextAct, newAct, departureTimeAtPrevAct); - } } diff --git a/jsprit-core/src/main/java/jsprit/core/problem/constraint/AdditionalTransportationCosts.java b/jsprit-core/src/main/java/jsprit/core/problem/constraint/AdditionalTransportationCosts.java new file mode 100644 index 00000000..f2a4451a --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/problem/constraint/AdditionalTransportationCosts.java @@ -0,0 +1,70 @@ +package jsprit.core.problem.constraint; + +import jsprit.core.problem.cost.VehicleRoutingTransportCosts; +import jsprit.core.problem.misc.JobInsertionContext; +import jsprit.core.problem.solution.route.activity.End; +import jsprit.core.problem.solution.route.activity.TourActivity; +import jsprit.core.util.CalculationUtils; + +/** + * Calculates additional transportation costs induced by inserting newAct. + * + * @author schroeder + * + */ +public class AdditionalTransportationCosts implements SoftActivityConstraint{ + + private VehicleRoutingTransportCosts routingCosts; + + /** + * Constructs the calculator that calculates additional transportation costs induced by inserting new activity. + * + *

It is calculated at local level, i.e. the additional costs of inserting act_new between act_i and act_j is c(act_i,act_new,newVehicle)+c(act_new,act_j,newVehicle)-c(act_i,act_j,oldVehicle) + *

If newVehicle.isReturnToDepot == false then the additional costs of inserting act_new between act_i and end is c(act_i,act_new) [since act_new is then the new end-of-route] + * + * @param routingCosts + */ + public AdditionalTransportationCosts(VehicleRoutingTransportCosts routingCosts) { + super(); + this.routingCosts = routingCosts; + } + + /** + * Returns additional transportation costs induced by inserting newAct. + * + *

It is calculated at local level, i.e. the additional costs of inserting act_new between act_i and act_j is c(act_i,act_new,newVehicle)+c(act_new,act_j,newVehicle)-c(act_i,act_j,oldVehicle) + *

If newVehicle.isReturnToDepot == false then the additional costs of inserting act_new between act_i and end is c(act_i,act_new) [since act_new is then the new end-of-route] + */ + @Override + public double getCosts(JobInsertionContext iFacts, TourActivity prevAct,TourActivity newAct, TourActivity nextAct, double depTimeAtPrevAct) { + double tp_costs_prevAct_newAct = routingCosts.getTransportCost(prevAct.getLocationId(), newAct.getLocationId(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle()); + double tp_time_prevAct_newAct = routingCosts.getTransportTime(prevAct.getLocationId(), newAct.getLocationId(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle()); + + double newAct_arrTime = depTimeAtPrevAct + tp_time_prevAct_newAct; + double newAct_endTime = CalculationUtils.getActivityEndTime(newAct_arrTime, newAct); + + //open routes + if(nextAct instanceof End){ + if(!iFacts.getNewVehicle().isReturnToDepot()){ + return tp_costs_prevAct_newAct; + } + } + + double tp_costs_newAct_nextAct = routingCosts.getTransportCost(newAct.getLocationId(), nextAct.getLocationId(), newAct_endTime, iFacts.getNewDriver(), iFacts.getNewVehicle()); + double totalCosts = tp_costs_prevAct_newAct + tp_costs_newAct_nextAct; + + double oldCosts; + if(iFacts.getRoute().isEmpty()){ + double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), depTimeAtPrevAct, iFacts.getNewDriver(), iFacts.getNewVehicle()); + oldCosts = tp_costs_prevAct_nextAct; + } + else{ + double tp_costs_prevAct_nextAct = routingCosts.getTransportCost(prevAct.getLocationId(), nextAct.getLocationId(), prevAct.getEndTime(), iFacts.getRoute().getDriver(), iFacts.getRoute().getVehicle()); + oldCosts = tp_costs_prevAct_nextAct; + } + + double additionalCosts = totalCosts - oldCosts; + return additionalCosts; + } + +} diff --git a/jsprit-core/src/main/java/jsprit/core/problem/constraint/ConstraintManager.java b/jsprit-core/src/main/java/jsprit/core/problem/constraint/ConstraintManager.java index 6c2dc332..fd6545c4 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/constraint/ConstraintManager.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/constraint/ConstraintManager.java @@ -14,7 +14,7 @@ import jsprit.core.problem.solution.route.state.RouteAndActivityStateGetter; import org.apache.log4j.Logger; @SuppressWarnings("deprecation") -public class ConstraintManager implements HardActivityStateLevelConstraint, HardRouteStateLevelConstraint{ +public class ConstraintManager implements HardActivityStateLevelConstraint, HardRouteStateLevelConstraint, SoftActivityConstraint, SoftRouteConstraint{ public static enum Priority { CRITICAL, HIGH, LOW @@ -26,6 +26,10 @@ public class ConstraintManager implements HardActivityStateLevelConstraint, Hard private HardRouteLevelConstraintManager routeLevelConstraintManager = new HardRouteLevelConstraintManager(); + private SoftActivityConstraintManager softActivityConstraintManager = new SoftActivityConstraintManager(); + + private SoftRouteConstraintManager softRouteConstraintManager = new SoftRouteConstraintManager(); + private VehicleRoutingProblem vrp; private RouteAndActivityStateGetter stateManager; @@ -56,6 +60,14 @@ public class ConstraintManager implements HardActivityStateLevelConstraint, Hard routeLevelConstraintManager.addConstraint((HardRouteStateLevelConstraint) c); constraintTypeKnown = true; } + if(c instanceof SoftRouteConstraint){ + softRouteConstraintManager.addConstraint((SoftRouteConstraint)c); + constraintTypeKnown = true; + } + if(c instanceof SoftActivityConstraint){ + softActivityConstraintManager.addConstraint((SoftActivityConstraint)c); + constraintTypeKnown = true; + } if(!constraintTypeKnown){ log.warn("constraint " + c + " unknown thus ignores the constraint. currently, a constraint must implement either HardActivityStateLevelConstraint or HardRouteStateLevelConstraint"); } @@ -82,6 +94,8 @@ public class ConstraintManager implements HardActivityStateLevelConstraint, Hard } } +// public void add + public void addConstraint(HardActivityStateLevelConstraint actLevelConstraint, Priority priority){ actLevelConstraintManager.addConstraint(actLevelConstraint,priority); } @@ -90,6 +104,14 @@ public class ConstraintManager implements HardActivityStateLevelConstraint, Hard routeLevelConstraintManager.addConstraint(routeLevelConstraint); } + public void addConstraint(SoftActivityConstraint softActivityConstraint){ + softActivityConstraintManager.addConstraint(softActivityConstraint); + } + + public void addConstraint(SoftRouteConstraint softRouteConstraint){ + softRouteConstraintManager.addConstraint(softRouteConstraint); + } + @Override public boolean fulfilled(JobInsertionContext insertionContext) { return routeLevelConstraintManager.fulfilled(insertionContext); @@ -104,7 +126,19 @@ public class ConstraintManager implements HardActivityStateLevelConstraint, Hard List constraints = new ArrayList(); constraints.addAll(actLevelConstraintManager.getAllConstraints()); constraints.addAll(routeLevelConstraintManager.getConstraints()); + constraints.addAll(softActivityConstraintManager.getConstraints()); + constraints.addAll(softRouteConstraintManager.getConstraints()); return Collections.unmodifiableCollection(constraints); } + + @Override + public double getCosts(JobInsertionContext insertionContext) { + return softRouteConstraintManager.getCosts(insertionContext); + } + + @Override + public double getCosts(JobInsertionContext iFacts, TourActivity prevAct,TourActivity newAct, TourActivity nextAct, double prevActDepTime) { + return softActivityConstraintManager.getCosts(iFacts, prevAct, newAct, nextAct, prevActDepTime); + } } \ No newline at end of file diff --git a/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftActivityConstraint.java b/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftActivityConstraint.java new file mode 100644 index 00000000..8817b2ad --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftActivityConstraint.java @@ -0,0 +1,10 @@ +package jsprit.core.problem.constraint; + +import jsprit.core.problem.misc.JobInsertionContext; +import jsprit.core.problem.solution.route.activity.TourActivity; + +public interface SoftActivityConstraint extends SoftConstraint{ + + public double getCosts(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime); + +} diff --git a/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftActivityConstraintManager.java b/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftActivityConstraintManager.java new file mode 100644 index 00000000..bb8a1106 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftActivityConstraintManager.java @@ -0,0 +1,29 @@ +package jsprit.core.problem.constraint; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import jsprit.core.problem.misc.JobInsertionContext; +import jsprit.core.problem.solution.route.activity.TourActivity; + +class SoftActivityConstraintManager implements SoftActivityConstraint{ + + private Collection softConstraints = new ArrayList(); + + public void addConstraint(SoftActivityConstraint constraint){ + softConstraints.add(constraint); + } + + Collection getConstraints(){ return Collections.unmodifiableCollection(softConstraints); } + + @Override + public double getCosts(JobInsertionContext iFacts, TourActivity prevAct,TourActivity newAct, TourActivity nextAct, double prevActDepTime) { + double sumCosts = 0.0; + for(SoftActivityConstraint c : softConstraints){ + sumCosts += c.getCosts(iFacts, prevAct, newAct, nextAct, prevActDepTime); + } + return sumCosts; + } + +} diff --git a/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftRouteConstraint.java b/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftRouteConstraint.java new file mode 100644 index 00000000..1cc1d6e5 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftRouteConstraint.java @@ -0,0 +1,9 @@ +package jsprit.core.problem.constraint; + +import jsprit.core.problem.misc.JobInsertionContext; + +public interface SoftRouteConstraint extends SoftConstraint{ + + public double getCosts(JobInsertionContext insertionContext); + +} diff --git a/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftRouteConstraintManager.java b/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftRouteConstraintManager.java new file mode 100644 index 00000000..311ac857 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/problem/constraint/SoftRouteConstraintManager.java @@ -0,0 +1,28 @@ +package jsprit.core.problem.constraint; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import jsprit.core.problem.misc.JobInsertionContext; + +class SoftRouteConstraintManager implements SoftRouteConstraint{ + + private Collection softConstraints = new ArrayList(); + + public void addConstraint(SoftRouteConstraint constraint){ + softConstraints.add(constraint); + } + + Collection getConstraints(){ return Collections.unmodifiableCollection(softConstraints); } + + @Override + public double getCosts(JobInsertionContext insertionContext) { + double sumCosts = 0.0; + for(SoftRouteConstraint c : softConstraints){ + sumCosts += c.getCosts(insertionContext); + } + return sumCosts; + } + +} diff --git a/jsprit-core/src/test/java/jsprit/core/algorithm/BuildCVRPAlgoFromScratch_IT.java b/jsprit-core/src/test/java/jsprit/core/algorithm/BuildCVRPAlgoFromScratch_IT.java index 9c3a63da..944999c0 100644 --- a/jsprit-core/src/test/java/jsprit/core/algorithm/BuildCVRPAlgoFromScratch_IT.java +++ b/jsprit-core/src/test/java/jsprit/core/algorithm/BuildCVRPAlgoFromScratch_IT.java @@ -32,6 +32,7 @@ import jsprit.core.algorithm.selector.SelectBest; import jsprit.core.algorithm.state.StateManager; import jsprit.core.algorithm.state.UpdateVariableCosts; import jsprit.core.problem.VehicleRoutingProblem; +import jsprit.core.problem.constraint.AdditionalTransportationCosts; import jsprit.core.problem.constraint.ConstraintManager; import jsprit.core.problem.io.VrpXMLReader; import jsprit.core.problem.solution.SolutionCostCalculator; @@ -67,6 +68,7 @@ public class BuildCVRPAlgoFromScratch_IT { ConstraintManager cManager = new ConstraintManager(vrp, stateManager); cManager.addLoadConstraint(); cManager.addTimeWindowConstraint(); + cManager.addConstraint(new AdditionalTransportationCosts(vrp.getTransportCosts())); VehicleFleetManager fleetManager = new InfiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager(); @@ -102,11 +104,7 @@ public class BuildCVRPAlgoFromScratch_IT { vra = new VehicleRoutingAlgorithm(vrp, strategyManager); vra.addListener(stateManager); vra.addListener(new RemoveEmptyVehicles(fleetManager)); - -// vra.getAlgorithmListeners().addListener(stateManager); -// vra.getSearchStrategyManager().addSearchStrategyModuleListener(stateManager); -// vra.getSearchStrategyManager().addSearchStrategyModuleListener(new RemoveEmptyVehicles(fleetManager)); - + VehicleRoutingProblemSolution iniSolution = new InsertionInitialSolutionFactory(bestInsertion, solutionCostCalculator).createSolution(vrp); vra.addInitialSolution(iniSolution); diff --git a/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java b/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java index 20093453..ce3dae97 100644 --- a/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java +++ b/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/ServiceInsertionAndLoadConstraintsTest.java @@ -136,7 +136,7 @@ public class ServiceInsertionAndLoadConstraintsTest { stateManager.informInsertionStarts(Arrays.asList(route), null); JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); - ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityInsertionCostsCalculator, hardRouteLevelConstraint, constraintManager); + ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, constraintManager); ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityInsertionCostsCalculator, hardRouteLevelConstraint, constraintManager); switcher.put(Pickup.class, serviceInsertionCalc); switcher.put(Delivery.class, serviceInsertionCalc); diff --git a/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java b/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java index 0937f9ee..0401f981 100644 --- a/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java +++ b/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/ShipmentInsertionCalculatorTest.java @@ -240,7 +240,7 @@ public class ShipmentInsertionCalculatorTest { stateManager.informInsertionStarts(Arrays.asList(route), null); JobCalculatorSwitcher switcher = new JobCalculatorSwitcher(); - ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, activityInsertionCostsCalculator, hardRouteLevelConstraint, constraintManager); + ServiceInsertionCalculator serviceInsertionCalc = new ServiceInsertionCalculator(routingCosts, constraintManager); ShipmentInsertionCalculator insertionCalculator = new ShipmentInsertionCalculator(routingCosts, activityInsertionCostsCalculator, hardRouteLevelConstraint, constraintManager); switcher.put(Pickup.class, serviceInsertionCalc); switcher.put(Shipment.class, insertionCalculator); diff --git a/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java b/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java index 675bcb98..82bd6c2c 100644 --- a/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java +++ b/jsprit-core/src/test/java/jsprit/core/algorithm/recreate/TestCalculatesServiceInsertion.java @@ -27,6 +27,7 @@ import java.util.Collection; import jsprit.core.algorithm.ExampleActivityCostFunction; import jsprit.core.algorithm.state.StateManager; import jsprit.core.problem.VehicleRoutingProblem; +import jsprit.core.problem.constraint.AdditionalTransportationCosts; import jsprit.core.problem.constraint.ConstraintManager; import jsprit.core.problem.cost.VehicleRoutingTransportCosts; import jsprit.core.problem.driver.DriverImpl; @@ -165,11 +166,9 @@ public class TestCalculatesServiceInsertion { ConstraintManager cManager = new ConstraintManager(vrp,states); cManager.addLoadConstraint(); cManager.addTimeWindowConstraint(); + cManager.addConstraint(new AdditionalTransportationCosts(costs)); - ExampleActivityCostFunction activityCosts = new ExampleActivityCostFunction(); - - - serviceInsertion = new ServiceInsertionCalculator(costs, new LocalActivityInsertionCostsCalculator(costs, activityCosts), cManager, cManager); + serviceInsertion = new ServiceInsertionCalculator(costs, cManager); // stateUpdater = new UpdateStates(states, costs, activityCosts); diff --git a/jsprit-examples/src/main/java/jsprit/examples/BuildAlgorithmFromScratchWithHardAndSoftConstraints.java b/jsprit-examples/src/main/java/jsprit/examples/BuildAlgorithmFromScratchWithHardAndSoftConstraints.java new file mode 100644 index 00000000..64f5aa9d --- /dev/null +++ b/jsprit-examples/src/main/java/jsprit/examples/BuildAlgorithmFromScratchWithHardAndSoftConstraints.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 . + * + * Contributors: + * Stefan Schroeder - initial API and implementation + ******************************************************************************/ +package jsprit.examples; + +import java.util.Collection; + +import jsprit.analysis.toolbox.SolutionPrinter; +import jsprit.core.algorithm.InsertionInitialSolutionFactory; +import jsprit.core.algorithm.RemoveEmptyVehicles; +import jsprit.core.algorithm.SearchStrategy; +import jsprit.core.algorithm.SearchStrategyManager; +import jsprit.core.algorithm.VariablePlusFixedSolutionCostCalculatorFactory; +import jsprit.core.algorithm.VehicleRoutingAlgorithm; +import jsprit.core.algorithm.acceptor.GreedyAcceptance; +import jsprit.core.algorithm.module.RuinAndRecreateModule; +import jsprit.core.algorithm.recreate.BestInsertionBuilder; +import jsprit.core.algorithm.recreate.InsertionStrategy; +import jsprit.core.algorithm.ruin.RadialRuinStrategyFactory; +import jsprit.core.algorithm.ruin.RandomRuinStrategyFactory; +import jsprit.core.algorithm.ruin.RuinStrategy; +import jsprit.core.algorithm.ruin.distance.AvgServiceAndShipmentDistance; +import jsprit.core.algorithm.selector.SelectBest; +import jsprit.core.algorithm.state.StateManager; +import jsprit.core.algorithm.state.UpdateVariableCosts; +import jsprit.core.algorithm.termination.IterationWithoutImprovementTermination; +import jsprit.core.problem.VehicleRoutingProblem; +import jsprit.core.problem.constraint.AdditionalTransportationCosts; +import jsprit.core.problem.constraint.ConstraintManager; +import jsprit.core.problem.solution.SolutionCostCalculator; +import jsprit.core.problem.solution.VehicleRoutingProblemSolution; +import jsprit.core.problem.vehicle.InfiniteFleetManagerFactory; +import jsprit.core.problem.vehicle.VehicleFleetManager; +import jsprit.core.util.Solutions; +import jsprit.instance.reader.SolomonReader; +import jsprit.util.Examples; + +public class BuildAlgorithmFromScratchWithHardAndSoftConstraints { + + /** + * @param args + */ + public static void main(String[] args) { + /* + * some preparation - create output folder + */ + Examples.createOutputFolder(); + + /* + * Build the problem. + * + * But define a problem-builder first. + */ + VehicleRoutingProblem.Builder vrpBuilder = VehicleRoutingProblem.Builder.newInstance(); + + /* + * A solomonReader reads solomon-instance files, and stores the required information in the builder. + */ + new SolomonReader(vrpBuilder).read("input/C101_solomon.txt"); + + /* + * Finally, the problem can be built. By default, transportCosts are crowFlyDistances (as usually used for vrp-instances). + */ + VehicleRoutingProblem vrp = vrpBuilder.build(); + + /* + * Build algorithm + */ + VehicleRoutingAlgorithm vra = buildAlgorithmFromScratch(vrp); + + /* + * search solution + */ + Collection solutions = vra.searchSolutions(); + + /* + * print result + */ + SolutionPrinter.print(Solutions.bestOf(solutions)); + + } + + private static VehicleRoutingAlgorithm buildAlgorithmFromScratch(VehicleRoutingProblem vrp) { + + /* + * manages route and activity states. + */ + StateManager stateManager = new StateManager(vrp); + /* + * tells stateManager to update load states + */ + stateManager.updateLoadStates(); + /* + * tells stateManager to update time-window states + */ + stateManager.updateTimeWindowStates(); + /* + * stateManager.addStateUpdater(updater); + * lets you register your own stateUpdater + */ + + /* + * updates variable costs once a vehicleRoute has changed (by removing or adding a customer) + */ + stateManager.addStateUpdater(new UpdateVariableCosts(vrp.getActivityCosts(), vrp.getTransportCosts(), stateManager)); + + /* + * constructs a constraintManager that manages the various hardConstraints (and soon also softConstraints) + */ + ConstraintManager constraintManager = new ConstraintManager(vrp,stateManager); + /* + * tells constraintManager to add timeWindowConstraints + */ + constraintManager.addTimeWindowConstraint(); + /* + * tells constraintManager to add loadConstraints + */ + constraintManager.addLoadConstraint(); + /* + * add an arbitrary number of hardConstraints by + * constraintManager.addConstraint(...) + */ + constraintManager.addConstraint(new AdditionalTransportationCosts(vrp.getTransportCosts())); + + /* + * define a fleetManager, here infinite vehicles can be used + */ + VehicleFleetManager fleetManager = new InfiniteFleetManagerFactory(vrp.getVehicles()).createFleetManager(); + + /* + * define ruin-and-recreate strategies + * + */ + /* + * first, define an insertion-strategy, i.e. bestInsertion + */ + BestInsertionBuilder iBuilder = new BestInsertionBuilder(vrp, fleetManager, stateManager, constraintManager); + /* + * no need to set further options + */ + InsertionStrategy iStrategy = iBuilder.build(); + + /* + * second, define random-ruin that ruins 50-percent of the selected solution + */ + RuinStrategy randomRuin = new RandomRuinStrategyFactory(0.5).createStrategy(vrp); + + /* + * third, define radial-ruin that ruins 30-percent of the selected solution + * the second para defines the distance between two jobs. + */ + RuinStrategy radialRuin = new RadialRuinStrategyFactory(0.3, new AvgServiceAndShipmentDistance(vrp.getTransportCosts())).createStrategy(vrp); + + /* + * now define a strategy + */ + /* + * but before define how a generated solution is evaluated + * here: the VariablePlusFixed.... comes out of the box and it does what its name suggests + */ + SolutionCostCalculator solutionCostCalculator = new VariablePlusFixedSolutionCostCalculatorFactory(stateManager).createCalculator(); + + SearchStrategy firstStrategy = new SearchStrategy(new SelectBest(), new GreedyAcceptance(1), solutionCostCalculator); + firstStrategy.addModule(new RuinAndRecreateModule("randomRuinAndBestInsertion", iStrategy, randomRuin)); + + SearchStrategy secondStrategy = new SearchStrategy(new SelectBest(), new GreedyAcceptance(1), solutionCostCalculator); + secondStrategy.addModule(new RuinAndRecreateModule("radialRuinAndBestInsertion", iStrategy, radialRuin)); + + /* + * put both strategies together, each with the prob of 0.5 to be selected + */ + SearchStrategyManager searchStrategyManager = new SearchStrategyManager(); + searchStrategyManager.addStrategy(firstStrategy, 0.5); + searchStrategyManager.addStrategy(secondStrategy, 0.5); + + /* + * construct the algorithm + */ + VehicleRoutingAlgorithm vra = new VehicleRoutingAlgorithm(vrp, searchStrategyManager); + //do not forgett to add the stateManager listening to the algorithm-stages + vra.addListener(stateManager); + //remove empty vehicles after insertion has finished + vra.addListener(new RemoveEmptyVehicles(fleetManager)); + + /* + * Do not forget to add an initial solution by vra.addInitialSolution(solution); + * or + */ + vra.addInitialSolution(new InsertionInitialSolutionFactory(iStrategy, solutionCostCalculator).createSolution(vrp)); + + /* + * define the nIterations (by default nIteration=100) + */ + vra.setNuOfIterations(1000); + + /* + * optionally define a premature termination criterion (by default: not criterion is set) + */ + vra.setPrematureAlgorithmTermination(new IterationWithoutImprovementTermination(100)); + + return vra; + } + +}