I thought I was close, but I am having issues creating contiguous sets for my solution, it's like the approach and I may need to do some sort combinatorics first, isn't of constraints.
I have a labor demand curve over some period of time, lets say 12 hours and the curve goes from 6am to 6pm
The sum of the columns must be == to the total labor for that hour, defined as the positions in s[i]
I am having trouble constraining the problem to output contiguous spans/block of time to represent valid shifts, i.e. shift 1 and 4 are valid, shift 2 and 3 are not.
I can get the continuous blocks to work when I use labor demand needs (num_hours) that are either 4,5,6,7,8, but then it won't create a solution, when there is clearly a way to make a schedule that would work.
I have been playing with an iteration on blocks of time and trying to create combinations of sets of variables to be equal to the constrain of 4,5,6,7,8 or 0. Not 100% sure I am approaching this in the correct way. I also need a way to add breaks into the constraints as well, but seems like one should walk before running.
from __future__ import print_function
from ortools.constraint_solver import pywrapcp
from ortools.constraint_solver import solver_parameters_pb2
from ortools.linear_solver import pywraplp
def main():
solver = pywrapcp.Solver("schedule_shifts")
num_required_workers = 15
num_hours = 8
num_shifts = 15
shifts = {}
y={}
for j in range(num_required_workers):
for i in range(num_hours):
shifts[(j, i)] = solver.IntVar(0, 1, "shifts(%i,%i)" % (j, i))
shifts_flat = [shifts[(j, i)] for j in range(num_required_workers) for i in range(num_hours)]
for j in range(num_required_workers):
y[j] = solver.IntVar(0, 1, "shift2(%i)" % (j))
import numpy.random as nprnd
# s = nprnd.randint(5,8, size=num_hours)
# print(s)
# print(sum(s))
s =[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
for i in range(num_hours):
solver.Add(solver.Sum([shifts[(j,i)] for j in range(num_required_workers)]) == s[i])
print(s[i],i)
for j in range(num_required_workers):
solver.Add(solver.Sum([shifts[(j,i)] for i in range(num_hours)]) <= 8 * y[j])
solver.Add(solver.Sum([shifts[(j,i)] for i in range(num_hours)]) >= 4 * y[j])
solver.Add(y[j]<=1)
# flatys = [y[j] for j in range(num_required_workers) ]
z={}
for j in range(num_required_workers):
for i in range(num_hours-3):
z[(j,i)] = solver.IntVar(0, 1, "shift3(%i,%i)" % (j,i))
solver.Add(z[(j,i)]<=1)
solver.Add(solver.Sum([shifts[(j, i+k)] for k in range(4)]) == 4 * z[(j,i)])
a ={}
for j in range(num_required_workers):
for i in range(num_hours-4):
a[(j,i)] = solver.IntVar(0, 1, "shift4(%i,%i)" % (j,i))
solver.Add(a[(j,i)]<=1)
solver.Add(solver.Sum([shifts[(j, i+k)] for k in range(5)]) == 5 * a[(j,i)])
b= {}
for j in range(num_required_workers):
for i in range(num_hours-5):
b[(j,i)] = solver.IntVar(0, 1, "shift5(%i,%i)" % (j,i))
solver.Add(b[(j,i)]<=1)
solver.Add(solver.Sum([shifts[(j, i+k)] for k in range(6)]) == 6 * b[(j,i)])
c={}
for j in range(num_required_workers):
for i in range(num_hours-6):
c[(j,i)] = solver.IntVar(0, 1, "shift6(%i,%i)" % (j,i))
solver.Add(c[(j,i)]<=1)
solver.Add(solver.Sum([shifts[(j, i+k)] for k in range(7)]) == 7 * c[(j,i)])
d={}
for j in range(num_required_workers):
for i in range(num_hours-7):
d[(j,i)] = solver.IntVar(0, 1, "shift7(%i,%i)" % (j,i))
solver.Add(d[(j,i)]<=1)
solver.Add(solver.Sum([shifts[(j, i+k)] for k in range(8)]) == 8 * d[(j,i)])
db = solver.Phase(shifts_flat, solver.CHOOSE_FIRST_UNBOUND,
solver.ASSIGN_MIN_VALUE)
solution = solver.Assignment()
collector = solver.AllSolutionCollector(solution)
import time
start2 = time.time()
solver.NewSearch(db,[collector])
num_solutions = 0
while solver.NextSolution():
for j in range(num_required_workers):
print("x{}: ".format(j), [shifts[(j, i)].Value() for i in range(num_hours)])
# print("num_coins:", totalhours.Value())
print(sum([shifts_flat[i].Value() for i in range(len(shifts_flat))]))
num_solutions += 1
if num_solutions == 5:
solver.EndSearch()
break
finish = time.time()
print(finish-start2)
print("num_solutions:", num_solutions)
print("failures:", solver.Failures())
print("branches:", solver.Branches())
if __name__ == "__main__":
main()