Model.AddConstrs does not work?
AnsweredHi,
Anyone happens to know what is wrong here? I'm a beginner, tried to debug but no success :(.
This is very similar to the example Workfoce3.py with a small modification in Objective function, but somehow the constraint shiftRequirements does not work: the problem has an optimal solution but the sum of each column of the solution x is not equal to the ShiftRequirements[s] value (which is all 1 here).
n_worker = 5
n_time_interval = 14
n_minimum_shift = [2.5] * n_worker
S = [1.] * n_time_interval
Workers = [str(i)+"_T" for i in range(n_worker)]
Shifts = [str(i)+"_S" for i in range(n_time_interval)]
ShiftRequirements = {s: S[i] for i, s in enumerate(Shifts)}
WorkerRequirements = {w: n_minimum_shift[i] for i, w in enumerate(Workers)}
A = np.ceil(np.random.rand(n_worker, n_time_interval) * 100)
observability = {(w, s): A[j][i]
for i, s in enumerate(Shifts)
for j, w in enumerate(Workers)}
availability = {(w, s): 0. if A[j][i]==0. else 1.
for i, s in enumerate(Shifts)
for j, w in enumerate(Workers)}
availability_k = gp.tuplelist(availability.keys())
try:
with gp.Model("Assignment") as model:
# x[w][s] == 1 if worker w is assigned to shift s.
x = model.addVars(availability_k, lb=0., ub=availability , name='x')
# Constraint: assign at most ShiftRequirements[s] workers to each shift s
model.addConstrs(
(x.sum('*', s) <= ShiftRequirements[s] for s in Shifts),name='shiftRequirement')
# Constraint: assign at least n_minimum_shift[w] shifts to each worker w
model.addConstrs(
(x.sum(w, '*') >= WorkerRequirements[w] for w in Workers),
name='workerRequirement')
# set objective
model.setObjective(gp.quicksum(observability[w, s]*x[w, s] for w, s in availability.keys()), GRB.MAXIMIZE)
# Save problem
model.write('assignment.lp')
# Optimize
model.optimize()
status = model.Status
if status == GRB.UNBOUNDED:
print('The model cannot be solved because it is unbounded')
sys.exit(0)
if status == GRB.OPTIMAL:
print('The optimal objective is %g' % model.objVal)
vx = model.getAttr('x', x)
sol = np.zeros((len(Workers), len(Shifts)))
for i, w in enumerate(Workers):
for j, s in enumerate(Shifts):
sol[i, j] = vx[w, s]
sys.exit(0)
if status != GRB.INF_OR_UNBD and status != GRB.INFEASIBLE:
print('Optimization was stopped with status %d' % status)
sys.exit(0)
except gp.GurobiError as e:
print('Error code ' + str(e.errno) + ": " + str(e))
except AttributeError as e:
print('Encountered an attribute error: ' + str(e))
finally:
#Safely release memory and/or server side resources consumed by
# the default environment.
gp.disposeDefaultEnv()
an example of sol would be:
array([[1., 0., 1., 1., 0., 1., 1., 0., 1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1., 0., 1., 1., 1., 1., 1.],
[1., 1., 0., 1., 1., 0., 1., 1., 1., 0., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1., 0., 0., 1., 0., 1., 1.],
[1., 0., 1., 0., 1., 1., 0., 0., 1., 1., 1., 0., 1., 1.]])
The sum of each column is not constrained to <= 1.
I"m appreciated any idea to debug.
Thank lots.
Vy
-
Official comment
This post is more than three years old. Some information may not be up to date. For current information, please check the Gurobi Documentation or Knowledge Base. If you need more help, please create a new post in the community forum. Or why not try our AI Gurobot?. -
Hi Vy,
I am not sure how you print your \(\texttt{sol}\) array but the solution of the model you posted does not violate any constraints. You can print the variables participating in each of the \(\leq 1\) constraints via
if status == GRB.OPTIMAL:
print('The optimal objective is %g' % model.objVal)
vx = model.getAttr('x', x)
sol = np.zeros((len(Workers), len(Shifts)))
counter = 0
for v in x:
print("%s: %f"%(x[v].VarName,x[v].X))
counter+=1
if counter == 5:
print("\n")
counter = 0and you can see that all variable groups add up to at most \(1\).
Best regards,
Jaromił0 -
Hi Jaromił,
Thank you very much for your response. I may have misunderstood the example code. Is this true?
x.sum('*', s) = x[0_T,s] + x[1_T,s] + x[2_T,s] + x[3_T,s] + x[4_T,s]So the constraint:
model.addConstrs((x.sum('*', s) <= ShiftRequirements[s] for s in Shifts), name='shiftRequirement')is equivalent to the below set of constraints (This is the constraints I actually want)
x[0_T,0_S] + x[1_T,0_S] + x[2_T,0_S] + x[3_T,0_S] + x[4_T,0_S] < 1
x[0_T,1_S] + x[1_T,1_S] + x[2_T,1_S] + x[3_T,1_S] + x[4_T,1_S] < 1
...
x[0_T,14_S] + x[1_T,14_S] + x[2_T,14_S] + x[3_T,14_S] + x[4_T,14_S] < 1This is what I got from running your code:
Solved in 0 iterations and 0.00 seconds
Optimal objective 1.938000000e+03
The optimal objective is 1938
x[0_T,0_S]: 1.000000
x[1_T,0_S]: 1.000000
x[2_T,0_S]: 1.000000
x[3_T,0_S]: 0.000000
x[4_T,0_S]: 1.000000
x[0_T,1_S]: 1.000000
x[1_T,1_S]: 1.000000
x[2_T,1_S]: 1.000000
x[3_T,1_S]: 1.000000
x[4_T,1_S]: 1.000000
x[0_T,2_S]: 1.000000
x[1_T,2_S]: 1.000000
x[2_T,2_S]: 1.000000
x[3_T,2_S]: 1.000000
x[4_T,2_S]: 1.000000
x[0_T,3_S]: 0.000000
x[1_T,3_S]: 1.000000
x[2_T,3_S]: 1.000000
x[3_T,3_S]: 1.000000
x[4_T,3_S]: 1.000000
x[0_T,4_S]: 0.000000
x[1_T,4_S]: 1.000000
x[2_T,4_S]: 1.000000
x[3_T,4_S]: 0.000000
x[4_T,4_S]: 0.000000
x[0_T,5_S]: 1.000000
x[1_T,5_S]: 1.000000
x[2_T,5_S]: 1.000000
x[3_T,5_S]: 1.000000
x[4_T,5_S]: 1.000000
x[0_T,6_S]: 1.000000
x[1_T,6_S]: 0.000000
x[2_T,6_S]: 1.000000
x[3_T,6_S]: 0.000000
x[4_T,6_S]: 1.000000
...
Optimization was stopped with status 2
Freeing default Gurobi environmentThe sum for each s = 0_S, ..., 14_S, for example,
x[0_T,0_S] + x[1_T,0_S] + x[2_T,0_S] + x[3_T,0_S] + x[4_T,0_S] = 5
is not less than 1 though.
0 -
Hi Vy,
Interesting, I have copy-pasted and execute the code you posted. This is the output I get when I execute your code
Optimize a model with 19 rows, 70 columns and 140 nonzeros
Model fingerprint: 0xd161aaf7
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [5e+00, 1e+02]
Bounds range [1e+00, 1e+00]
RHS range [1e+00, 2e+00]
Presolve time: 0.00s
Presolved: 19 rows, 70 columns, 140 nonzeros
Iteration Objective Primal Inf. Dual Inf. Time
0 1.2260000e+03 3.000000e+00 0.000000e+00 0s
5 1.1880000e+03 0.000000e+00 0.000000e+00 0s
Solved in 5 iterations and 0.00 seconds
Optimal objective 1.188000000e+03
The optimal objective is 1188Could you please check wether the \(\texttt{assignment.lp}\) file you write in your code contains the inequality constraints?
Best regards,
Jaromił0 -
Hi Jaromił,
My apology, the code I ran to get my above output is a bit different from the code I posted above: I took out some code which modified the matrix A because I thought it was unimportant. I ran code I posted above and get the right result, i.e. all constraints are obeyed. And actually it's the removed code that causes the <=1 constraint fails and I'm still not sure why. This is the code which modified A and make the <=1 constraint fails (everything else is the same):
# create a random observation_cond_mat
observation_cond_mat = np.ceil(np.random.rand(n_target, n_time_interval) * 100)
observation_cond_mat -= 20.
observation_cond_mat = observation_cond_mat.clip(0) # replace negative with 0
observation_cond_mat[:, 10] = np.zeros(n_target) # add a col of all zeros.
# remove cols with all zero values and keep track of the indices.
unobservable_time_indice = np.where(~observation_cond_mat.any(axis=0))[0]
observation_cond_mat = np.delete(observation_cond_mat, unobservable_time_indice, axis=1)
# remove corresponding zero cols from Shift array
Shifts = np.delete(Shifts, unobservable_time_indice)
A = observation_cond_mat
#np.ceil(np.random.rand(n_worker, n_time_interval) * 100)When this code is included, the assignment.lp does have the failed constraint (shiftRequirement) but in a form I don't understand (different from the constraint which works- workRequirement)
Subject To
shiftRequirement[0,_,S]: <= 1
shiftRequirement[1,_,S]: <= 1
shiftRequirement[2,_,S]: <= 1
shiftRequirement[3,_,S]: <= 1
shiftRequirement[4,_,S]: <= 1
shiftRequirement[5,_,S]: <= 1
shiftRequirement[6,_,S]: <= 1
shiftRequirement[7,_,S]: <= 1
shiftRequirement[8,_,S]: <= 1
shiftRequirement[9,_,S]: <= 1
shiftRequirement[1,1,_,S]: <= 1
shiftRequirement[1,2,_,S]: <= 1
shiftRequirement[1,3,_,S]: <= 1
workerRequirement[0_T]: x[0_T,0_S] + x[0_T,1_S] + x[0_T,2_S] + x[0_T,3_S] + x[0_T,4_S] + x[0_T,5_S] + x[0_T,6_S] + x[0_T,7_S] + x[0_T,8_S] + x[0_T,9_S] + x[0_T,11_S] + x[0_T,12_S] + x[0_T,13_S] >= 2.50 -
Hi Vy,
The call
Shifts = np.delete(Shifts, unobservable_time_indice)
turns the list \(\texttt{Shifts}\) into a numpy ndarray object. In order to keep \(\texttt{Shifts}\) a list you have to call
Shifts.remove(Shifts[unobservable_time_indice[0]])
This should resolve your issue.
Best regards,
Jaromił0 -
Hi Jaromił,
Thank you so much for your help. This solves the issue. I some how thought an numpy ndarray Shifts was ok.
Best,
Vy
0
Post is closed for comments.
Comments
7 comments