From ebeae6f693103923e9eb50e55f98ae145cba618a Mon Sep 17 00:00:00 2001 From: oblonski Date: Wed, 8 Jul 2015 18:54:22 +0200 Subject: [PATCH] multiple tws --- .../state/ActivityStartAsSoonAsArrived.java | 14 ++ .../state/ActivityStartStrategy.java | 12 ++ ...vityStartsAsSoonAsNextTimeWindowOpens.java | 31 ++++ ...ActivityStartsAsSoonAsTimeWindowOpens.java | 15 ++ .../state/UpdatePracticalTimeWindows.java | 24 ++- ...eVehicleDependentPracticalTimeWindows.java | 20 ++- ...VehicleDependentTimeWindowConstraints.java | 33 +++- .../route/activity/DeliverService.java | 12 ++ .../route/activity/DeliverShipment.java | 8 + .../problem/solution/route/activity/End.java | 8 + .../route/activity/PickupService.java | 12 ++ .../route/activity/PickupShipment.java | 7 + .../route/activity/ServiceActivity.java | 12 ++ .../solution/route/activity/Start.java | 10 +- .../solution/route/activity/TourActivity.java | 4 + .../jsprit/core/util/ActivityTimeTracker.java | 35 ++-- .../state/GetLatestArrivalTimeTest.java | 157 ++++++++++++++++++ 17 files changed, 384 insertions(+), 30 deletions(-) create mode 100644 jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartAsSoonAsArrived.java create mode 100644 jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartStrategy.java create mode 100644 jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartsAsSoonAsNextTimeWindowOpens.java create mode 100644 jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartsAsSoonAsTimeWindowOpens.java create mode 100644 jsprit-core/src/test/java/jsprit/core/algorithm/state/GetLatestArrivalTimeTest.java diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartAsSoonAsArrived.java b/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartAsSoonAsArrived.java new file mode 100644 index 00000000..1d92a835 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartAsSoonAsArrived.java @@ -0,0 +1,14 @@ +package jsprit.core.algorithm.state; + +import jsprit.core.problem.solution.route.activity.TourActivity; + +/** + * Created by schroeder on 08/07/15. + */ +public class ActivityStartAsSoonAsArrived implements ActivityStartStrategy { + + @Override + public double getActivityStartTime(TourActivity activity, double arrivalTime) { + return arrivalTime; + } +} diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartStrategy.java b/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartStrategy.java new file mode 100644 index 00000000..bd36cde7 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartStrategy.java @@ -0,0 +1,12 @@ +package jsprit.core.algorithm.state; + +import jsprit.core.problem.solution.route.activity.TourActivity; + +/** + * Created by schroeder on 08/07/15. + */ +public interface ActivityStartStrategy { + + public double getActivityStartTime(TourActivity activity, double arrivalTime); + +} diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartsAsSoonAsNextTimeWindowOpens.java b/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartsAsSoonAsNextTimeWindowOpens.java new file mode 100644 index 00000000..b8d626e4 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartsAsSoonAsNextTimeWindowOpens.java @@ -0,0 +1,31 @@ +package jsprit.core.algorithm.state; + +import jsprit.core.problem.solution.route.activity.TimeWindow; +import jsprit.core.problem.solution.route.activity.TourActivity; + +/** + * Created by schroeder on 08/07/15. + */ +public class ActivityStartsAsSoonAsNextTimeWindowOpens implements ActivityStartStrategy { + + @Override + public double getActivityStartTime(TourActivity activity, double arrivalTime) { + boolean next = false; + for(TimeWindow tw : activity.getTimeWindows()){ + if(next){ + return Math.max(tw.getStart(),arrivalTime); + } + if(tw.getStart() <= arrivalTime && tw.getEnd() >= arrivalTime){ + return arrivalTime; + } + else if(tw.getEnd() < arrivalTime){ + next = true; + } + else if(tw.getStart() > arrivalTime){ + return tw.getStart(); + } + } + return arrivalTime; + } + +} diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartsAsSoonAsTimeWindowOpens.java b/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartsAsSoonAsTimeWindowOpens.java new file mode 100644 index 00000000..d3044165 --- /dev/null +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/state/ActivityStartsAsSoonAsTimeWindowOpens.java @@ -0,0 +1,15 @@ +package jsprit.core.algorithm.state; + +import jsprit.core.problem.solution.route.activity.TourActivity; + +/** + * Created by schroeder on 08/07/15. + */ +public class ActivityStartsAsSoonAsTimeWindowOpens implements ActivityStartStrategy { + + @Override + public double getActivityStartTime(TourActivity activity, double arrivalTime) { + return Math.max(activity.getTheoreticalEarliestOperationStartTime(),arrivalTime); + } + +} diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/state/UpdatePracticalTimeWindows.java b/jsprit-core/src/main/java/jsprit/core/algorithm/state/UpdatePracticalTimeWindows.java index e0240c9a..5a3a2d73 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/state/UpdatePracticalTimeWindows.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/state/UpdatePracticalTimeWindows.java @@ -19,8 +19,11 @@ package jsprit.core.algorithm.state; import jsprit.core.problem.cost.VehicleRoutingTransportCosts; import jsprit.core.problem.solution.route.VehicleRoute; import jsprit.core.problem.solution.route.activity.ReverseActivityVisitor; +import jsprit.core.problem.solution.route.activity.TimeWindow; import jsprit.core.problem.solution.route.activity.TourActivity; +import java.util.Collection; + /** * Updates and memorizes latest operation start times at activities. * @@ -55,14 +58,31 @@ class UpdatePracticalTimeWindows implements ReverseActivityVisitor, StateUpdater @Override public void visit(TourActivity activity) { double potentialLatestArrivalTimeAtCurrAct = latestArrTimeAtPrevAct - transportCosts.getBackwardTransportTime(activity.getLocation(), prevAct.getLocation(), latestArrTimeAtPrevAct, route.getDriver(),route.getVehicle()) - activity.getOperationTime(); - double latestArrivalTime = Math.min(activity.getTheoreticalLatestOperationStartTime(), potentialLatestArrivalTimeAtCurrAct); - + Collection timeWindows = activity.getTimeWindows(); + double latestArrivalTime = getLatestArrivalTime(timeWindows,potentialLatestArrivalTimeAtCurrAct); states.putInternalTypedActivityState(activity, InternalStates.LATEST_OPERATION_START_TIME, latestArrivalTime); latestArrTimeAtPrevAct = latestArrivalTime; prevAct = activity; } + private double getLatestArrivalTime(Collection timeWindows, double potentialLatestArrivalTimeAtCurrAct) { + TimeWindow last = null; + for(TimeWindow tw : timeWindows){ + if(tw.getStart() <= potentialLatestArrivalTimeAtCurrAct && tw.getEnd() >= potentialLatestArrivalTimeAtCurrAct){ + return potentialLatestArrivalTimeAtCurrAct; + } + else if(tw.getStart() > potentialLatestArrivalTimeAtCurrAct){ + if(last == null){ + return potentialLatestArrivalTimeAtCurrAct; + } + else return last.getEnd(); + } + last = tw; + } + return last.getEnd(); + } + @Override public void finish() {} } diff --git a/jsprit-core/src/main/java/jsprit/core/algorithm/state/UpdateVehicleDependentPracticalTimeWindows.java b/jsprit-core/src/main/java/jsprit/core/algorithm/state/UpdateVehicleDependentPracticalTimeWindows.java index 7f0afa73..1ea1ca46 100644 --- a/jsprit-core/src/main/java/jsprit/core/algorithm/state/UpdateVehicleDependentPracticalTimeWindows.java +++ b/jsprit-core/src/main/java/jsprit/core/algorithm/state/UpdateVehicleDependentPracticalTimeWindows.java @@ -21,6 +21,7 @@ import jsprit.core.problem.Location; import jsprit.core.problem.cost.VehicleRoutingTransportCosts; import jsprit.core.problem.solution.route.VehicleRoute; import jsprit.core.problem.solution.route.activity.ReverseActivityVisitor; +import jsprit.core.problem.solution.route.activity.TimeWindow; import jsprit.core.problem.solution.route.activity.TourActivity; import jsprit.core.problem.vehicle.Vehicle; @@ -85,7 +86,7 @@ public class UpdateVehicleDependentPracticalTimeWindows implements ReverseActivi Location prevLocation = location_of_prevAct[vehicle.getVehicleTypeIdentifier().getIndex()]; double potentialLatestArrivalTimeAtCurrAct = latestArrTimeAtPrevAct - transportCosts.getBackwardTransportTime(activity.getLocation(), prevLocation, latestArrTimeAtPrevAct, route.getDriver(), vehicle) - activity.getOperationTime(); - double latestArrivalTime = Math.min(activity.getTheoreticalLatestOperationStartTime(), potentialLatestArrivalTimeAtCurrAct); + double latestArrivalTime = getLatestArrivalTime(activity.getTimeWindows(),potentialLatestArrivalTimeAtCurrAct); stateManager.putInternalTypedActivityState(activity, vehicle, InternalStates.LATEST_OPERATION_START_TIME, latestArrivalTime); latest_arrTimes_at_prevAct[vehicle.getVehicleTypeIdentifier().getIndex()] = latestArrivalTime; location_of_prevAct[vehicle.getVehicleTypeIdentifier().getIndex()] = activity.getLocation(); @@ -95,5 +96,22 @@ public class UpdateVehicleDependentPracticalTimeWindows implements ReverseActivi @Override public void finish() {} + private double getLatestArrivalTime(Collection timeWindows, double potentialLatestArrivalTimeAtCurrAct) { + TimeWindow last = null; + for(TimeWindow tw : timeWindows){ + if(tw.getStart() <= potentialLatestArrivalTimeAtCurrAct && tw.getEnd() >= potentialLatestArrivalTimeAtCurrAct){ + return potentialLatestArrivalTimeAtCurrAct; + } + else if(tw.getStart() > potentialLatestArrivalTimeAtCurrAct){ + if(last == null){ + return potentialLatestArrivalTimeAtCurrAct; + } + else return last.getEnd(); + } + last = tw; + } + return last.getEnd(); + } + } diff --git a/jsprit-core/src/main/java/jsprit/core/problem/constraint/VehicleDependentTimeWindowConstraints.java b/jsprit-core/src/main/java/jsprit/core/problem/constraint/VehicleDependentTimeWindowConstraints.java index b22c44c6..f43c619c 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/constraint/VehicleDependentTimeWindowConstraints.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/constraint/VehicleDependentTimeWindowConstraints.java @@ -45,11 +45,21 @@ public class VehicleDependentTimeWindowConstraints implements HardActivityConstr this.routingCosts = routingCosts; } + public double getLatestOperationStartTime(TourActivity act){ + return act.getTimeWindows().get(act.getTimeWindows().size()-1).getEnd(); + } + + public double getEarliestOperationStartTime(TourActivity act){ + return act.getTimeWindows().get(0).getStart(); + } + @Override public ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) { double latestVehicleArrival = iFacts.getNewVehicle().getLatestArrival(); Double latestArrTimeAtNextAct; Location nextActLocation; + double nextAct_theoreticalLatestOperationStartTime = getLatestOperationStartTime(nextAct); + if(nextAct instanceof End) { latestArrTimeAtNextAct = latestVehicleArrival; nextActLocation = iFacts.getNewVehicle().getEndLocation(); @@ -63,7 +73,7 @@ public class VehicleDependentTimeWindowConstraints implements HardActivityConstr // if(latestArrTimeAtNextAct == null) //try to get latest_operation_start_time of currVehicle // latestArrTimeAtNextAct = states.getActivityState(nextAct, iFacts.getRoute().getVehicle(), StateFactory.LATEST_OPERATION_START_TIME ,Double.class); if(latestArrTimeAtNextAct == null) {//otherwise set it to theoretical_latest_operation_startTime - latestArrTimeAtNextAct = nextAct.getTheoreticalLatestOperationStartTime(); + latestArrTimeAtNextAct = nextAct_theoreticalLatestOperationStartTime; // throw new IllegalStateException("this is strange and should not be"); //ToDo here, there should be another solution } @@ -77,9 +87,15 @@ public class VehicleDependentTimeWindowConstraints implements HardActivityConstr * |--- vehicle's operation time ---| * |--- prevAct or newAct or nextAct ---| */ - if(latestVehicleArrival < prevAct.getTheoreticalEarliestOperationStartTime() || - latestVehicleArrival < newAct.getTheoreticalEarliestOperationStartTime() || - latestVehicleArrival < nextAct.getTheoreticalEarliestOperationStartTime()){ + double prevAct_theoreticalEarliestOperationStartTime = getEarliestOperationStartTime(prevAct); + double newAct_theoreticalEarliestOperationStartTime = getEarliestOperationStartTime(newAct); +// newAct.getTheoreticalEarliestOperationStartTime(); + double nextAct_theoreticalEarliestOperationStartTime = getEarliestOperationStartTime(nextAct); +// nextAct.getTheoreticalEarliestOperationStartTime(); + + if(latestVehicleArrival < prevAct_theoreticalEarliestOperationStartTime || + latestVehicleArrival < newAct_theoreticalEarliestOperationStartTime || + latestVehicleArrival < nextAct_theoreticalEarliestOperationStartTime){ return ConstraintsStatus.NOT_FULFILLED_BREAK; } /* @@ -89,7 +105,10 @@ public class VehicleDependentTimeWindowConstraints implements HardActivityConstr * |--- prevAct ---| * |--- newAct ---| */ - if(newAct.getTheoreticalLatestOperationStartTime() < prevAct.getTheoreticalEarliestOperationStartTime()){ + double newAct_theoreticalLatestOperationStartTime = getLatestOperationStartTime(newAct); +// newAct.getTheoreticalLatestOperationStartTime(); + + if(newAct_theoreticalLatestOperationStartTime < prevAct_theoreticalEarliestOperationStartTime){ return ConstraintsStatus.NOT_FULFILLED_BREAK; } @@ -107,14 +126,14 @@ public class VehicleDependentTimeWindowConstraints implements HardActivityConstr * |--- newAct ---| * |--- nextAct ---| */ - if(newAct.getTheoreticalEarliestOperationStartTime() > nextAct.getTheoreticalLatestOperationStartTime()){ + if(newAct_theoreticalEarliestOperationStartTime > nextAct_theoreticalLatestOperationStartTime){ return ConstraintsStatus.NOT_FULFILLED; } // log.info("check insertion of " + newAct + " between " + prevAct + " and " + nextAct + ". prevActDepTime=" + prevActDepTime); double arrTimeAtNewAct = prevActDepTime + routingCosts.getTransportTime(prevAct.getLocation(), newAct.getLocation(), prevActDepTime, iFacts.getNewDriver(), iFacts.getNewVehicle()); double endTimeAtNewAct = CalculationUtils.getActivityEndTime(arrTimeAtNewAct, newAct); double latestArrTimeAtNewAct = - Math.min(newAct.getTheoreticalLatestOperationStartTime(), + Math.min(newAct_theoreticalLatestOperationStartTime, latestArrTimeAtNextAct - routingCosts.getBackwardTransportTime(newAct.getLocation(),nextActLocation,latestArrTimeAtNextAct,iFacts.getNewDriver(),iFacts.getNewVehicle()) - newAct.getOperationTime() diff --git a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/DeliverService.java b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/DeliverService.java index f4ccb563..295a61d9 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/DeliverService.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/DeliverService.java @@ -21,6 +21,9 @@ import jsprit.core.problem.Capacity; import jsprit.core.problem.Location; import jsprit.core.problem.job.Delivery; +import java.util.ArrayList; +import java.util.List; + public final class DeliverService extends AbstractActivity implements DeliveryActivity{ private Delivery delivery; @@ -34,11 +37,14 @@ public final class DeliverService extends AbstractActivity implements DeliveryAc private double theoreticalEarliest; private double theoreticalLatest; + + private List timeWindows; public DeliverService(Delivery delivery) { super(); this.delivery = delivery; capacity = Capacity.invert(delivery.getSize()); + timeWindows = new ArrayList(delivery.getTimeWindows(0.)); } private DeliverService(DeliverService deliveryActivity){ @@ -49,6 +55,7 @@ public final class DeliverService extends AbstractActivity implements DeliveryAc this.theoreticalLatest = deliveryActivity.getTheoreticalLatestOperationStartTime(); capacity = deliveryActivity.getSize(); setIndex(deliveryActivity.getIndex()); + timeWindows = new ArrayList(delivery.getTimeWindows(0.)); } @Override @@ -61,6 +68,11 @@ public final class DeliverService extends AbstractActivity implements DeliveryAc theoreticalLatest = latest; } + @Override + public List getTimeWindows() { + return timeWindows; + } + @Override public String getName() { return delivery.getType(); diff --git a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/DeliverShipment.java b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/DeliverShipment.java index 70df55d6..5bd67dbd 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/DeliverShipment.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/DeliverShipment.java @@ -22,6 +22,8 @@ import jsprit.core.problem.Location; import jsprit.core.problem.job.Job; import jsprit.core.problem.job.Shipment; +import java.util.List; + public final class DeliverShipment extends AbstractActivity implements DeliveryActivity{ private Shipment shipment; @@ -62,6 +64,12 @@ public final class DeliverShipment extends AbstractActivity implements DeliveryA } + @Override + public List getTimeWindows() { +// return shipment.getDeliveryTimeWindow(); + return null; + } + @Override public String getName() { return "deliverShipment"; diff --git a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/End.java b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/End.java index ee4325d9..80637322 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/End.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/End.java @@ -21,6 +21,9 @@ import jsprit.core.problem.Capacity; import jsprit.core.problem.Location; import jsprit.core.util.Coordinate; +import java.util.Arrays; +import java.util.List; + public final class End extends AbstractActivity implements TourActivity { @Deprecated @@ -68,6 +71,11 @@ public final class End extends AbstractActivity implements TourActivity { theoretical_latestOperationStartTime = theoreticalLatestOperationStartTime; } + @Override + public List getTimeWindows() { + return Arrays.asList(TimeWindow.newInstance(theoretical_earliestOperationStartTime,theoretical_latestOperationStartTime)); + } + public End(Location location, double theoreticalStart, double theoreticalEnd) { super(); this.location = location; diff --git a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/PickupService.java b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/PickupService.java index 4ea58546..6b9698a3 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/PickupService.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/PickupService.java @@ -22,6 +22,9 @@ import jsprit.core.problem.Location; import jsprit.core.problem.job.Pickup; import jsprit.core.problem.job.Service; +import java.util.ArrayList; +import java.util.List; + public final class PickupService extends AbstractActivity implements PickupActivity{ private Service pickup; @@ -33,10 +36,13 @@ public final class PickupService extends AbstractActivity implements PickupActiv private double theoreticalEarliest; private double theoreticalLatest; + + private List timeWindows; public PickupService(Pickup pickup) { super(); this.pickup = pickup; + timeWindows = new ArrayList(pickup.getTimeWindows(0.)); } public PickupService(Service service){ @@ -50,6 +56,7 @@ public final class PickupService extends AbstractActivity implements PickupActiv this.theoreticalEarliest = pickupActivity.getTheoreticalEarliestOperationStartTime(); this.theoreticalLatest = pickupActivity.getTheoreticalLatestOperationStartTime(); setIndex(pickupActivity.getIndex()); + timeWindows = new ArrayList(pickup.getTimeWindows(0.)); } @Override @@ -62,6 +69,11 @@ public final class PickupService extends AbstractActivity implements PickupActiv this.theoreticalLatest = latest; } + @Override + public List getTimeWindows() { + return timeWindows; + } + @Override public String getName() { return pickup.getType(); diff --git a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/PickupShipment.java b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/PickupShipment.java index 8d667c8e..c150abc6 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/PickupShipment.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/PickupShipment.java @@ -22,6 +22,8 @@ import jsprit.core.problem.Location; import jsprit.core.problem.job.Job; import jsprit.core.problem.job.Shipment; +import java.util.List; + public final class PickupShipment extends AbstractActivity implements PickupActivity{ private Shipment shipment; @@ -58,6 +60,11 @@ public final class PickupShipment extends AbstractActivity implements PickupActi } + @Override + public List getTimeWindows() { + return null; + } + @Override public String getName() { return "pickupShipment"; diff --git a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/ServiceActivity.java b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/ServiceActivity.java index ee4b7920..7cb405bd 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/ServiceActivity.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/ServiceActivity.java @@ -22,6 +22,9 @@ import jsprit.core.problem.Location; import jsprit.core.problem.job.Service; import jsprit.core.problem.solution.route.activity.TourActivity.JobActivity; +import java.util.ArrayList; +import java.util.List; + public class ServiceActivity extends AbstractActivity implements JobActivity{ @Deprecated @@ -72,10 +75,13 @@ public class ServiceActivity extends AbstractActivity implements JobActivity{ private final Service service; + + private List timeWindows; protected ServiceActivity(Service service) { counter++; this.service = service; + timeWindows = new ArrayList(service.getTimeWindows(0.)); } protected ServiceActivity(ServiceActivity serviceActivity) { @@ -84,6 +90,7 @@ public class ServiceActivity extends AbstractActivity implements JobActivity{ this.arrTime = serviceActivity.getArrTime(); this.endTime = serviceActivity.getEndTime(); setIndex(serviceActivity.getIndex()); + timeWindows = new ArrayList(serviceActivity.getTimeWindows()); } @@ -166,6 +173,11 @@ public class ServiceActivity extends AbstractActivity implements JobActivity{ theoreticalLatest = latest; } + @Override + public List getTimeWindows() { + return timeWindows; + } + @Override public String getName() { return service.getType(); diff --git a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/Start.java b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/Start.java index cf0d6cba..000f423f 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/Start.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/Start.java @@ -20,6 +20,9 @@ import jsprit.core.problem.AbstractActivity; import jsprit.core.problem.Capacity; import jsprit.core.problem.Location; +import java.util.Arrays; +import java.util.List; + public final class Start extends AbstractActivity implements TourActivity { public final static String ACTIVITY_NAME = "start"; @@ -103,7 +106,12 @@ public final class Start extends AbstractActivity implements TourActivity { this.theoretical_latestOperationStartTime=time; } - @Deprecated + @Override + public List getTimeWindows() { + return Arrays.asList(TimeWindow.newInstance(theoretical_earliestOperationStartTime,theoretical_latestOperationStartTime)); + } + + @Deprecated @Override public String getLocationId() { if(location == null) return null; diff --git a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/TourActivity.java b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/TourActivity.java index 8800f9d8..ac93f96e 100644 --- a/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/TourActivity.java +++ b/jsprit-core/src/main/java/jsprit/core/problem/solution/route/activity/TourActivity.java @@ -21,6 +21,8 @@ import jsprit.core.problem.HasIndex; import jsprit.core.problem.Location; import jsprit.core.problem.job.Job; +import java.util.List; + /** * Basic interface for tour-activities. * @@ -35,6 +37,8 @@ public interface TourActivity extends HasIndex { public void setTheoreticalLatestOperationStartTime(double latest); + public List getTimeWindows(); + /** * Basic interface of job-activies. * diff --git a/jsprit-core/src/main/java/jsprit/core/util/ActivityTimeTracker.java b/jsprit-core/src/main/java/jsprit/core/util/ActivityTimeTracker.java index fe4f819b..2ad2337a 100644 --- a/jsprit-core/src/main/java/jsprit/core/util/ActivityTimeTracker.java +++ b/jsprit-core/src/main/java/jsprit/core/util/ActivityTimeTracker.java @@ -16,6 +16,9 @@ ******************************************************************************/ package jsprit.core.util; +import jsprit.core.algorithm.state.ActivityStartAsSoonAsArrived; +import jsprit.core.algorithm.state.ActivityStartStrategy; +import jsprit.core.algorithm.state.ActivityStartsAsSoonAsTimeWindowOpens; import jsprit.core.problem.cost.ForwardTransportTime; import jsprit.core.problem.solution.route.VehicleRoute; import jsprit.core.problem.solution.route.activity.ActivityVisitor; @@ -43,19 +46,29 @@ public class ActivityTimeTracker implements ActivityVisitor{ private double actEndTime; - private ActivityPolicy activityPolicy = ActivityPolicy.AS_SOON_AS_TIME_WINDOW_OPENS; + private ActivityStartStrategy startStrategy; public ActivityTimeTracker(ForwardTransportTime transportTime) { super(); this.transportTime = transportTime; + this.startStrategy = new ActivityStartsAsSoonAsTimeWindowOpens(); } public ActivityTimeTracker(ForwardTransportTime transportTime, ActivityPolicy activityPolicy) { super(); this.transportTime = transportTime; - this.activityPolicy = activityPolicy; + if(activityPolicy.equals(ActivityPolicy.AS_SOON_AS_ARRIVED)){ + this.startStrategy = new ActivityStartsAsSoonAsTimeWindowOpens(); + } + else this.startStrategy = new ActivityStartAsSoonAsArrived(); } + public ActivityTimeTracker(ForwardTransportTime transportTime, ActivityStartStrategy startStrategy) { + super(); + this.transportTime = transportTime; + this.startStrategy = startStrategy; + } + public double getActArrTime(){ return actArrTime; } @@ -78,35 +91,19 @@ public class ActivityTimeTracker implements ActivityVisitor{ if(!beginFirst) throw new IllegalStateException("never called begin. this however is essential here"); double transportTime = this.transportTime.getTransportTime(prevAct.getLocation(), activity.getLocation(), startAtPrevAct, route.getDriver(), route.getVehicle()); double arrivalTimeAtCurrAct = startAtPrevAct + transportTime; - actArrTime = arrivalTimeAtCurrAct; - double operationStartTime; - - if(activityPolicy.equals(ActivityPolicy.AS_SOON_AS_TIME_WINDOW_OPENS)){ - operationStartTime = Math.max(activity.getTheoreticalEarliestOperationStartTime(), arrivalTimeAtCurrAct); - } - else if(activityPolicy.equals(ActivityPolicy.AS_SOON_AS_ARRIVED)){ - operationStartTime = actArrTime; - } - else operationStartTime = actArrTime; - - double operationEndTime = operationStartTime + activity.getOperationTime(); - + double operationEndTime = startStrategy.getActivityStartTime(activity,arrivalTimeAtCurrAct) + activity.getOperationTime(); actEndTime = operationEndTime; - prevAct = activity; startAtPrevAct = operationEndTime; - } @Override public void finish() { double transportTime = this.transportTime.getTransportTime(prevAct.getLocation(), route.getEnd().getLocation(), startAtPrevAct, route.getDriver(), route.getVehicle()); double arrivalTimeAtCurrAct = startAtPrevAct + transportTime; - actArrTime = arrivalTimeAtCurrAct; actEndTime = arrivalTimeAtCurrAct; - beginFirst = false; } diff --git a/jsprit-core/src/test/java/jsprit/core/algorithm/state/GetLatestArrivalTimeTest.java b/jsprit-core/src/test/java/jsprit/core/algorithm/state/GetLatestArrivalTimeTest.java new file mode 100644 index 00000000..a3b3379a --- /dev/null +++ b/jsprit-core/src/test/java/jsprit/core/algorithm/state/GetLatestArrivalTimeTest.java @@ -0,0 +1,157 @@ +package jsprit.core.algorithm.state; + +import jsprit.core.problem.solution.route.activity.TimeWindow; +import junit.framework.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collection; + +/** + * Created by schroeder on 08/07/15. + */ +public class GetLatestArrivalTimeTest { + + @Test + public void whenSingleTW_itShouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(0,10); + Assert.assertEquals(10.,getLatestArrivalTime(Arrays.asList(tw),20)); + } + + @Test + public void whenSingleTW2_itShouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(0,10); + Assert.assertEquals(8.,getLatestArrivalTime(Arrays.asList(tw),8)); + } + + @Test + public void whenSingleTW3_itShouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + Assert.assertEquals(1.,getLatestArrivalTime(Arrays.asList(tw),1)); + } + + @Test + public void whenMultipleTW_itShouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(30.,getLatestArrivalTime(Arrays.asList(tw,tw2),40)); + } + + @Test + public void whenMultipleTW2_itShouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(25.,getLatestArrivalTime(Arrays.asList(tw,tw2),25)); + } + + @Test + public void whenMultipleTW3_itShouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(10.,getLatestArrivalTime(Arrays.asList(tw,tw2),19)); + } + + @Test + public void whenMultipleTW4_itShouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(8.,getLatestArrivalTime(Arrays.asList(tw,tw2),8)); + } + + @Test + public void whenMultipleTW5_itShouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(1.,getLatestArrivalTime(Arrays.asList(tw,tw2),1)); + } + + @Test + public void whenSingleTW_ActivityStartTime_shouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + Assert.assertEquals(2.,getActivityStartTime(Arrays.asList(tw),1)); + } + + @Test + public void whenSingleTW2_ActivityStartTime_shouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + Assert.assertEquals(3.,getActivityStartTime(Arrays.asList(tw),3)); + } + + @Test + public void whenSingleTW3_ActivityStartTime_shouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + Assert.assertEquals(11.,getActivityStartTime(Arrays.asList(tw),11)); + } + + @Test + public void whenMultipleTW1_ActivityStartTime_shouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(2.,getActivityStartTime(Arrays.asList(tw,tw2),1)); + } + + @Test + public void whenMultipleTW2_ActivityStartTime_shouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(3.,getActivityStartTime(Arrays.asList(tw,tw2),3)); + } + + @Test + public void whenMultipleTW3_ActivityStartTime_shouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(20.,getActivityStartTime(Arrays.asList(tw,tw2),11)); + } + + @Test + public void whenMultipleTW4_ActivityStartTime_shouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(21.,getActivityStartTime(Arrays.asList(tw,tw2),21)); + } + + @Test + public void whenMultipleTW5_ActivityStartTime_shouldReturnCorrectTime(){ + TimeWindow tw = TimeWindow.newInstance(2,10); + TimeWindow tw2 = TimeWindow.newInstance(20,30); + Assert.assertEquals(31.,getActivityStartTime(Arrays.asList(tw,tw2),31)); + } + + private double getLatestArrivalTime(Collection timeWindows, double potentialLatestArrivalTimeAtCurrAct) { + TimeWindow last = null; + for(TimeWindow tw : timeWindows){ + if(tw.getStart() <= potentialLatestArrivalTimeAtCurrAct && tw.getEnd() >= potentialLatestArrivalTimeAtCurrAct){ + return potentialLatestArrivalTimeAtCurrAct; + } + else if(tw.getStart() > potentialLatestArrivalTimeAtCurrAct){ + if(last == null){ + return potentialLatestArrivalTimeAtCurrAct; + } + else return last.getEnd(); + } + last = tw; + } + return last.getEnd(); + } + + private double getActivityStartTime(Collection timeWindows, double arrivalTime) { + boolean next = false; + for(TimeWindow tw : timeWindows){ + if(next){ + return Math.max(tw.getStart(),arrivalTime); + } + if(tw.getStart() <= arrivalTime && tw.getEnd() >= arrivalTime){ + return arrivalTime; + } + else if(tw.getEnd() < arrivalTime){ + next = true; + } + else if(tw.getStart() > arrivalTime){ + return tw.getStart(); + } + } + return arrivalTime; + } + +}