mirror of
https://github.com/graphhopper/jsprit.git
synced 2020-01-24 07:45:05 +01:00
add multiple time windows to services
This commit is contained in:
parent
954f2588a4
commit
7582c5e038
13 changed files with 278 additions and 51 deletions
|
|
@ -35,6 +35,7 @@ import org.apache.logging.log4j.LogManager;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Calculator that calculates the best insertion position for a {@link Service}.
|
||||
|
|
@ -123,14 +124,21 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator{
|
|||
TourActivity prevAct = start;
|
||||
double prevActStartTime = newVehicleDepartureTime;
|
||||
int actIndex = 0;
|
||||
boolean loopBroken = false;
|
||||
for(TourActivity nextAct : currentRoute.getTourActivities().getActivities()){
|
||||
Iterator<TourActivity> activityIterator = currentRoute.getActivities().iterator();
|
||||
boolean tourEnd = false;
|
||||
while(!tourEnd){
|
||||
TourActivity nextAct;
|
||||
if(activityIterator.hasNext()) nextAct = activityIterator.next();
|
||||
else{
|
||||
nextAct = end;
|
||||
tourEnd = true;
|
||||
}
|
||||
double actArrTime = prevActStartTime + transportCosts.getTransportTime(prevAct.getLocation(),deliveryAct2Insert.getLocation(),prevActStartTime,newDriver,newVehicle);
|
||||
Collection<TimeWindow> timeWindows = service.getTimeWindows(actArrTime);
|
||||
boolean not_fulfilled_break = true;
|
||||
for(TimeWindow timeWindow : timeWindows) {
|
||||
deliveryAct2Insert.setTheoreticalEarliestStart(timeWindow.getStart());
|
||||
deliveryAct2Insert.setTheoreticalLatestStart(timeWindow.getEnd());
|
||||
deliveryAct2Insert.setTheoreticalEarliestOperationStartTime(timeWindow.getStart());
|
||||
deliveryAct2Insert.setTheoreticalLatestOperationStartTime(timeWindow.getEnd());
|
||||
ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, nextAct, prevActStartTime);
|
||||
if (status.equals(ConstraintsStatus.FULFILLED)) {
|
||||
//from job2insert induced costs at activity level
|
||||
|
|
@ -146,34 +154,18 @@ final class ServiceInsertionCalculator implements JobInsertionCostsCalculator{
|
|||
not_fulfilled_break = false;
|
||||
}
|
||||
}
|
||||
if(not_fulfilled_break){
|
||||
loopBroken = true;
|
||||
break;
|
||||
}
|
||||
if(not_fulfilled_break) break;
|
||||
double nextActArrTime = prevActStartTime + transportCosts.getTransportTime(prevAct.getLocation(), nextAct.getLocation(), prevActStartTime, newDriver, newVehicle);
|
||||
prevActStartTime = CalculationUtils.getActivityEndTime(nextActArrTime, nextAct);
|
||||
prevAct = nextAct;
|
||||
actIndex++;
|
||||
}
|
||||
if(!loopBroken){
|
||||
Collection<TimeWindow> timeWindows = service.getTimeWindows(actArrTime);
|
||||
for(TimeWindow timeWindow : timeWindows) {
|
||||
|
||||
}
|
||||
ConstraintsStatus status = hardActivityLevelConstraint.fulfilled(insertionContext, prevAct, deliveryAct2Insert, end, prevActStartTime);
|
||||
if(status.equals(ConstraintsStatus.FULFILLED)){
|
||||
double additionalICostsAtActLevel = softActivityConstraint.getCosts(insertionContext, prevAct, deliveryAct2Insert, end, prevActStartTime);
|
||||
double additionalTransportationCosts = additionalTransportCostsCalculator.getCosts(insertionContext, prevAct, end, deliveryAct2Insert, prevActStartTime);
|
||||
if(additionalICostsAtRouteLevel + additionalICostsAtActLevel + additionalTransportationCosts < bestCost){
|
||||
bestCost = additionalICostsAtRouteLevel + additionalICostsAtActLevel + additionalTransportationCosts;
|
||||
insertionIndex = actIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(insertionIndex == InsertionData.NO_INDEX) {
|
||||
return InsertionData.createEmptyInsertionData();
|
||||
}
|
||||
InsertionData insertionData = new InsertionData(bestCost, InsertionData.NO_INDEX, insertionIndex, newVehicle, newDriver);
|
||||
deliveryAct2Insert.setTheoreticalEarliestOperationStartTime(bestTimeWindow.getStart());
|
||||
deliveryAct2Insert.setTheoreticalLatestOperationStartTime(bestTimeWindow.getEnd());
|
||||
insertionData.getEvents().add(new InsertActivity(currentRoute,newVehicle,deliveryAct2Insert,insertionIndex));
|
||||
insertionData.getEvents().add(new SwitchVehicle(currentRoute,newVehicle,newVehicleDepartureTime));
|
||||
insertionData.setVehicleDepartureTime(newVehicleDepartureTime);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import jsprit.core.problem.Location;
|
|||
import jsprit.core.problem.Skills;
|
||||
import jsprit.core.problem.solution.route.activity.TimeWindow;
|
||||
import jsprit.core.problem.solution.route.activity.TimeWindows;
|
||||
import jsprit.core.problem.solution.route.activity.TimeWindowsImpl;
|
||||
import jsprit.core.util.Coordinate;
|
||||
|
||||
import java.util.Collection;
|
||||
|
|
@ -86,10 +87,14 @@ public class Service extends AbstractJob {
|
|||
|
||||
protected Location location;
|
||||
|
||||
protected TimeWindows timeWindows;
|
||||
protected TimeWindows timeWindows = new TimeWindowsImpl();
|
||||
|
||||
private boolean twAdded = false;
|
||||
|
||||
Builder(String id){
|
||||
this.id = id;
|
||||
timeWindows = new TimeWindowsImpl();
|
||||
((TimeWindowsImpl)timeWindows).add(timeWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -163,9 +168,33 @@ public class Service extends AbstractJob {
|
|||
public Builder setTimeWindow(TimeWindow tw){
|
||||
if(tw == null) throw new IllegalArgumentException("time-window arg must not be null");
|
||||
this.timeWindow = tw;
|
||||
this.timeWindows = new TimeWindowsImpl();
|
||||
((TimeWindowsImpl) timeWindows).add(tw);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addTimeWindow(TimeWindow timeWindow) {
|
||||
if(timeWindow == null) throw new IllegalArgumentException("time-window arg must not be null");
|
||||
if(!twAdded){
|
||||
timeWindows = new TimeWindowsImpl();
|
||||
twAdded = true;
|
||||
}
|
||||
for(TimeWindow tw : ((TimeWindowsImpl)timeWindows).getTimeWindows()){
|
||||
if(timeWindow.getStart() > tw.getStart() && timeWindow.getStart() < tw.getEnd()){
|
||||
throw new IllegalStateException("time-windows cannot overlap each other. overlap: " + tw + ", " + timeWindow);
|
||||
}
|
||||
if(timeWindow.getEnd() > tw.getStart() && timeWindow.getEnd() < tw.getEnd()){
|
||||
throw new IllegalStateException("time-windows cannot overlap each other. overlap: " + tw + ", " + timeWindow);
|
||||
}
|
||||
}
|
||||
((TimeWindowsImpl) timeWindows).add(timeWindow);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addTimeWindow(double earliest, double latest) {
|
||||
return addTimeWindow(TimeWindow.newInstance(earliest, latest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the service.
|
||||
*
|
||||
|
|
@ -189,7 +218,10 @@ public class Service extends AbstractJob {
|
|||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private final String id;
|
||||
|
|
@ -222,8 +254,8 @@ public class Service extends AbstractJob {
|
|||
timeWindowManager = builder.timeWindows;
|
||||
}
|
||||
|
||||
public Collection<TimeWindow> getTimeWindows(double time){
|
||||
return timeWindowManager.getTimeWindows(time);
|
||||
public Collection<TimeWindow> getTimeWindows(double arrTime){
|
||||
return timeWindowManager.getTimeWindows(arrTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -31,6 +31,10 @@ public final class DeliverService extends AbstractActivity implements DeliveryAc
|
|||
|
||||
private double endTime;
|
||||
|
||||
private double theoreticalEarliest;
|
||||
|
||||
private double theoreticalLatest;
|
||||
|
||||
public DeliverService(Delivery delivery) {
|
||||
super();
|
||||
this.delivery = delivery;
|
||||
|
|
@ -41,10 +45,22 @@ public final class DeliverService extends AbstractActivity implements DeliveryAc
|
|||
this.delivery=deliveryActivity.getJob();
|
||||
this.arrTime=deliveryActivity.getArrTime();
|
||||
this.endTime=deliveryActivity.getEndTime();
|
||||
this.theoreticalEarliest = deliveryActivity.getTheoreticalEarliestOperationStartTime();
|
||||
this.theoreticalLatest = deliveryActivity.getTheoreticalLatestOperationStartTime();
|
||||
capacity = deliveryActivity.getSize();
|
||||
setIndex(deliveryActivity.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalEarliestOperationStartTime(double earliest) {
|
||||
theoreticalEarliest = earliest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalLatestOperationStartTime(double latest) {
|
||||
theoreticalLatest = latest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return delivery.getType();
|
||||
|
|
@ -62,12 +78,12 @@ public final class DeliverService extends AbstractActivity implements DeliveryAc
|
|||
|
||||
@Override
|
||||
public double getTheoreticalEarliestOperationStartTime() {
|
||||
return delivery.getTimeWindow().getStart();
|
||||
return theoreticalEarliest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTheoreticalLatestOperationStartTime() {
|
||||
return delivery.getTimeWindow().getEnd();
|
||||
return theoreticalLatest;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -52,6 +52,16 @@ public final class DeliverShipment extends AbstractActivity implements DeliveryA
|
|||
return shipment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalEarliestOperationStartTime(double earliest) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalLatestOperationStartTime(double latest) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "deliverShipment";
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ public final class End extends AbstractActivity implements TourActivity {
|
|||
+ "][twEnd=" + Activities.round(theoretical_latestOperationStartTime) + "]";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "end";
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ public final class PickupService extends AbstractActivity implements PickupActiv
|
|||
|
||||
private double depTime;
|
||||
|
||||
private double theoreticalEarliest;
|
||||
|
||||
private double theoreticalLatest;
|
||||
|
||||
public PickupService(Pickup pickup) {
|
||||
super();
|
||||
this.pickup = pickup;
|
||||
|
|
@ -43,9 +47,21 @@ public final class PickupService extends AbstractActivity implements PickupActiv
|
|||
this.pickup=pickupActivity.getJob();
|
||||
this.arrTime=pickupActivity.getArrTime();
|
||||
this.depTime=pickupActivity.getEndTime();
|
||||
this.theoreticalEarliest = pickupActivity.getTheoreticalEarliestOperationStartTime();
|
||||
this.theoreticalLatest = pickupActivity.getTheoreticalLatestOperationStartTime();
|
||||
setIndex(pickupActivity.getIndex());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalEarliestOperationStartTime(double earliest) {
|
||||
this.theoreticalEarliest = earliest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalLatestOperationStartTime(double latest) {
|
||||
this.theoreticalLatest = latest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return pickup.getType();
|
||||
|
|
@ -63,12 +79,12 @@ public final class PickupService extends AbstractActivity implements PickupActiv
|
|||
|
||||
@Override
|
||||
public double getTheoreticalEarliestOperationStartTime() {
|
||||
return pickup.getTimeWindow().getStart();
|
||||
return theoreticalEarliest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTheoreticalLatestOperationStartTime() {
|
||||
return pickup.getTimeWindow().getEnd();
|
||||
return theoreticalLatest;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -48,6 +48,16 @@ public final class PickupShipment extends AbstractActivity implements PickupActi
|
|||
return shipment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalEarliestOperationStartTime(double earliest) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalLatestOperationStartTime(double latest) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "pickupShipment";
|
||||
|
|
|
|||
|
|
@ -24,12 +24,16 @@ import jsprit.core.problem.solution.route.activity.TourActivity.JobActivity;
|
|||
|
||||
public class ServiceActivity extends AbstractActivity implements JobActivity{
|
||||
|
||||
@Deprecated
|
||||
public static int counter = 0;
|
||||
|
||||
public double arrTime;
|
||||
|
||||
public double endTime;
|
||||
|
||||
private double theoreticalEarliest;
|
||||
|
||||
private double theoreticalLatest;
|
||||
/**
|
||||
* @return the arrTime
|
||||
*/
|
||||
|
|
@ -115,11 +119,11 @@ public class ServiceActivity extends AbstractActivity implements JobActivity{
|
|||
}
|
||||
|
||||
public double getTheoreticalEarliestOperationStartTime() {
|
||||
return service.getTimeWindow().getStart();
|
||||
return theoreticalEarliest;
|
||||
}
|
||||
|
||||
public double getTheoreticalLatestOperationStartTime() {
|
||||
return service.getTimeWindow().getEnd();
|
||||
return theoreticalLatest;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -152,6 +156,16 @@ public class ServiceActivity extends AbstractActivity implements JobActivity{
|
|||
+ "][twEnd=" + Activities.round(getTheoreticalLatestOperationStartTime()) + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalEarliestOperationStartTime(double earliest) {
|
||||
theoreticalEarliest = earliest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTheoreticalLatestOperationStartTime(double latest) {
|
||||
theoreticalLatest = latest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return service.getType();
|
||||
|
|
|
|||
|
|
@ -93,10 +93,12 @@ public final class Start extends AbstractActivity implements TourActivity {
|
|||
return theoretical_latestOperationStartTime;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setTheoreticalEarliestOperationStartTime(double time) {
|
||||
this.theoretical_earliestOperationStartTime=time;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setTheoreticalLatestOperationStartTime(double time) {
|
||||
this.theoretical_latestOperationStartTime=time;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
package jsprit.core.problem.solution.route.activity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Created by schroeder on 26/05/15.
|
||||
*/
|
||||
public class TimeWindowsImpl implements TimeWindows {
|
||||
|
||||
private Collection<TimeWindow> timeWindows = new ArrayList<TimeWindow>();
|
||||
|
||||
public void add(TimeWindow timeWindow){
|
||||
timeWindows.add(timeWindow);
|
||||
}
|
||||
|
||||
public Collection<TimeWindow> getTimeWindows() {
|
||||
return Collections.unmodifiableCollection(timeWindows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<TimeWindow> getTimeWindows(double time) {
|
||||
return Collections.unmodifiableCollection(timeWindows);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -31,6 +31,10 @@ import jsprit.core.problem.job.Job;
|
|||
*/
|
||||
public interface TourActivity extends HasIndex {
|
||||
|
||||
public void setTheoreticalEarliestOperationStartTime(double earliest);
|
||||
|
||||
public void setTheoreticalLatestOperationStartTime(double latest);
|
||||
|
||||
/**
|
||||
* Basic interface of job-activies.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
package jsprit.core.algorithm;
|
||||
|
||||
import jsprit.core.algorithm.box.Jsprit;
|
||||
import jsprit.core.problem.Location;
|
||||
import jsprit.core.problem.VehicleRoutingProblem;
|
||||
import jsprit.core.problem.job.Service;
|
||||
import jsprit.core.problem.solution.VehicleRoutingProblemSolution;
|
||||
import jsprit.core.problem.vehicle.VehicleImpl;
|
||||
import jsprit.core.util.Solutions;
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Created by schroeder on 26/05/15.
|
||||
*/
|
||||
public class MultipleTimeWindowsTest {
|
||||
|
||||
@Test
|
||||
public void service2ShouldNotBeInserted(){
|
||||
Service s = Service.Builder.newInstance("s1").setLocation(Location.newInstance(10,0)).build();
|
||||
|
||||
Service s2 = Service.Builder.newInstance("s2")
|
||||
.addTimeWindow(50.,60.)
|
||||
.setLocation(Location.newInstance(20, 0)).build();
|
||||
|
||||
VehicleImpl v = VehicleImpl.Builder.newInstance("v1").setStartLocation(Location.newInstance(0,0))
|
||||
.setEarliestStart(0.).setLatestArrival(40).build();
|
||||
|
||||
VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().addJob(s).addJob(s2).addVehicle(v).build();
|
||||
VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(vrp);
|
||||
algorithm.setMaxIterations(100);
|
||||
VehicleRoutingProblemSolution solution = Solutions.bestOf(algorithm.searchSolutions());
|
||||
|
||||
Assert.assertEquals(1,solution.getUnassignedJobs().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void service2ShouldBeInsertedIntoNewVehicle(){
|
||||
Service s = Service.Builder.newInstance("s1").setLocation(Location.newInstance(10,0))
|
||||
.addTimeWindow(5.,15.).build();
|
||||
|
||||
Service s2 = Service.Builder.newInstance("s2")
|
||||
.addTimeWindow(50.,60.)
|
||||
.setLocation(Location.newInstance(20, 0)).build();
|
||||
|
||||
VehicleImpl v = VehicleImpl.Builder.newInstance("v1").setStartLocation(Location.newInstance(0,0))
|
||||
.setEarliestStart(0.).setLatestArrival(40).build();
|
||||
|
||||
VehicleImpl v2 = VehicleImpl.Builder.newInstance("v2").setStartLocation(Location.newInstance(0,0))
|
||||
.setEarliestStart(40.).setLatestArrival(80).build();
|
||||
|
||||
VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().addJob(s).addJob(s2).addVehicle(v).addVehicle(v2).build();
|
||||
VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(vrp);
|
||||
algorithm.setMaxIterations(100);
|
||||
VehicleRoutingProblemSolution solution = Solutions.bestOf(algorithm.searchSolutions());
|
||||
|
||||
Assert.assertEquals(0,solution.getUnassignedJobs().size());
|
||||
Assert.assertEquals(2, solution.getRoutes().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void service2ShouldBeInserted(){
|
||||
Service s = Service.Builder.newInstance("s1").setLocation(Location.newInstance(10,0)).build();
|
||||
|
||||
Service s2 = Service.Builder.newInstance("s2")
|
||||
.addTimeWindow(50., 60.).addTimeWindow(15., 25)
|
||||
.setLocation(Location.newInstance(20, 0)).build();
|
||||
|
||||
VehicleImpl v = VehicleImpl.Builder.newInstance("v1").setStartLocation(Location.newInstance(0,0))
|
||||
.setEarliestStart(0.).setLatestArrival(40).build();
|
||||
|
||||
VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance().addJob(s).addJob(s2).addVehicle(v).build();
|
||||
VehicleRoutingAlgorithm algorithm = Jsprit.createAlgorithm(vrp);
|
||||
algorithm.setMaxIterations(100);
|
||||
VehicleRoutingProblemSolution solution = Solutions.bestOf(algorithm.searchSolutions());
|
||||
|
||||
Assert.assertEquals(0,solution.getUnassignedJobs().size());
|
||||
}
|
||||
}
|
||||
|
|
@ -184,4 +184,28 @@ public class ServiceTest {
|
|||
assertEquals("name",s.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldKnowMultipleTimeWindows(){
|
||||
Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc"))
|
||||
.addTimeWindow(TimeWindow.newInstance(0., 10.)).addTimeWindow(TimeWindow.newInstance(20., 30.))
|
||||
.setName("name").build();
|
||||
assertEquals(2,s.getTimeWindows(0.).size());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void whenMultipleTWOverlap_throwEx(){
|
||||
Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc"))
|
||||
.addTimeWindow(TimeWindow.newInstance(0.,10.))
|
||||
.addTimeWindow(TimeWindow.newInstance(5., 30.))
|
||||
.setName("name").build();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void whenMultipleTWOverlap2_throwEx(){
|
||||
Service s = Service.Builder.newInstance("s").setLocation(Location.newInstance("loc"))
|
||||
.addTimeWindow(TimeWindow.newInstance(20., 30.))
|
||||
.addTimeWindow(TimeWindow.newInstance(0., 25.))
|
||||
.setName("name").build();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue