Download
%
% ECLiPSe sample code
% Author: Joachim Schimpf
% Licenced under CC-BY-4.0 : http://creativecommons.org/licenses/by/4.0/
%
% This is a simple model for the Test Scheduling Problem from the CP2015
% Modelling Competition, described at http://csplib.org/Problems/prob073
%
% Uses straightforward no-overlap constraints and a simple search heuristic.
%


% Data structure declarations
:- local struct(task(name,dur,machs,nmach,res,nres,start,mach)).
:- local struct(res(name,cap)).
:- local struct(mach(name,id)).

% Libraries used
:- lib(ic).
:- lib(ic_edge_finder).
:- lib(branch_and_bound).
:- lib(timeout).


main :-
%        Instance = "instances/t40m10r3-2.pl",        % opt=1725
        Instance = "instances/t50m10r3-9.pl",         % opt=7279
%        Instance = "instances/t100m50r10-11.pl",     % opt=4970
%        Instance = "instances/t500m50r5-5.pl",       % opt=33848
        get_data(Instance, Resources, Machines, Tasks),
        model(Resources, Machines, Tasks, MakeSpan),
        solve(Tasks, MakeSpan),
        writeln(makespan=MakeSpan).


model(Resources, Machines, Tasks, MakeSpan) :-

        % Initialize machine variables, define task ends and makespan
        (
            foreach(task{start:S,dur:D,machs:Ms,mach:M},Tasks),
            foreach(S,Starts), foreach(E,Ends), foreach(D,Durs)
        do
            M #:: Ms,           % machine variable
            E #= S+D            % end time
        ),
        MakeSpan #= max(Ends),
        LowerBound is integer(ceiling(sum(Durs)/length(Machines))),
        MakeSpan #>= LowerBound,

        % Initialize start times
        Starts #:: 0..sum(Durs)-min(Durs),

        % Non-overlap on global resources
        ( foreach(res{name:RN},Resources), param(Tasks) do
            (
                foreach(task{res:Rs,start:S,dur:D},Tasks),
                fromto(Starts,Starts1,Starts2,[]),
                fromto(Durs,Durs1,Durs2,[]),
                param(RN)
            do
                ( memberchk(RN,Rs) ->
                    Starts1 = [S|Starts2],
                    Durs1 = [D|Durs2]
                ;
                    Starts1 = Starts2,
                    Durs1 = Durs2
                )
            ),
            ( Starts==[] -> true ; disjunctive(Starts, Durs) )
        ),

        % Non-overlap on machines
        ( fromto(Tasks,[T1|Ts],Ts,[]) >> (foreach(T2,Ts),param(T1)) do
            task{start:S1,dur:D1,mach:M1} = T1,
            task{start:S2,dur:D2,mach:M2} = T2,
            M1#=M2 => (S1+D1#=<S2 or S2+D2#=<S1)
        ).


solve(Tasks, MakeSpan) :-

        % Order tasks with many resources/few machines/long duration first
        sort(dur of task, >=, Tasks, Tasks1),
        sort(nmach of task, =<, Tasks1, Tasks2),
        sort(nres of task, >=, Tasks2, OrderedTasks),

        % branch-and-bound, allowing 10s for each improvement
        bb_min(
            timeout(label(OrderedTasks), 10, (writeln(timeout),fail)),
            MakeSpan,
            bb_options{strategy:restart}        % faster
%            bb_options{strategy:dichotomic}    % better
        ).

    % Assign machines and earliest feasible start time
    label(Tasks) :-
        ( foreach(task{start:Start,mach:Machine},Tasks), count(_I,1,_) do
            indomain(Machine, random),
            once indomain(Start, min)       % incomplete labeling here!
        ).


% Collect instance data: Resources, Machines, Tasks
get_data(Instance, Resources, Machines, Tasks) :-
        compile(Instance),
        findall(res{name:N,cap:C}, resource(N,C), Resources),
        findall(mach{name:N}, embedded_board(N), Machines),
        ( foreach(mach{id:I},Machines), count(I,1,NMach) do true ),
        findall(task{name:N,dur:D,machs:Ms,nmach:NM,res:Rs,nres:NR}, (
                test(N,D,MNames,Rs,_,_),
                length(Rs, NR),
                ( MNames==[] ->
                    NM = NMach, Ms = 1..NMach
                ;
                    length(MNames, NM),
                    ( foreach(MName,MNames), foreach(M,Ms), param(Machines) do
                        memberchk(mach{name:MName,id:M}, Machines)
                    )
                )
            ), Tasks).