0

I am trying to update the rhs of a constraint iteratively under a while loop in docplex, however, it does not work properly. As I analyze the output text file, while some constraints are updated as I want, others are not.

z_bar is a list consists of y+1 elements and is updated at each iteration. Constraint set I want to change the RHS of consists of (x+1)*(y+1) constraints. z_bar is related to the j indice, however since each constraint involves i and j indices, I have to update all. What do you think I am doing wrong?

Original constraint set:

for i in range(1, x + 1):
    for j in range(1, y + 1):
        sub_cbd.add_constraint(x_cbd[i, j] <= z_bar[j], ctname='constraint_name{0}{1}'.format(i, j))

Updating constraint set rhs attempt:

for i in range(1, x + 1):
    for j in range(1, y + 1):
        sub_cbd.get_constraint_by_name('constraint_name{0}{1}'.format(i, j)).rhs = z_bar[j]

For the updated z_bar: [0, 0, 0, 0, 1, 1, 0...0], 15 elements in total, two 1s and 13 0s.

How it looks now:

constraint11: x_1_1 <= 0
 constraint12: x_1_2 <= 0
 constraint13: x_1_3 <= 0
 constraint14: x_1_4 <= 0
 constraint15: x_1_5 <= 1
 constraint16: x_1_6 <= 1
 constraint17: x_1_7 <= 0
 constraint18: x_1_8 <= 0
 constraint19: x_1_9 <= 0
 constraint110: x_1_10 <= 0
 constraint111: x_1_11 <= 1
 constraint112: x_1_12 <= 1
 constraint113: x_1_13 <= 1
 constraint114: x_1_14 <= 1
 constraint115: x_1_15 <= 1
 constraint21: x_2_1 <= 0
 constraint22: x_2_2 <= 0
 constraint23: x_2_3 <= 0
 constraint24: x_2_4 <= 0
 constraint25: x_2_5 <= 1
 constraint26: x_2_6 <= 1
 constraint27: x_2_7 <= 0
 constraint28: x_2_8 <= 0
 constraint29: x_2_9 <= 0
 constraint210: x_2_10 <= 0
 constraint211: x_2_11 <= 1
 constraint212: x_2_12 <= 1
 constraint213: x_2_13 <= 1
 constraint214: x_2_14 <= 1
 constraint215: x_2_15 <= 1

How it should look:

constraint11: x_1_1 <= 0
 constraint12: x_1_2 <= 0
 constraint13: x_1_3 <= 0
 constraint14: x_1_4 <= 0
 constraint15: x_1_5 <= 1
 constraint16: x_1_6 <= 1
 constraint17: x_1_7 <= 0
 constraint18: x_1_8 <= 0
 constraint19: x_1_9 <= 0
 constraint110: x_1_10 <= 0
 constraint111: x_1_11 <= 0
 constraint112: x_1_12 <= 0
 constraint113: x_1_13 <= 0
 constraint114: x_1_14 <= 0
 constraint115: x_1_15 <= 0
 constraint21: x_2_1 <= 0
 constraint22: x_2_2 <= 0
 constraint23: x_2_3 <= 0
 constraint24: x_2_4 <= 0
 constraint25: x_2_5 <= 1
 constraint26: x_2_6 <= 1
 constraint27: x_2_7 <= 0
 constraint28: x_2_8 <= 0
 constraint29: x_2_9 <= 0
 constraint210: x_2_10 <= 0
 constraint211: x_2_11 <= 0
 constraint212: x_2_12 <= 0
 constraint213: x_2_13 <= 0
 constraint214: x_2_14 <= 0
 constraint215: x_2_15 <= 0
diabolik
  • 55
  • 6

2 Answers2

1

Full working example in https://github.com/AlexFleischerParis/zoodocplex/blob/master/zooincremental.py

from docplex.mp.model import Model

# original model

mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
mdl.add_constraint(nbbus40*40 + nbbus30*30 >= 300, 'kids')
mdl.minimize(nbbus40*500 + nbbus30*400)

mdl.solve()

for v in mdl.iter_integer_vars():
    print(v," = ",v.solution_value)

#now 350 kids instead of 300

print()
print("now 350 kids instead of 300")    
    
