eyeprolog

Resource allocation with integer constraints

Which worker should do which job so the team finishes fast?

Three workers, three jobs, and a duration table per job. Integer constraints from library(clpz) link who does what to how long it takes, and keep only assignments that finish within 12 time units.

Run it in the playground · Source


The question

Jobs A, B, and C each get a different worker numbered 1 to 3. Job A takes 5, 3, or 4 time units depending on the worker, job B 4, 6, or 2, job C 7, 2, or 5. A side condition ties the worker numbers together, job A may not be faster than job B, and the total must be at most 12. Which assignments qualify? A second clause shows how to read back what the solver knows about a variable’s domain, its set of possible values.


The allocation model

clpz_example(allocation, plan([A, B, C], durations([DA, DB, DC]), Total)) :-
  [A, B, C] ins 1..3,
  all_distinct([A, B, C]),
  scalar_product([1, 2, 3], [A, B, C], #=, 11),
  element(A, [5, 3, 4], DA),
  element(B, [4, 6, 2], DB),
  element(C, [7, 2, 5], DC),
  chain(#>=, [DA, DB]),
  sum([DA, DB, DC], #=, Total),
  Fast in 0..1,
  Fast #<==> Total #=< 12,
  Fast #= 1,
  labeling([ff, down], [A, B, C, Fast]).

element(A, [5, 3, 4], DA) makes DA the A-th entry of the table. scalar_product/4 requires A + 2B + 3C = 11. Fast #<==> Total #=< 12 reifies the deadline: Fast is 1 exactly when the total is at most 12.


Domain reflection

clpz_example(domain, domain(Infimum, Supremum, Size, Domain)) :-
  X in 2..4 \/ 7,
  fd_var(X),
  fd_inf(X, Infimum),
  fd_sup(X, Supremum),
  fd_size(X, Size),
  fd_dom(X, Domain).

X may be 2, 3, 4, or 7. The fd_* predicates report the smallest and largest value, the number of values, and the domain itself.


The answer

eyeprolog examples/clpz-resource-allocation.pl
clpz_example(allocation, plan([2, 3, 1], durations([3, 2, 7]), 12)).
clpz_example(allocation, plan([3, 1, 2], durations([4, 4, 2]), 10)).
clpz_example(domain, domain(2, 7, 4, 2..4 \/ 7)).

Two assignments meet the deadline, with totals 12 and 10. The domain of X runs from 2 to 7 and holds 4 values.


The reason

step(clpz_example(allocation, plan([2, 3, 1], durations([3, 2, 7]), 12)),
     rule(1),
     ['A' = 2, 'B' = 3, 'C' = 1, 'DA' = 3, 'DB' = 2, 'DC' = 7, 'Total' = 12, 'Fast' = 1],
     [[2, 3, 1] ins 1..3,
      …
      element(2, [5, 3, 4], 3),
      …
      1 #<==> 12 #=< 12,
      …]).

The first plan holds by the allocation rule with every value filled in: worker 2 on job A takes 3 units (the second table entry), and the total of 12 meets the deadline, so Fast is 1.


The check

condition('C5', re_decision, ok, 25).
obligation(builtin, stateful, fd_inf(A, 2)).
obligation(builtin, stateful, fd_dom(A, 2..4 \/ 7)).
trusted(5).
verdict(checked_with_obligations).

All allocation steps are verified, recomputed or composed. The five fd_* steps read the solver’s internal state for a variable that is never bound, so the checker cannot recompute them. It keeps them as obligations: the domain answer is as good as the constraint store that produced it.


Try it

Learn more in The Art of EyeProlog, Chapter 24.