0

In a simple range I try to get the amount of successive assignments for a variable. The values should be between 6-12 or should be 0. For example in the case a hospital has 24 shifts and an employee should work between 6 and 12 hours or not at all.

# Build shifts
shifts = {}
for n in all_nurses:
    for d in all_days:
        for s in all_shifts:
            shifts[(n, d, s)] = model.NewBoolVar('shift_n%id%is%i' % (n, d, s))

# Count successive occurrences
for e_count in all_nurses:
    s_count = 0
    while s_count < len(all_shifts):
        model.Add(sum(shifts[e_count, s_count] for s in range(e_count, e_count + 6 == 6) #min
        model.Add(sum(shifts[e_count, s_count] for s in range(e_count, e_count + 12 <= 12) #min

Unfortunately this doesn't work since it increases the value with only one, what would be the best approach to check if how many hours have been assigned and increase s_count with that value?

Jan Koekepan
  • 81
  • 1
  • 9
  • If thats really a hard-constraint, consider formulating it as DFA using [AddAutomaton](https://developers.google.com/optimization/reference/python/sat/python/cp_model#addautomaton). There should be some examples in the repo. Some motivation/background [is available here](https://sofdem.github.io/art/demassey14cmp.pdf). – sascha May 04 '22 at 09:16
  • @sascha thanks for the suggestion and docs! I suppose there should be an easier way to solve this supposedly easy task? Is there really no way to count the sum and increase the `s_count` with that value? – Jan Koekepan May 04 '22 at 13:43
  • 2
    CP-SAT natively supports sum(bool_vars) in [0] U [6..12]. – Laurent Perron May 04 '22 at 15:27

1 Answers1

3

If you just want to constrain the sum, you should use this method

model.AddLinearExpressionInDomain(sum(bool_vars), cp_model.Domain.FromIntervals([0, 0], [6, 12]))

If you want to constrain the length of a sequence, you should look at the shift_scheduling example

In particular, the soft sequence constraint. The idea is the following, for every starting point, you want to forbid 010, 0110, 01110, ..., 0111110 and 01111111111110 (0110 means work[start] is false, work[start + 1] is true, work[start + 2] is true, work[start + 3] is false.

To forbid a sequence, just add a nogood, that is a clause (or AddBoolOr containing the negation of the pattern.

in my example bool_or(work[start], work[start + 1].Not(), work[start + 2].Not(), work[start + 3]).

Loop over all starting points and all patterns. And pay attention to the boundary conditions.

Laurent Perron
  • 8,594
  • 1
  • 8
  • 22
  • `for e_count in all_nurses: shift_min_max_length = cp_model.Domain.FromIntervals([[0, 0], [6, 12]]) model.AddLinearExpressionInDomain(sum(shifts[e_count, s_count] for s_count in all_shifts, shift_min_max_length)` Gives no errors, but also no solutions although there should clearly be solutions. Should the `sum()` be changed? – Jan Koekepan May 04 '22 at 19:53
  • ')' in the wrong place – Laurent Perron May 05 '22 at 03:15
  • That is a typo. The problem is that it counts all the shifts, and not the successive shifts. Ofcourse a nurse is allowed to work more then 12 hours, but not consecutively. – Jan Koekepan May 05 '22 at 08:56