In the example of the job shop problem (https://developers.google.com/optimization/scheduling/job_shop), the result of the optimization provides that all three jobs to be optimized are simultaneously active from time 3 to time 8. The problem I have to solve instead requires that no more than a certain number of jobs can be active at the same time.
I tried to add these constraints to the job shop code:
for job_id1, job1 in enumerate(jobs_data):
model.Add(
sum(
[
model.NewBoolVar(f"""{
all_tasks[job_id1, len(job1)-1].end} > {all_tasks[job_id2, 0].start
}""")
for job_id2, job2 in enumerate(jobs_data)
]
+
[
model.NewBoolVar(f"""{
all_tasks[job_id1, 0].start} < {all_tasks[job_id2, len(job2)-1].end
}""")
for job_id2, job2 in enumerate(jobs_data)
]
) <= 2
)
which are correct from the syntax point of view (therefore they do not return any error messages) but have no effect on the simulation result, which continues to be always the same, that is (the solution I get is a little different from the one that is present in the job shop post):
Solution:
Optimal Schedule Length: 11.0
Machine 0: job_1_task_0 job_0_task_0
[0,2] [2,5]
Machine 1: job_2_task_0 job_0_task_1 job_1_task_2
[0,4] [5,7] [7,11]
Machine 2: job_1_task_1 job_2_task_1 job_0_task_2
[2,3] [4,7] [7,9]
while I would like a solution like this:
Solution:
Optimal Schedule Length: 14.0
Machine 0: job_1_task_0 job_0_task_0
[0,2] [7,10]
Machine 1: job_2_task_0 job_1_task_2 job_0_task_1
[0,4] [4,8] [10,12]
Machine 2: job_1_task_1 job_2_task_1 job_0_task_2
[2,3] [4,7] [12,14]
in which job 0 starts only after job 2 has finished, thus ensuring that there are no more than two jobs active at the same time (obviously this implies that the overall time increases from 11 to 14 but in my real problem that I have to resolve I am obliged to do so).
My idea was to compare each job with all the other jobs and, for each job (let's call it "job_id1"), if its end_time of the last task is greater than the start_time of the first task of another job or his start_time of the first task is greater than the end_time of the last task of the same other job so I add a 1 in the form of a boolean variable in a list (the reason why I had to use NewBoolVar is explained in this post How can I use CpModel to make three 1s appear in a 1D array ? (or-tools)). When the list is full I do the sum of all the 1's in the list and this gives me a measure of how many jobs are superimposed on the job "job_id1" and I impose that this sum have to be less or equal than a certain value, for example 2. Adding this constraint for all jobs in theory should ensure that there are never more than N simultaneously active jobs.
Any suggestions are greatly appreciated, thank you all.
=================================================================
UPDATE:
Thanks to @sascha and Hakan Kjellerstrand (http://www.hakank.org/google_or_tools/) and, in particular to the example http://hakank.org/or_tools/furniture_moving_add_cumulative_sat.py, I replaced the previous approach with the following one based on the AddCumulative constraint
n = 3
duration = [7, 7, 7]
demand = [1, 1, 1]
upper_limit = 14
start_times = [
model.NewIntVar(0, upper_limit, "start_times[%i]" % i) for i in range(n)
]
end_times = [
model.NewIntVar(0, upper_limit * 2, "end_times[%i]" % i) for i in range(n)
]
end_time = model.NewIntVar(0, upper_limit * 2, "end_time")
intervals = [model.NewIntervalVar(start_times[i],duration[i],end_times[i],f"interval[{i}]") for i in range(n)]
# number of needed resources, to be minimized
num_resources = model.NewIntVar(1, 2, "num_resources")
model.AddCumulative(intervals, demand, num_resources)
model.Add(num_resources <= 2)
model.Minimize(num_resources)
that should be better and modify a little bit the output results in this one:
Solution:
Optimal Schedule Length: 1.0
Machine 0: job_1_task_0 job_0_task_0
[0,2] [2,5]
Machine 1: job_2_task_0 job_1_task_2 job_0_task_1
[0,4] [4,8] [8,10]
Machine 2: job_1_task_1 job_2_task_1 job_0_task_2
[2,3] [4,7] [10,12]
where job_0_task_1, job_1_task_2 and job_0_task_2 have now different start_time and end_time.
Unfortunately this is not yet the result I was hoping to achieve since more than 2 jobs are simultaneously active between instant 2 and instant 7 although I have indicated a number of resources less than or equal to 2 (in the problem I have to solve, each resource is associated with only one job), so I am asking again if anyone can help me understand why, thank you.