Hi jie31best,
I too observed that some of the routes of VehicleRoutingProblemSolution does not respect the constraint. In my case, I was trying to implementing the problem in which there were some delivery locations and some pickup locations and some bikes. Constraint was that volume picked between two delivery locations must be less than some number(call it X). Same goes also for, between Start and first delivery location and last delivery location and End. Also I did not force that delivery locations have to be visited, which I ensured by not adding penalty for unassigned delivery jobs in solutions cost calculator. If delivery has to (depending on optimization process and objective function) occur then the above constraint is considered.
Now the interpretation,
Each delivery location comprises of job which contains time window(predefined) in which some bigger vehicle is going to be at that location. Volume to be delivered is considered is zero. Penalty is set to zero which implies that bike does not have to visit these places.
X is actually the bike capacity.
To solve the problem, I considered the bikes to have infinite capacity. Then forced the constraint above mentioned(setting X to be actual bike capacity). Now I also added that between two delivery points, picked volume must be greater than “some value” which I call threshold.
I found that there are cases in which, drop locations are consecutively visited which violates the constraint that picked volume must be greater that some threshold value.
Note that I did not use Delivery Object for locations representing delivery as Delivery Object is made for different purpose.
All object are of Kind Service. I used getName() method to distinguish the deliveries(I named “drop” for deliveries)
Here is the code:
public class ConstraintA implements HardActivityConstraint {
private final int VEHICLE_CAPACITY , THRESHOLD;
public ConstraintA(int vehicleCapacity, double percent) {
this.VEHICLE_CAPACITY = vehicleCapacity;
this.THRESHOLD = (int) (vehicleCapacity * percent);
}
@Override
public ConstraintsStatus fulfilled(JobInsertionContext iFacts, TourActivity prevAct, TourActivity newAct, TourActivity nextAct, double prevActDepTime) {
List<TourActivity> tourActivities = iFacts.getRoute().getActivities();
Job newJob = ((JobActivity) newAct).getJob();
boolean isNewJobDelivery = isJobDelivery.test(newJob);
int[] vol = getPickedVol(tourActivities, prevAct, nextAct);
if (isNewJobDelivery) {
if (vol[0] >= THRESHOLD && vol[1] >= THRESHOLD)
return ConstraintsStatus.FULFILLED;
} else {
if (vol[0] + vol[1] + newJob.getSize().get(0) <= VEHICLE_CAPACITY)
return ConstraintsStatus.FULFILLED;
}
return ConstraintsStatus.NOT_FULFILLED;
}}
// vol has size 2. vol[0] is volume picked from prevAct upto previous delivery and after Start.
// vol[1] is volume picked from nextAct upto next delivery and before End.
isJobDelivery.test is used to test whether or not the current job is delivery.
I think why constraint is not followed is due the ruin precess. Of course, whenever insertion of a TourActivity has to happen the constraint has be followed. But ruin may occur only to subroute of the VehicleRoute and may result into removal of delivery location and inserting into some other route. VehicleRoute from which the delivery location is removed may break the constraint. So in this way we also need to restrict the ruin process and not only the insertion process.