Firstly, the cumulative / / [2,3] do not have the time to set the expression, so you have to publish explicit restrictions expressing "if two tasks of different families work on the same machine, then there should be a space between the end of the predecessor task and the beginning of the successor task "
This can be encoded by calling:
setups(Ss, Ms, [6,6,3,7,5,8,4,4,4,5], [1,1,1,1,2,2,2,3,3,3], [4,4,4,4,3,3,3,5,5,5]),
defined as:
% post setup constraints for start times Ss, machines Ms, durations % Ds, task families Fs, and setup times Ts setups(Ss, Ms, Ds, Fs, Ts) :- ( fromto(Ss,[S1|Ss2],Ss2,[]), fromto(Ms,[M1|Ms2],Ms2,[]), fromto(Ds,[D1|Ds2],Ds2,[]), fromto(Fs,[F1|Fs2],Fs2,[]), fromto(Ts,[T1|Ts2],Ts2,[]) do ( foreach(S2,Ss2), foreach(M2,Ms2), foreach(D2,Ds2), foreach(F2,Fs2), foreach(T2,Ts2), param(S1,M1,D1,F1,T1) do ( F1 = F2 -> true ; % find forbidden interval for S2-S1 if on same machine L is 1-(T1+D2), U is (T2+D1)-1, StartToStart in \(L..U), (M1
Secondly, if the machines are interchangeable, as in your example, you can break the symmetries by indicating that 1 should happen before 2 and 2 should happen before 3 in Ms:
value_order(Ms),
defined as:
value_order(Ms) :- automaton(Ms, [source(q0),sink(q0),sink(q1),sink(q2)], [arc(q0,1,q1), arc(q1,1,q1), arc(q1,2,q2), arc(q2,1,q2), arc(q2,2,q2), arc(q2,3,q2)]).
Thirdly, fixing all the machines before starting all starts is a much better search strategy. Another refinement is to (a) fix the machines, (b) shorten the intervals of tasks sufficient to put order on the machine, (c) fix the start time:
Q1
With these changes, the optimal solution with proof of optimality is obtained within approximately 550 ms:
| ?- statistics(runtime,_), go(Ss,Es,Ms,_,[step]), statistics(runtime,R). Ss = [1,7,1,13,7,12,17,1,5,9], Es = [7,13,4,20,12,20,21,5,9,14], Ms = [1,1,2,1,2,2,3,3,3,3], R = [1621540,550] ? yes