I had to break the constraint into two parts and it worked

m.addConstrs(0 <= f1[i,j,h] for i,j in arcs for h in area) #(4g)
m.addConstrs(f1[i,j,h] <= 200*z[i,j] for i,j in arcs for h in area) #(4g)

I am wondering why this change was made!

• Gurobi Staff

Hi Sagnik,

Adding two-sided constraints was never supported in Gurobi 8.1.1. This is noted in the Gurobi 8.1.1 documentation here:

"Note that double inequality constraints, like 1 <= x + y <= 2 or 1 <= x[i] + y[i] <= 2 for i in range(3) are NOT supported in this API, and will result in one of the inequalities to be silently disregarded, which will result in unexpected behavior."

For example, consider the following code:

m = Model()x = m.addVar(name="x")m.addConstr(1 <= x <= 2, name="twosided")

In Gurobi 8.1.1, this results in the following model (in LP format):

MinimizeSubject To  twosided: x <= 2BoundsEnd

Note the missing constraint. This change of behavior in Gurobi 9.0 simply disallows this syntax, preventing unexpected behavior like this.

In your example, is f1 is a variable? If so, it is nonnegative by default (unless you explicitly set its lower bound), so you may not even need the nonnegativity constraint.

I hope this helps. Thanks!

Eli

thank you so much!

Hello, I am having a similar problem, I know that if we want to write a constraint like this $$LB \leq v \leq UB$$ I have to do it separately so, I wrote them like this:

