I'm using the cp_solver to solve a fairly standard assignment problem: Minimize cost for assigning N tasks to M workers. In my setup workers can take multiple tasks, but each task can only be assigned to one worker. So far, so good.
But I want to also add a constraint (or, alternatively, modify the objective), to minimize the overall number of workers employed to do the tasks. One approach I've tried is using a constraint that ensures each worker gets either 0, or > 3 tasks. I've used the following code to do this:
for i in range(num_workers):
enough_work = model.NewBoolVar("%i has enough work" % i)
worker_off = model.NewBoolVar("worker %i off" % i)
model.Add(sum( x[j][i] for j in range(num_tasks)) >= 3).OnlyEnforceIf(enough_work)
model.Add(sum( x[j][i] for j in range(num_tasks)) < 3).OnlyEnforceIf(enough_work.Not())
model.Add(sum( x[j][i] for j in range(num_tasks)) <= 0).OnlyEnforceIf(worker_off)
model.Add(sum( x[j][i] for j in range(num_tasks)) > 0).OnlyEnforceIf(worker_off.Not())
model.AddBoolOr((enough_work, worker_off))
This works, but now the problem takes hours to solve, rather than milliseconds. :( In my case I need the problem solution in < 2s runtime.
Another solution I've been considering is to add the number of employed workers into the objective function. But to do that I'd need a function that could count non-zero elements of X[*][j] and I don't see any functions that'd support that.
What's the best way to add this type of constraint / objective? I'm open to relaxations (solution doesn't have to be exact), but I do need to keep it within < 2s runtime.
Thanks,
Aish