Restrict back to back same location job

There are some jobs to be done in same location. I wanted to add constraint not to do 2 jobs same location back to back. I tried with HardActivityConstraint.

if (prevAct instanceof TourActivity.JobActivity && nextAct instanceof TourActivity.JobActivity) {
    if(prevAct.getLocation().getId().equalsIgnoreCase(newAct.getLocation().getId())) {
        return ConstraintsStatus.NOT_FULFILLED;
    }
    if(nextAct.getLocation().getId().equalsIgnoreCase(newAct.getLocation().getId())) {
        return ConstraintsStatus.NOT_FULFILLED;
    }
}
if (prevAct instanceof TourActivity.JobActivity) {
    if(prevAct.getLocation().getId().equalsIgnoreCase(newAct.getLocation().getId())) {
        return ConstraintsStatus.NOT_FULFILLED;
    }
}
if (nextAct instanceof TourActivity.JobActivity) {
    if(nextAct.getLocation().getId().equalsIgnoreCase(newAct.getLocation().getId())) {
        return ConstraintsStatus.NOT_FULFILLED;
    }
}

But this is not working. Can anyone point the mistake I made?

Here are the input:
job/vehicle,location id,x,y,capacity
Vehicle,99,30,40,3
Job1,1,0,40,1
Job2,1,0,40,1
Job3,1,0,40,1
Job4,2,60,30,1
Job5,2,60,30,1
Job6,2,60,30,1

I think this can be done in simpler way:

 if(prevAct.getLocation().getId().equalsIgnoreCase(newAct.getLocation().getId()) ||
          nextAct.getLocation().getId().equalsIgnoreCase(newAct.getLocation().getId())) {
      return ConstraintsStatus.NOT_FULFILLED;
 }

This is also not working :frowning:

Hi @sutirtha_kayal,

I haven’t tried out your implemented constraint, but I think a possible reason could be that the ruin phase breaks the constraint.

Your constraint controls the insertion phase and makes sure that (if all jobs are to be inserted, e.g., when building initial solution) AAB is not allowed (A and B indicate locations) while ABA is. However, in the ruin phase, it is possible that the B is ruined from ABA and it leaves AA for the insertion phase, which then inserts B after the second A so that cost is minimized and results in a sequence of AAB. Thus in the final solution your constraint is broken.

Some related posts can be found here:

Best regards,
He

Thanks @jie31best

I have applied

RuinListener

with the help of this
And now it is working. It restricts solver to produce solution having same location job back to back.
But my purpose is not solved yet. I actually put a constraint that did not allow a vehicle to visit to a place unless he does 4 more jobs.

I want below solution for the example I have given like:

Start->Job1->Job4->Depot
Start->Job2->Job6->Depot
Start->Job3->Job5->Depot

Its basically the next stage. Can you suggest me anything for this?

such a constraint could also potentially be broken in the ruin process, thus you need to apply another corresponding ruin listener, if you haven’t done so.

I tried to add constraint as:

  if(prevAct instanceof TourActivity.JobActivity || nextAct instanceof TourActivity.JobActivity) {
        if(prevAct instanceof TourActivity.JobActivity && nextAct instanceof TourActivity.JobActivity) {
            if (prevAct.getLocation().getId().equalsIgnoreCase(nextAct.getLocation().getId())) {
                return ConstraintsStatus.NOT_FULFILLED;
            }
        }
        if(prevAct instanceof TourActivity.JobActivity) {
            if (prevAct.getLocation().getId().equalsIgnoreCase(newAct.getLocation().getId())) {
                return ConstraintsStatus.NOT_FULFILLED;
            }
        }
        if(nextAct instanceof TourActivity.JobActivity) {
            if (nextAct.getLocation().getId().equalsIgnoreCase(newAct.getLocation().getId())) {
                return ConstraintsStatus.NOT_FULFILLED;
            }
        }
    }

So that there should not be any job in same location back to back and not after one job.

Additionally added ruin listener as:

public void ruinEnds(Collection<VehicleRoute> routes, Collection<Job> unassignedJobs) {
    Map<TourActivity, VehicleRoute> toDeleteActRouteMap = new HashMap<>();
    int count = 4;
    for(VehicleRoute route : routes){
        for (int i = 0; i < route.getActivities().size() - 1; i++) {
            TourActivity act1 = route.getActivities().get(i);
            for(int j = i+1; j < i+1+count && j < route.getActivities().size(); j++) {
                TourActivity act2 = route.getActivities().get(j);
                if(act1.getLocation().getId().equalsIgnoreCase(act2.getLocation().getId())) {
                    toDeleteActRouteMap.put(act2, 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);
            }
        }
    }
}

This above code is not working. Can you please help me to find out the error?