1
0
Fork 0
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:
oblonski 2015-05-26 09:17:47 +02:00
parent 954f2588a4
commit 7582c5e038
13 changed files with 278 additions and 51 deletions

View file

@ -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);

View file

@ -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,6 +218,9 @@ public class Service extends AbstractJob {
this.name = name;
return this;
}
}
@ -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

View file

@ -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

View file

@ -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";

View file

@ -147,6 +147,7 @@ public final class End extends AbstractActivity implements TourActivity {
+ "][twEnd=" + Activities.round(theoretical_latestOperationStartTime) + "]";
}
@Override
public String getName() {
return "end";

View file

@ -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

View file

@ -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";

View file

@ -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();

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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.
*

View file

@ -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());
}
}

View file

@ -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();
}
}