Hi
I’m trying to implement flexible time windows (soft time windows) in jsprit and there were several questions, the answers to which I would like to receive.
- What should be the behavior when flexible time windows are enabled?
For example, if I use hard time windows (VehicleDependentTimeWindowConstraints) by adding constraintManager.addTimeWindowConstraint()
, then points that do not fall within the time interval are thrown out.
- Should a different route with a new transport be built for points that do not fall within the specified time window, or, for example, should the application offer the user a different time?
After reading similar topics on the forum I found the following code:
public class SoftTimeWindowConstraint implements SoftActivityConstraint {
// TODO: Choose good penalty factors
private static final int ACTIVITY_EARLY_PENALTY_FACTOR = 1000;
private static final int ACTIVITY_LATE_PENALTY_FACTOR = 1000;
private static final int VEHICLE_EARLY_PENALTY_FACTOR = 1000;
private static final int VEHICLE_LATE_PENALTY_FACTOR = 1000;
public SoftTimeWindowConstraint() {
super();
}
@Override
public double getCosts(JobInsertionContext iFacts,
TourActivity prevAct, TourActivity newAct,
TourActivity nextAct, double prevActDepTime) {
List<TourActivity> newActList = new ArrayList<TourActivity>();
newActList.add(newAct);
return softTimeWindowPenalties(newActList, iFacts.getNewVehicle(), iFacts.getNewDepTime());
}
public static double softTimeWindowPenalties(List<TourActivity> activities, Vehicle vehicle, double vehicleDepartureTime) {
double totalPenalty = 0.0;
TourActivity lastActivity = null;
double lastActivityEndTime = Double.NEGATIVE_INFINITY;
for (TourActivity activity : activities) {
double timeEarlyForActivity = Math.max(0, activity.getTheoreticalEarliestOperationStartTime() - activity.getArrTime());
double timeLateForActivity = Math.max(0, activity.getEndTime() - (activity.getTheoreticalLatestOperationStartTime() - activity.getOperationTime()));
totalPenalty += penaltyIfViolation(timeEarlyForActivity,ACTIVITY_EARLY_PENALTY_FACTOR);
totalPenalty += penaltyIfViolation(timeLateForActivity,ACTIVITY_LATE_PENALTY_FACTOR);
// Get the last activity in the tour to compare to the vehicle's end time
if (activity.getEndTime() > lastActivityEndTime) {
lastActivityEndTime = activity.getEndTime();
lastActivity = activity;
}
}
double timeEarlyForVehicle = Math.max(0, vehicle.getEarliestDeparture() - vehicleDepartureTime);
double timeLateForVehicle = Math.max(0, (lastActivity.getEndTime() - lastActivity.getOperationTime()) - vehicle.getLatestArrival());
totalPenalty += penaltyIfViolation(timeEarlyForVehicle,VEHICLE_EARLY_PENALTY_FACTOR);
totalPenalty += penaltyIfViolation(timeLateForVehicle,VEHICLE_LATE_PENALTY_FACTOR);
return totalPenalty;
}
public static double penaltyIfViolation(double timeViolation, double penaltyFactor) {
if (timeViolation > 0) {
return penaltyFactor * Math.pow(timeViolation,2);
} else {
return 0.0;
}
}
}
- What is the basis for choosing penalty factors?
- Should the flexible window enable functionality be for the entire route (all points) or should it be possible to enable it for a separate point?
When I use hard time windows, if there is a point that does not hit the specified time, then no exception is thrown and the point is simply thrown from the system.
For this I have implemented the following:
UnassignedJobReasonTracker reasonTracker = new UnassignedJobReasonTracker();
algorithm.addListener(reasonTracker);
and
private void checkIfUnassignedJobsPresent(VehicleRoutingProblemSolution solution) {
if (!solution.getUnassignedJobs().isEmpty()) {
StringBuilder reason = new StringBuilder();
for (Job unassignedJob : solution.getUnassignedJobs()) {
String jobId = unassignedJob.getId();
int reasonCode = reasonTracker.getMostLikelyReasonCode(jobId);
String message = reasonTracker.getHumanReadableReason(reasonCode);
reason.append("Reason code: ").append(reasonCode).append(" | ");
for (Activity activity : unassignedJob.getActivities()) {
reason
.append(activity.getActivityType())
.append(" ")
.append("located in { ")
.append("latitude: ")
.append(activity.getLocation().getCoordinate().getX())
.append(" | ")
.append("longitude: ")
.append(activity.getLocation().getCoordinate().getY())
.append(" } ")
.append(message);
}
}
log.debug(reason.toString());
throw new ImpossibleRoutingException(reason.toString());
}
}
- Is this the correct way to find points that have not been routed or are there other ways?
Thanks in advance for your help!