rxn = ['a','b','c','d','e']LB = [-1000,0,-1000,0,-1000]UB = [1000,1000,1000,1000,1000]reactions = [i for i in range(len(rxn))]m = gp.Model()vo = m.addVars(range(len(rxn),name='v')m.addConstrs((LB[j] <= vo[j] for j in reactions), name='LB')m.addConstrs((vo[j] <= UB[j] for j in reactions), name='UB)

And I get the same error:

---> 16 f.addConstrs((LB[j] <= v[j] for j in reactions), name='LB')
17
18 f.addConstrs((v[j] <= UB[j] for j in reactions), name='UB')

src\gurobipy\tempconstr.pxi in gurobipy.gurobipy.TempConstr.__bool__()

GurobiError: Constraint has no bool value (are you trying "lb <= expr <= ub"?)

Ultimately I want to have a set of constraints like this:

$LB*y_j \leq v_j \leq UB*y_j$ for j in a knockout set and:

$LB \leq v_j \leq UB$ if j not in the knockout set, the easiest way I could think of is:

for j in reactions:    if j in knockout:        m.addConstr((LB[j] * yo[j] <= vo[j]), name='LBy')        m.addConstr((vo[j] <= yo[j] * UB[j]), name='UBy')    else:        m.addConstr((LB[j] <= vo[j]), name='LB')        m.addConstr((vo[j] <= UB[j]), name='UB')

It is worth to mention that $$v_o$$ is a continuous variable and $$y_o$$ is binary, I would appreciate some help with this.

• Gurobi Staff

Which version of Gurobi are you using? The first code snippet is missing a parenthesis in the call to Model.addVars() and an apostrophe in the last line. Otherwise, the code should work as intended. I.e., the snippet

import gurobipy as gprxn = ['a', 'b', 'c', 'd', 'e']LB = [-1000, 0, -1000, 0, -1000]UB = [1000, 1000, 1000, 1000, 1000]reactions = [i for i in range(len(rxn))]m = gp.Model()vo = m.addVars(range(len(rxn)), name='v')m.addConstrs((LB[j] <= vo[j] for j in reactions), name='LB')m.addConstrs((vo[j] <= UB[j] for j in reactions), name='UB')

adds the following constraints to the model:

LB[0]: v[0] >= -1000LB[1]: v[1] >= 0LB[2]: v[2] >= -1000LB[3]: v[3] >= 0LB[4]: v[4] >= -1000UB[0]: v[0] <= 1000UB[1]: v[1] <= 1000UB[2]: v[2] <= 1000UB[3]: v[3] <= 1000UB[4]: v[4] <= 1000

Hello Eli,

I am using 9.1.0 and python 3.7.6

• Gurobi Staff

Can you please try running the code snippet I posted, and also post the output of $$\texttt{gp.gurobi.version()}$$? The error message you posted does not match the code you posted (the error message references a model named $$\texttt{f}$$ instead of $$\texttt{m}$$).

Note that since $$\texttt{LB}$$ and $$\texttt{UB}$$ are bounds on the $$\texttt{vo}$$ variables, you could just assign these bounds in the call to Model.addVars():

vo = m.addVars(range(len(rxn)), lb=LB, ub=UB, name='v')

Of course, this approach won't work for constraints of the form $$\ell y_j \leq v_j \leq u y_j$$, which you're working towards.

Hello Eli,

I ran the code snippet you posted and it gives me the same constraints as you wrote

These are the actual constraints I have written in my model:

f = gp.Model()reactions = [i for i in range(len(rxn))]metabolites = [i for i in range(len(met))]v = f.addVars(range(len(rxn)),obj=c, name='v')f.modelSense = GRB.MAXIMIZEf.addConstrs((gp.quicksum(S[i,j] * v[j] for j in reactions) == b[i] for i in metabolites), name='FBA')f.addConstrs((LB[j] <= v[j] for j in reactions), name='LB')f.addConstrs((v[j] <= UB[j] for j in reactions), name='UB')f.optimize()

Note that: reactions, c, LB and UB are arrays of the same length, and S has a length of (metabolitesXreactions) b and metabolites have the same length

Other than the name I see no difference, I will try to run again my code, I tried with the bounds being declared in m.AddVars but as you point out is not what I am working towards, if I relaxed the model that way it works without any problem

Hello,

I tried by allowing the lb and ub in f.addVars to be from - infinity to infinity and declaring them as continuos, but I still get the same error, I thought maybe it had to do with the values the variables were taking given the default values in the bounds

• Gurobi Staff

Can you please post a complete, self-contained code example that reproduces the issue? I don't see anything wrong with the snippet you posted, but it is missing some definitions. Based on the error message, I would guess that $$\texttt{LB}$$ was at some point overwritten to include a TempConstr object.

Hello Eli,

This is just a brief example, but the error doesn't show, I will go carefully on my model to figure what's causing it

met = ['A','B','C','D','E']metabolites = [ i for i in range(len(met))]reac = ['v1','v2','v3','v4','v5','v6','v7']reactions = [i for i in range(len(reac))]LB =[0,-10,0,0,-10,-10,-10]UB =[10,10,10,10,10,10,10]SM = {(0,0):-1,(0,1):0,(0,2):1,(0,3):0,(0,4):0,(0,5):0,(0,6):0,       (1,0):1,(1,1):-1,(1,2):0,(1,3):-1,(1,4):0,(1,5):0,(1,6):0,       (2,0):0,(2,1):1,(2,2):0,(2,3):0,(2,4):1,(2,5):0,(2,6):0,       (3,0):-1,(3,1):0,(3,2):0,(3,3):0,(3,4):0,(3,5):1,(3,6):0,       (4,0):1,(4,1):0,(4,2):0,(4,3):0,(4,4):0,(4,5):0,(4,6):1}ob = [0,0,0,1,0,0,0]m = gp.Model()v = m.AddVars(range(len(reac)), vtype=GRB.CONTINUOUS, obj=ob, name='v')m.addConstrs((gp.quicksum(SM[i,j]*v[j] for j in reactions) == 0 for ir in metabolites), name='FBA')m.addConstrs((LB[j] <= v[j] for j in reactions), name='LB')m.addConstrs((v[j] <= UB[j] for j in reactions), name='UB)
m.optimize()if m.status == GRB.OPTIMAL:    obj = m.getObjective()    print('Max v4 Flux','->',obj.getValue())    var = m.getAttr('X',m.getVars())    print('------>')    for i in range(len(reac)):        print('flux:',reac[i],'->',var[i])

which produces :

Gurobi Optimizer version 9.1.0 build v9.1.0rc0 (win64)
Thread count: 4 physical cores, 8 logical processors, using up to 8 threadsOptimize a model with 19 rows, 7 columns and 25 nonzerosModel fingerprint: 0x4d86fd76Coefficient statistics:
Matrix range     [1e+00, 1e+00]
Objective range  [1e+00, 1e+00]
Bounds range     [0e+00, 0e+00]
RHS range        [1e+01, 1e+01]Presolve removed 19 rows and 7 columnsPresolve time: 0.01sPresolve: All rows and columns removedIteration    Objective       Primal Inf.    Dual Inf.      Time
0   -0.0000000e+00   0.000000e+00   0.000000e+00      0s

Solved in 0 iterations and 0.01 seconds
Optimal objective -0.000000000e+00
Max v4 Flux -> 0.0
------>
flux: v1 -> 0.0
flux: v2 -> 0.0
flux: v3 -> 0.0
flux: v4 -> 0.0
flux: v5 -> 0.0
flux: v6 -> 0.0
flux: v7 -> 0.0

However, when I declare the bounds and delete the corresponding constraints

m.AddVars(range(len(reac)),lb=UB,ub=UB, vtype=GRB.CONTINUOUS, obj=ob, name='v')

The result is different (and the correct answer)

Gurobi Optimizer version 9.1.0 build v9.1.0rc0 (win64)Thread count: 4 physical cores, 8 logical processors, using up to 8 threadsOptimize a model with 19 rows, 7 columns and 25 nonzerosModel fingerprint: 0xbc50aaa9Coefficient statistics:
Matrix range     [1e+00, 1e+00]
Objective range  [1e+00, 1e+00]
Bounds range     [1e+01, 1e+01]
RHS range        [1e+01, 1e+01]
Presolve removed 19 rows and 7 columns
Presolve time: 0.02s
Presolve: All rows and columns removedIteration    Objective       Primal Inf.    Dual Inf.      Time
0    1.0000000e+01   0.000000e+00   0.000000e+00      0s

Solved in 0 iterations and 0.02 seconds
Optimal objective  1.000000000e+01Max v4 Flux -> 10.0
------>
flux: v1 -> 0.0
flux: v2 -> -10.0
flux: v3 -> 0.0
flux: v4 -> 10.0
flux: v5 -> 10.0
flux: v6 -> 0.0
flux: v7 -> 0.0

Upon further analysis, I realized the difference is that if I declare lb=GRB.INFINITY, ub=GRB.INFINITY in the first model, the result is the same, so I am assuming it has to do with how 'open' the variable is. Still quite confused to be honest but, I appreciate the time you've taken to answer me back.

• Gurobi Staff

By default, the lower bound on variables added with Model.addVar()/Model.addVars() is 0. Since your variables can be negative, you either need to pass $$\texttt{LB}$$ as the $$\texttt{lb}$$ keyword argument of Model.addVars(), or explicitly set a lower bound of $$\texttt{-GRB.CONTINUOUS}$$ and add lower-bound constraints later.

Hello Eli,

Kindly let me know why the following code sets the indicator variable z to zero.

My work is such that if the variable resource_allocation_variables[j,p,r] is one, then the constraint cap_2[1] <= quicksum(DR_dicts_using_user_BS_BBU_pair_key[(j,p,r)]*resource_allocation_variables[j,p,r] for r in MBS_BBU_list) <= capacity_vector[1] should be satisfied, for a given user p attached to node j.

So I created indicator variable z for this conditional statement. Now I am wondering why no user p is allocated any resource r on the node j, yet the resources are available.

z = f.addVars(DR_dicts_using_user_BS_pair_key.keys(), vtype=GRB.BINARY, name="z")
f.addConstrs(((z[j,p]==1)>>(cap_2[1] <= quicksum(DR_dicts_using_user_BS_BBU_pair_key[(j,p,r)]*resource_allocation_variables[j,p,r] for r in MBS_BBU_list)) for j in MBS_list for p in list_R_users), name='MBS_uRLLC_QoS_1')f.addConstrs(((z[j,p]==1)>>(quicksum(DR_dicts_using_user_BS_BBU_pair_key[(j,p,r)]*resource_allocation_variables[j,p,r] for r in MBS_BBU_list) <= capacity_vector[1]) for j in MBS_list for p in list_R_users), name='MBS_uRLLC_QoS_1')f.addConstrs(((z[j,p]==0) >> (quicksum(resource_allocation_variables[j,p,r] for r in MBS_BBU_list) <=0) for j in MBS_list for p in list_R_users), name='MBS_uRLLC_QoS_2')f.addConstrs(((z[j+Num_BS,p]==1)>>(cap_2[1] <= quicksum(DR_dicts_using_user_BS_BBU_pair_key[(j+Num_BS,p,r)]*resource_allocation_variables[j+Num_BS,p,r] for r in UAV_BBU_list)) for j in UAV_list for p in list_R_users), name='UAV_uRLLC_QoS_1')f.addConstrs(((z[j+Num_BS,p]==1)>>(quicksum(DR_dicts_using_user_BS_BBU_pair_key[(j+Num_BS,p,r)]*resource_allocation_variables[j+Num_BS,p,r] for r in UAV_BBU_list) <= capacity_vector[1]) for j in UAV_list for p in list_R_users), name='UAV_uRLLC_QoS_1')f.addConstrs(((z[j+Num_BS,p]==0) >> (quicksum(resource_allocation_variables[j+Num_BS,p,r] for r in UAV_BBU_list) <=0) for j in UAV_list for p in list_R_users), name='UAV_uRLLC_QoS_2')f.addConstrs(((z[j+Num_BS+Num_UAVS,p]==1)>>(cap_2[1] <= quicksum(DR_dicts_using_user_BS_BBU_pair_key[(j+Num_BS+Num_UAVS,p,r)]*resource_allocation_variables[j+Num_BS+Num_UAVS,p,r] for r in HAP_BBU_list)) for j in HAP_list for p in list_R_users), name='HAP_uRLLC_QoS_1')f.addConstrs(((z[j+Num_BS+Num_UAVS,p]==1)>>(quicksum(DR_dicts_using_user_BS_BBU_pair_key[(j+Num_BS+Num_UAVS,p,r)]*resource_allocation_variables[j+Num_BS+Num_UAVS,p,r] for r in HAP_BBU_list) <= capacity_vector[1]) for j in HAP_list for p in list_R_users), name='HAP_uRLLC_QoS_1')f.addConstrs(((z[j+Num_BS+Num_UAVS,p]==0) >> (quicksum(resource_allocation_variables[j+Num_BS+Num_UAVS,p,r] for r in HAP_BBU_list) <=0) for j in HAP_list for p in list_R_users), name='HAP_uRLLC_QoS_2')
• Gurobi Staff

if the variable resource_allocation_variables[j,p,r] is one, then the constraint [...] should be satisfied, for a given user p attached to node j

Do you mean the constraint should be satisfied if the variable $$\texttt{z[j, p]}$$ equals $$1$$? This is what your code currently models.

It's hard to say exactly what's wrong, because the code you provided does not run on its own. Due to your third constraint family, it is expected that if $$\texttt{z[j, p]}$$ equals $$0$$, then all of the corresponding resource allocation variables also equal $$0$$.

Below are a few ideas that may help:

1. You can use Model.write() to write out an LP model file, then visually inspect the constraints for correctness. Before you do this, I recommend changing your constraint names to be unique. Your first two constraint families both use the name $$\texttt{MBS_uRLLC_QoS_1}$$. This will result in (e.g.) two constraints named $$\texttt{MBS_uRLLC_QoS_1[0,0]}$$, two constraints named $$\texttt{MBS_uRLLC_QoS_1[0,1]}$$, etc.
2. If there is a specific $$\texttt{z[j, p]}$$ variable you think should equal $$1$$ in the optimal solution, you could try fixing that variable to $$1$$ and solving the model again. The resulting model should either (i) be infeasible, or (ii) admit an optimal objective value that is no less than (for a minimization problem) the optimal objective value obtained by leaving that $$\texttt{z[j, p]}$$ variable unfixed.
3. Semi-continuous variables are variables that must assume a value of $$0$$, or a value between the specified lower and upper bounds. You could remove the binary indicator variables $$\texttt{z}$$ and instead introduce semi-continuous variables equal to the weighted resource allocation for each user and node. The code to do so would look something like this:
y = f.addVars(    DR_dicts_using_user_BS_pair_key.keys(),    lb=cap_2[1],    ub=capacity_vector[1],    vtype=GRB.SEMICONT,    name="y")m.addConstrs(    (        y[j, p]        == quicksum(            DR_dicts_using_user_BS_BBU_pair_key[(j, p, r)]            * resource_allocation_variables[j, p, r]            for r in MBS_BBU_list        )        for j in MBS_list        for p in list_R_users    ),    name="MBS_uRLLC_QoS")

If these ideas do not help, please post a minimal, self-contained code example that reproduces the behavior you describe.

Hello Eli,

I've same problem with error "Constraint has no bool value (are you trying "lb <= expr <= ub"?)". I worked in Gurobi 10.0.3 and python 3.11.4

This is my main code:

import pandas as pdimport gurobipy as gpimport timeitimport numpy as npdf = pd.read_excel('3D_data_IJRT.xlsx')J = [int(value) for value in df['J'].unique().tolist()]T = [int(value) for value in df['P'].unique().tolist()]max_T = max(T)I = [int(value) for value in df.columns[2:].tolist()]  # Assuming C0, C1, C3, C4 are the column headersR = []# Define the rank_value functiondef rank_value(value):    if value <= 8:        return 1    elif value <= 15:        return 2    else:        return 3for j in J:    for t in T:        filtered_data = df[(df['J'] == j) & (df['P'] == t)][I].values.flatten()  # Get values as a flattened NumPy array        ranked_data = [rank_value(value) for value in filtered_data]  # Apply rank_value to each element        R.extend(ranked_data)
D = {}for i in I:    D[i] = {}    for t in T:        D[i][t] = df[df['P'] == t][i].count()alpha = {}for i in I:    alpha[i] = {}    for t in T:        alpha[i][t] = D[i][t] * 1/3        S = {}for i in I:    S[i] = {}    for j in J:        S[i][j] = {}        for t in T:            S[i][j][t] = (df[(df['J'] == j) & (df['P'] == t)][i].values) + 2 + 10 + 5
This is my Decision Variable code:def decisionVariables(M, gp, I, J, T, R, x, y, d, delta, theta, ar):     for j in J:        for t in T:            y[j, t] = M.addVar(vtype=gp.GRB.INTEGER, name=f"y_{j}_{t}")    M.update()    for i in I:        for j in J:            for r in R:                for t in T:                   d[i, j, r, t] = M.addVar(vtype=gp.GRB.CONTINUOUS, name=f"d_{i}_{j}_{r}_{t}")

And this is my constraint code:

 for j in  (J):        for t in  (T):         result = gp.quicksum(alpha[i][t] * S[i][j][t] * d[i, j, r, t] for i in (I) for r in (R))         M.addConstr(result <= (150 * y[j, t]))The Error:
constraints.constraints(M, gp, x, y, I, J, T, R, Zmax, Atotal, Amax, delta, d, theta, alpha, ar, max_T, S, L)  File D:\Laela Widiyaningsih\py\constraints.py:67 in constraints    M.addConstr(result <= (150 * y[j, t]))  File src\gurobipy\tempconstr.pxi:57 in gurobipy.TempConstr.__bool__GurobiError: Constraint has no bool value (are you trying "lb <= expr <= ub"?)
• Gurobi Staff

Unfortunately, I can't say for certain what's causing the issue, because the code is not self-contained. Can you please (i) post a link to the $$\texttt{3D_data_IJRT.xlsx}$$ workbook, and (ii) modify the code so it will run without any modifications?