Skip to content

Commit db0c39e

Browse files
committed
refine max-time-in-vehicle constraint
1 parent b5998e1 commit db0c39e

22 files changed

+446
-434
lines changed

jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/VehicleRoutingAlgorithm.java

+11-12
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,6 @@
1717
*/
1818
package com.graphhopper.jsprit.core.algorithm;
1919

20-
import java.util.ArrayList;
21-
import java.util.Collection;
22-
import java.util.HashSet;
23-
import java.util.Set;
24-
25-
import org.slf4j.Logger;
26-
import org.slf4j.LoggerFactory;
27-
2820
import com.graphhopper.jsprit.core.algorithm.SearchStrategy.DiscoveredSolution;
2921
import com.graphhopper.jsprit.core.algorithm.listener.SearchStrategyListener;
3022
import com.graphhopper.jsprit.core.algorithm.listener.SearchStrategyModuleListener;
@@ -38,6 +30,13 @@
3830
import com.graphhopper.jsprit.core.problem.solution.route.VehicleRoute;
3931
import com.graphhopper.jsprit.core.problem.solution.route.activity.TourActivity;
4032
import com.graphhopper.jsprit.core.util.Solutions;
33+
import org.slf4j.Logger;
34+
import org.slf4j.LoggerFactory;
35+
36+
import java.util.ArrayList;
37+
import java.util.Collection;
38+
import java.util.HashSet;
39+
import java.util.Set;
4140

4241