mdl.get_constraint_by_name("kids").rhs=350;
mdl.solve()

for v in mdl.iter_integer_vars():
    print(v," = ",v.solution_value)

# no more than 4 buses 40 seats

print()
print("no more than 4 buses 40 seats")


mdl.get_var_by_name("nbBus40").ub=4
mdl.solve()

for v in mdl.iter_integer_vars():
    print(v," = ",v.solution_value)

#change the objective so that cost for 40 seats is 450
#and remove the limit on the number of buses 40 seats

print()
print("change the objective so that cost for 40 seats is 450")
print("and remove the limit on the number of buses 40 seats  ")  
    
mdl.get_var_by_name("nbBus40").ub=1000
mdl.set_objective("min",nbbus40*450 + nbbus30*400);
mdl.solve()

for v in mdl.iter_integer_vars():
    print(v," = ",v.solution_value)

Now let me change this into a loop:

from docplex.mp.model import Model

# original model

mdl = Model(name='buses')
nbbus40 = mdl.integer_var(name='nbBus40')
nbbus30 = mdl.integer_var(name='nbBus30')
for i in range(0,10):
   mdl.add_constraint(nbbus40*40 + nbbus30*30*i >= 300+i,'kids'+str(i))
   
mdl.minimize(nbbus40*500 + nbbus30*400)

mdl.solve()

for v in mdl.iter_integer_vars():
    print(v," = ",v.solution_value)

#now 350 kids instead of 300

for i in range(0,10):    
   mdl.get_constraint_by_name("kids"+str(i)).rhs=350+i;
   mdl.export("zoo.lp")
mdl.solve()

and then in zoo.lp I get

Minimize
 obj: 500 nbBus40 + 400 nbBus30
Subject To
 kids0: 40 nbBus40 >= 350
 kids1: 40 nbBus40 + 30 nbBus30 >= 351
 kids2: 40 nbBus40 + 60 nbBus30 >= 352
 kids3: 40 nbBus40 + 90 nbBus30 >= 353
 kids4: 40 nbBus40 + 120 nbBus30 >= 354
 kids5: 40 nbBus40 + 150 nbBus30 >= 355
 kids6: 40 nbBus40 + 180 nbBus30 >= 356
 kids7: 40 nbBus40 + 210 nbBus30 >= 357
 kids8: 40 nbBus40 + 240 nbBus30 >= 358
 kids9: 40 nbBus40 + 270 nbBus30 >= 359

Bounds

Generals
 nbBus40 nbBus30
End
Alex Fleischer
  • 9,276
  • 2
  • 12
  • 15
  • Thanks for the comment! This is very interesting... It still does not work. I debugged the problem and found that somehow it just does not update some indices of z_bar. For instance, z_bar is a list [1,1,1,0,0,0,..0]. 15 elements in total, first 3 elements are 1, rest are 0. When I debugged the for loop, I found that, it updates the first 11 elements, but does not change the rest for j indice. I added how it looks and how it should look to the main post. Thanks again. – diabolik Feb 06 '23 at 12:58
0

I tried with the following code, which seems to work. Please can you update it so that it shows the issue.

N = 2
M = 15

m = Model()

xs = m.continuous_var_matrix(N, M, name=lambda ij: f"x{ij[0]+1}{ij[1]+1}")
right = [33] * M

for i in range(N):
    for j in range(M):
        m.add_constraint(xs[i, j ] <= right[j], f"K{i+1}{j+1}")

print(m.lp_string)

zbar = [0] * M

zbar[1] = 1
zbar[2] = 2

for i in range(N):
    for j in range(M):
        ct = m.get_constraint_by_name(f"K{i+1}{j+1}")
        assert ct is not None
        ct.rhs = zbar[j]

print(m.lp_string)

As a side remark, I would have used separators in the variable names, to avoid name duplicates, though this does not seem to happen in your case. However, for a square matrix x111 is ambiguous, can be (1,11) or (11,1). Note that DOcplex can generate non_ambiguous names for you; if I had passed x to continuous_var_matrix, DOcplex will have generated names such as x_i_j for all (i,j) pairs.

Philippe Couronne
  • 826
  • 1
  • 5
  • 6