Hi @kneekill,
I tested on the following example:
VehicleTypeImpl type = VehicleTypeImpl.Builder.newInstance("type")
.addCapacityDimension(0, 3)
.build();
VehicleImpl v1 = VehicleImpl.Builder.newInstance("v1")
.setType(type)
.setReturnToDepot(false)
.setStartLocation(Location.newInstance(0, 0))
.build();
Delivery s1 = Delivery.Builder.newInstance("s1")
.setLocation(Location.newInstance(0, 0))
.build();
Delivery s2 = Delivery.Builder.newInstance("s2")
.setLocation(Location.newInstance(0, 0))
.build();
Service s3 = Service.Builder.newInstance("s3")
.setLocation(Location.newInstance(0, 1))
.build();
VehicleRoutingProblem vrp = VehicleRoutingProblem.Builder.newInstance()
.addJob(s1)
.addJob(s2)
.addJob(s3)
.addVehicle(v1)
.setFleetSize(VehicleRoutingProblem.FleetSize.FINITE)
.build();
Jsprit.Builder algorithmBuilder = Jsprit.Builder.newInstance(vrp);
StateManager stateManager = new StateManager(vrp);
ConstraintManager constraintManager = new ConstraintManager(vrp, stateManager);
constraintManager.addConstraint(new HardActivityConstraint() {
@Override
public ConstraintsStatus fulfilled(
JobInsertionContext iFacts,
TourActivity prevAct,
TourActivity newAct,
TourActivity nextAct,
double prevActDepTime) {
if(isInstanceOfDelivery(prevAct) && isInstanceOfDelivery(newAct)) {
return ConstraintsStatus.NOT_FULFILLED;
}
if(isInstanceOfDelivery(newAct) && isInstanceOfDelivery(nextAct)) {
return ConstraintsStatus.NOT_FULFILLED;
}
return ConstraintsStatus.FULFILLED;
}
}, ConstraintManager.Priority.HIGH);
algorithmBuilder.setStateAndConstraintManager(stateManager, constraintManager);
VehicleRoutingAlgorithm algorithm = algorithmBuilder.buildAlgorithm();
VehicleRoutingProblemSolution s = Solutions.bestOf(algorithm.searchSolutions());
SolutionPrinter.print(vrp, s, SolutionPrinter.Print.VERBOSE);
so now, as you have observed, even with the constraint that two delivery activities cannot be in a row, I get a solution with cost 1 and sequence s1, s2, s3 - two delivery activities in a row.
Then I add the following InsertionStartsListener:
algorithm.addListener(new InsertionStartsListener() {
@Override
public void informInsertionStarts(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs) {
Map<TourActivity, VehicleRoute> toDeleteActRouteMap = new HashMap<>();
for(VehicleRoute route : routes){
for (int i = 0; i < route.getActivities().size() - 1; i++) {
TourActivity act1 = route.getActivities().get(i);
TourActivity act2 = route.getActivities().get(i + 1);
if(isInstanceOfDelivery(act1) && isInstanceOfDelivery(act2)) {
toDeleteActRouteMap.put(act1, route);
}
}
}
for (Map.Entry<TourActivity, VehicleRoute> entry : toDeleteActRouteMap.entrySet()) {
TourActivity act = entry.getKey();
VehicleRoute route = entry.getValue();
if (act instanceof TourActivity.JobActivity) {
Job job = ((TourActivity.JobActivity) act).getJob();
boolean removed = route.getTourActivities().removeJob(job);
if(removed) {
unassignedJobs.add(job);
}
}
}
}
});
now I get a solution with cost 2 and sequence s2, s3, s1 - it follows the constraint.
Hopefully this helps.
Best regards,
He