4342
/**
@@ -155,13 +154,13 @@ private void verify(VehicleRoutingProblemSolution solution) {
155154
allJobs.removeAll(route.getTourActivities().getJobs());
156155
if (route.getVehicle().getIndex() == 0)
157156
throw new IllegalStateException("vehicle used in initial solution has no index. probably a vehicle is used that has not been added to the " +
158-
" the VehicleRoutingProblem. only use vehicles that have already been added to the problem.");
157+
" the VehicleRoutingProblem. only use vehicles that have already been added to the problem.");
159158
for (TourActivity act : route.getActivities()) {
160159
if (act.getIndex() == 0)
161160
throw new IllegalStateException("act in initial solution has no index. activities are created and associated to their job in VehicleRoutingProblem\n." +
162-
" thus if you build vehicle-routes use the jobActivityFactory from vehicle routing problem like that \n" +
163-
" VehicleRoute.Builder.newInstance(knownVehicle).setJobActivityFactory(vrp.getJobActivityFactory).addService(..)....build() \n" +
164-
" then the activities that are created to build the route are identical to the ones used in VehicleRoutingProblem");
161+
" thus if you build vehicle-routes use the jobActivityFactory from vehicle routing problem like that \n" +
162+
" VehicleRoute.Builder.newInstance(knownVehicle).setJobActivityFactory(vrp.getJobActivityFactory).addService(..)....build() \n" +
163+
" then the activities that are created to build the route are identical to the ones used in VehicleRoutingProblem");
165164
}
166165
}
167166

jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/UpdateMaxTimeInVehicle.java

+43
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,49 @@ public void finish() {
190190
}
191191
}
192192

193+
public void finish(List<TourActivity> activities, Job ignore) {
194+
for (Vehicle v : vehicles) {
195+
int vehicleIndex = v.getVehicleTypeIdentifier().getIndex();
196+
197+
//!!! open routes !!!
198+
double routeEnd;
199+
if (!v.isReturnToDepot()) routeEnd = prevActEndTimes[vehicleIndex];
200+
else
201+
routeEnd = prevActEndTimes[vehicleIndex] + transportTime.getTransportTime(prevActLocations[vehicleIndex], v.getEndLocation(), prevActEndTimes[vehicleIndex], route.getDriver(), v);
202+
203+
Map<String, Double> openDeliveries = new HashMap<>();
204+
for (Job job : openPickupEndTimes.get(vehicleIndex).keySet()) {
205+
if (job == ignore) continue;
206+
double actEndTime = openPickupEndTimes.get(vehicleIndex).get(job);
207+
double slackTime = job.getMaxTimeInVehicle() - (routeEnd - actEndTime);
208+
openDeliveries.put(job.getId(), slackTime);
209+
}
210+
211+
double minSlackTimeAtEnd = minSlackTime(openDeliveries);
212+
stateManager.putRouteState(route, v, latestStartId, routeEnd + minSlackTimeAtEnd);
213+
List<TourActivity> acts = new ArrayList<>(activities);
214+
Collections.reverse(acts);
215+
for (TourActivity act : acts) {
216+
if (act instanceof ServiceActivity || act instanceof PickupActivity) {
217+
String jobId = ((TourActivity.JobActivity) act).getJob().getId();
218+
openDeliveries.remove(jobId);
219+
double minSlackTime = minSlackTime(openDeliveries);
220+
double latestStart = actStart(act, v) + minSlackTime;
221+
stateManager.putActivityState(act, v, latestStartId, latestStart);
222+
} else {
223+
String jobId = ((TourActivity.JobActivity) act).getJob().getId();
224+
if (slackTimes.get(vehicleIndex).containsKey(act)) {
225+
double slackTime = slackTimes.get(vehicleIndex).get(act);
226+
openDeliveries.put(jobId, slackTime);
227+
}
228+
double minSlackTime = minSlackTime(openDeliveries);
229+
double latestStart = actStart(act, v) + minSlackTime;
230+
stateManager.putActivityState(act, v, latestStartId, latestStart);
231+
}
232+
}
233+
}
234+
}
235+
193236
private double actStart(TourActivity act, Vehicle v) {
194237
return actStartTimes.get(v.getVehicleTypeIdentifier().getIndex()).get(act);
195238
}

jsprit-core/src/main/java/com/graphhopper/jsprit/core/algorithm/state/VehicleDependentTraveledDistance.java

+13-13
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public double getDistance() {
6363

6464
private List<Vehicle> uniqueVehicles;
6565

66-
private Map<VehicleTypeKey,State> states;
66+
private Map<VehicleTypeKey, State> states;
6767

6868
public VehicleDependentTraveledDistance(TransportDistance transportCostMatrices, StateManager stateManager, StateId distanceInRouteId, Collection<Vehicle> vehicles) {
6969
this.transportDistance = transportCostMatrices;
@@ -75,8 +75,8 @@ public VehicleDependentTraveledDistance(TransportDistance transportCostMatrices,
7575
private List<Vehicle> getUniqueVehicles(Collection<Vehicle> vehicles) {
7676
Set<VehicleTypeKey> types = new HashSet<>();
7777
List<Vehicle> uniqueVehicles = new ArrayList<>();
78-
for(Vehicle v : vehicles){
79-
if(!types.contains(v.getVehicleTypeIdentifier())){
78+
for (Vehicle v : vehicles) {
79+
if (!types.contains(v.getVehicleTypeIdentifier())) {
8080
types.add(v.getVehicleTypeIdentifier());
8181
uniqueVehicles.add(v);
8282
}
@@ -88,32 +88,32 @@ private List<Vehicle> getUniqueVehicles(Collection<Vehicle> vehicles) {
8888
public void begin(VehicleRoute route) {
8989
this.route = route;
9090
states = new HashMap<>();
91-
for(Vehicle v : uniqueVehicles){
92-
State state = new State(v.getStartLocation(),0);
93-
states.put(v.getVehicleTypeIdentifier(),state);
91+
for (Vehicle v : uniqueVehicles) {
92+
State state = new State(v.getStartLocation(), 0);
93+
states.put(v.getVehicleTypeIdentifier(), state);
9494
}
9595
}
9696

9797
@Override
9898
public void visit(TourActivity activity) {
99-
for(Vehicle v : uniqueVehicles){
99+
for (Vehicle v : uniqueVehicles) {
100100
State old = states.get(v.getVehicleTypeIdentifier());
101101
double distance = old.getDistance();
102-
distance += transportDistance.getDistance(old.getPrevLocation(),activity.getLocation(),0,v);
103-
stateManager.putActivityState(activity,v,traveledDistanceId,distance);
104-
states.put(v.getVehicleTypeIdentifier(),new State(activity.getLocation(),distance));
102+
distance += transportDistance.getDistance(old.getPrevLocation(), activity.getLocation(), 0, v);
103+
stateManager.putActivityState(activity, v, traveledDistanceId, distance);
104+
states.put(v.getVehicleTypeIdentifier(), new State(activity.getLocation(), distance));
105105
}
106106
}
107107

108108
@Override
109109
public void finish() {
110-
for(Vehicle v : uniqueVehicles){
110+
for (Vehicle v : uniqueVehicles) {
111111
State old = states.get(v.getVehicleTypeIdentifier());
112112
double distance = old.getDistance();
113-
if(v.isReturnToDepot()) {
113+
if (v.isReturnToDepot()) {
114114
distance += transportDistance.getDistance(old.getPrevLocation(), v.getEndLocation(), 0, v);
115115
}
116-
stateManager.putRouteState(route,v,traveledDistanceId, distance);
116+
stateManager.putRouteState(route, v, traveledDistanceId, distance);
117117
}
118118
}
119119

jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/AbstractVehicle.java

+29-29
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,35 @@ public void setIndex(int index) {
4545

4646
private VehicleTypeKey vehicleIdentifier;
4747

48-
private Object userData;
48+
private Object userData;
4949

50-
/**
51-
* @return User-specific domain data associated with the vehicle
52-
*/
53-
@Override
50+
/**
51+
* @return User-specific domain data associated with the vehicle
52+
*/
53+
@Override
5454
public Object getUserData() {
55-
return userData;
56-
}
57-
58-
protected void setUserData(Object userData) {
59-
this.userData = userData;
60-
}
61-
62-
@Override
63-
public int getIndex() {
64-
return index;
65-
}
66-
67-
protected void setIndex(int index) {
68-
this.index = index;
69-
}
70-
71-
@Override
72-
public VehicleTypeKey getVehicleTypeIdentifier() {
73-
return vehicleIdentifier;
74-
}
75-
76-
protected void setVehicleIdentifier(VehicleTypeKey vehicleTypeIdentifier) {
77-
this.vehicleIdentifier = vehicleTypeIdentifier;
78-
}
55+
return userData;
56+
}
57+
58+
protected void setUserData(Object userData) {
59+
this.userData = userData;
60+
}
61+
62+
@Override
63+
public int getIndex() {
64+
return index;
65+
}
66+
67+
protected void setIndex(int index) {
68+
this.index = index;
69+
}
70+
71+
@Override
72+
public VehicleTypeKey getVehicleTypeIdentifier() {
73+
return vehicleIdentifier;
74+
}
75+
76+
protected void setVehicleIdentifier(VehicleTypeKey vehicleTypeIdentifier) {
77+
this.vehicleIdentifier = vehicleTypeIdentifier;
78+
}
7979
}

jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/Location.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,14 @@ public static Builder newInstance() {
7474

7575
/**
7676
* Sets user specific domain data associated with the object.
77-
*
77+
* <p>
7878
* <p>
7979
* The user data is a black box for the framework, it only stores it,
8080
* but never interacts with it in any way.
8181
* </p>
8282
*
83-
* @param userData
84-
* any object holding the domain specific user data
85-
* associated with the object.
83+
* @param userData any object holding the domain specific user data
84+
* associated with the object.
8685
* @return builder
8786
*/
8887
public Builder setUserData(Object userData) {
@@ -130,7 +129,7 @@ public Builder setId(String id) {
130129
* @param name
131130
* @return
132131
*/
133-
public Builder setName(String name){
132+
public Builder setName(String name) {
134133
this.name = name;
135134
return this;
136135
}
@@ -191,7 +190,9 @@ public Coordinate getCoordinate() {
191190
return coordinate;
192191
}
193192

194-
public String getName() { return name; }
193+
public String getName() {
194+
return name;
195+
}
195196

196197
@Override
197198
public boolean equals(Object o) {

jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/constraint/MaxDistanceConstraint.java

+21-21
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
/**
3535
* Created by schroeder on 11/10/16.
3636
*/
37-
public class MaxDistanceConstraint implements HardActivityConstraint{
37+
public class MaxDistanceConstraint implements HardActivityConstraint {
3838

3939
private StateManager stateManager;
4040

@@ -44,7 +44,7 @@ public class MaxDistanceConstraint implements HardActivityConstraint{
4444

4545
private Double[] maxDistances;
4646

47-
public MaxDistanceConstraint(StateManager stateManager, StateId distanceId, TransportDistance distanceCalculator, Map<Vehicle,Double> maxDistancePerVehicleMap) {
47+
public MaxDistanceConstraint(StateManager stateManager, StateId distanceId, TransportDistance distanceCalculator, Map<Vehicle, Double> maxDistancePerVehicleMap) {
4848
this.stateManager = stateManager;
4949
this.distanceId = distanceId;
5050
this.distanceCalculator = distanceCalculator;
@@ -53,50 +53,50 @@ public MaxDistanceConstraint(StateManager stateManager, StateId distanceId, Tran
5353

5454
private void makeArray(Map<Vehicle, Double> maxDistances) {
5555
int maxIndex = getMaxIndex(maxDistances.keySet());
56-
this.maxDistances = new Double[maxIndex+1];
57-
for(Vehicle v : maxDistances.keySet()){
58-
this.maxDistances[v.getIndex()]=maxDistances.get(v);
56+
this.maxDistances = new Double[maxIndex + 1];
57+
for (Vehicle v : maxDistances.keySet()) {
58+
this.maxDistances[v.getIndex()] = maxDistances.get(v);
5959
}
6060
}
6161

6262
private int getMaxIndex(Collection<Vehicle> vehicles) {
6363
int index = 0;
64-
for(Vehicle v : vehicles){
65-
if(v.getIndex() > index) index = v.getIndex();
64+
for (Vehicle v : vehicles) {
65+
if (v.getIndex() > index) index = v.getIndex();
6666
}
6767
return index;
6868
}
6969

7070
@Override
7171
public ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) {
72-
if(!hasMaxDistance(iFacts.getNewVehicle())) return ConstraintsStatus.FULFILLED;
72+
if (!hasMaxDistance(iFacts.getNewVehicle())) return ConstraintsStatus.FULFILLED;
7373
Double currentDistance = 0d;
7474
boolean routeIsEmpty = iFacts.getRoute().isEmpty();
75-
if(!routeIsEmpty){
76-
currentDistance = stateManager.getRouteState(iFacts.getRoute(),iFacts.getNewVehicle(), distanceId,Double.class);
75+
if (!routeIsEmpty) {
76+
currentDistance = stateManager.getRouteState(iFacts.getRoute(), iFacts.getNewVehicle(), distanceId, Double.class);
7777
}
7878
double maxDistance = getMaxDistance(iFacts.getNewVehicle());
79-
if(currentDistance > maxDistance) return ConstraintsStatus.NOT_FULFILLED_BREAK;
79+
if (currentDistance > maxDistance) return ConstraintsStatus.NOT_FULFILLED_BREAK;
8080

8181
double distancePrevAct2NewAct = distanceCalculator.getDistance(prevAct.getLocation(), newAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle());
8282
double distanceNewAct2nextAct = distanceCalculator.getDistance(newAct.getLocation(), nextAct.getLocation(), iFacts.getNewDepTime(), iFacts.getNewVehicle());
8383
double distancePrevAct2NextAct = distanceCalculator.getDistance(prevAct.getLocation(), nextAct.getLocation(), prevActDepTime, iFacts.getNewVehicle());
84-
if(prevAct instanceof Start && nextAct instanceof End) distancePrevAct2NextAct = 0;
85-
if(nextAct instanceof End && !iFacts.getNewVehicle().isReturnToDepot()){
84+
if (prevAct instanceof Start && nextAct instanceof End) distancePrevAct2NextAct = 0;
85+
if (nextAct instanceof End && !iFacts.getNewVehicle().isReturnToDepot()) {
8686
distanceNewAct2nextAct = 0;
8787
distancePrevAct2NextAct = 0;
8888
}
8989
double additionalDistance = distancePrevAct2NewAct + distanceNewAct2nextAct - distancePrevAct2NextAct;
90-
if(currentDistance + additionalDistance > maxDistance) return ConstraintsStatus.NOT_FULFILLED;
90+
if (currentDistance + additionalDistance > maxDistance) return ConstraintsStatus.NOT_FULFILLED;
9191

9292

9393
double additionalDistanceOfPickup = 0;
94-
if(newAct instanceof DeliverShipment){
94+
if (newAct instanceof DeliverShipment) {
9595
int iIndexOfPickup = iFacts.getRelatedActivityContext().getInsertionIndex();
9696
TourActivity pickup = iFacts.getAssociatedActivities().get(0);
9797
TourActivity actBeforePickup;
98-
if(iIndexOfPickup > 0) actBeforePickup = iFacts.getRoute().getActivities().get(iIndexOfPickup-1);
99-
else actBeforePickup = new Start(iFacts.getNewVehicle().getStartLocation(),0,Double.MAX_VALUE);
98+
if (iIndexOfPickup > 0) actBeforePickup = iFacts.getRoute().getActivities().get(iIndexOfPickup - 1);
99+
else actBeforePickup = new Start(iFacts.getNewVehicle().getStartLocation(), 0, Double.MAX_VALUE);
100100
TourActivity actAfterPickup;
101101
if (iIndexOfPickup < iFacts.getRoute().getActivities().size())
102102
actAfterPickup = iFacts.getRoute().getActivities().get(iIndexOfPickup);
@@ -114,21 +114,21 @@ public ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prev
114114
}
115115

116116

117-
if(currentDistance + additionalDistance + additionalDistanceOfPickup > maxDistance){
117+
if (currentDistance + additionalDistance + additionalDistanceOfPickup > maxDistance) {
118118
return ConstraintsStatus.NOT_FULFILLED;
119119
}
120120

121121
return ConstraintsStatus.FULFILLED;
122122
}
123123

124-
private boolean hasMaxDistance(Vehicle newVehicle){
125-
if(newVehicle.getIndex() >= this.maxDistances.length) return false;
124+
private boolean hasMaxDistance(Vehicle newVehicle) {
125+
if (newVehicle.getIndex() >= this.maxDistances.length) return false;
126126
return this.maxDistances[newVehicle.getIndex()] != null;
127127
}
128128

129129
private double getMaxDistance(Vehicle newVehicle) {
130130
Double maxDistance = this.maxDistances[newVehicle.getIndex()];
131-
if(maxDistance == null) return Double.MAX_VALUE;
131+
if (maxDistance == null) return Double.MAX_VALUE;
132132
return maxDistance;
133133
}
134134
}

jsprit-core/src/main/java/com/graphhopper/jsprit/core/problem/vehicle/Vehicle.java

+9-8
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,15 @@ public interface Vehicle extends HasId, HasIndex {
7272
public abstract VehicleTypeKey getVehicleTypeIdentifier();
7373

7474
public abstract Skills getSkills();
75-
/**
76-
* @return User-specific domain data associated with the vehicle
77-
*/
78-
public Object getUserData();
75+
76+
/**
77+
* @return User-specific domain data associated with the vehicle
78+
*/
79+
public Object getUserData();
7980

8081
public abstract Break getBreak();
81-
// Switch to this as soon as we switct to Java 8:
82-
// default Object getUserData() {
83-
// return null;
84-
// };
82+
// Switch to this as soon as we switct to Java 8:
83+
// default Object getUserData() {
84+
// return null;
85+
// };
8586
}

0 commit comments

Comments
 (0)