Home > front end >  How do I add Gurobi constraints and variables from lists of strings in an automated way?
How do I add Gurobi constraints and variables from lists of strings in an automated way?

Time:01-21

Objective: Add variables and constraints in a loop to a Gurobi model from a list of variables and a list of constraints each of varying lengths where each variable and constraint is stored as a string

Problem: Gurobi only accepts variables and constraints in the Gurobi linear expression type. I need to do this in an automated way because the list of constraints might be very long. So somehow I need to convert those strings to a Gurobi LinExpr type.

Data: I have a list of variables as in this example:

variables=["y1","y2","y3","y4","y5","y6","y7"]

and I have a list of constraints like that:

['y6 y7-d*y1>=0', '1*y2 1*y3-1*u1*d-y6-y7 d*y1>=0', '1*y4 1*y5-u2*1*d-y6-y7 d*y1>=0', '1*x1 1*x2-1*d-1*y2-1*y3 1*u1*d-1*y4-1*y5 u2*1*d y6 y7-d*y1>=0']

Desired Solution:

I want to add those variables to my Gurobi model as in this example:

y1 = model.addVar(vtype=GRB.CONTINUOUS, name = "y1", lb=0)
y2 = model.addVar(vtype=GRB.CONTINUOUS, name = "y2", lb=0)
y3 = model.addVar(vtype=GRB.CONTINUOUS, name = "y3", lb=0)
y4 = model.addVar(vtype=GRB.CONTINUOUS, name = "y4", lb=0)
y5 = model.addVar(vtype=GRB.CONTINUOUS, name = "y5", lb=0)
y6 = model.addVar(vtype=GRB.CONTINUOUS, name = "y6", lb=0)
y7 = model.addVar(vtype=GRB.CONTINUOUS, name = "y7", lb=0)

and the constraints like that:

c1 = model.addConstr(y1 y2-d*y1>=0,"c1")
c2 = model.addConstr(1*y2 1*y3-1*y5*d-y6-y7 d*y1>=0,"c2")
c3 = model.addConstr(1*y4 1*y5-u2*1*d-y6-y7 d*y1>=0,"c3")
c4 = model.addConstr(1*y1 1*y2-1*d-1*y2-1*y3 1*u1*d-1*y4-1*y5 u2*1*d y6 y7-d*y1>=0,"c4")

My Try:

I tried to store the data in a dictionary and use the update function to store the variables and constraints like this:

y_dict={}
for y_variable in range(y_count-1):
        y_dict['y{}'.format(y_variable 1)]=model.addVar(vtype=GRB.CONTINUOUS, name = "y{}".format(y_variable 1), lb=0)
locals().update(c_dict)

c_dict={}
for idx, constraint in enumerate(list_of_constraints):
    c_dict['c{}'.format(idx 1)]=model.addConstr(eval(constraint),"c{}".format(idx 1))
locals().update(c_dict)

But, the error I get when I set the objective is

GurobiError: Variable not in model

Is there any way to convert strings to the Gurobi Linear Expression type?

CodePudding user response:

Just for the sake of completeness, another approach worth mentioning would be to transform all variables inside the constraint strings into index notation, e.g. y1 becomes y[1]. Using regular expressions, this is straightforward:

import re

def clean_constr_str(s, var_names):
    for var_name in var_names:
        s = re.sub(r"("   var_name   r")(\d*)", r"\1[\2]", s)
    return s

Here, s is the constraint string and var_names is an iterable containing all variable names. Given that you already know the variable names, this allows you to add all variables at once as a tupledict and then parse the constraints without modifying the logical symbol table:

import gurobipy as gp

variables = ["y1","y2","y3","y4","y5","y6","y7"]

list_of_constraints = [
    'y6 y7-d*y1>=0', 
    '1*y2 1*y3-1*u1*d-y6-y7 d*y1>=0', 
    '1*y4 1*y5-u2*1*d-y6-y7 d*y1>=0', 
    '1*x1 1*x2-1*d-1*y2-1*y3 1*u1*d-1*y4-1*y5 u2*1*d y6 y7-d*y1>=0'
]

model = gp.Model()

# Add all variables
y = model.addVars(range(1, len(variables) 1), vtype="C", name="y")
d = model.addVar(vtype="C", name="d")
u = model.addVars(range(1, 3), vtype="C", name="u")
x = model.addVars(range(1, 3), vtype="C", name="x")

# add the constraints
c = {}
for i, constraint in enumerate(list_of_constraints, 1):
    con = eval(clean_constr_str(constraint, ["y", "u", "x"]))
    c[i] = model.addConstr(con, name=f"c{i}")

model.update()
print(model.display())

yields

Minimize
  <gurobi.LinExpr: 0.0>
Subject To
  c1: <gurobi.QuadExpr: y[6]   y[7]   [ -1.0 y[1] * d ]> >= 0
c2: <gurobi.QuadExpr: y[2]   y[3]   -1.0 y[6]   -1.0 y[7]   [ y[1] * d   -1.0 d * u[1]
 ]> >= 0
c3: <gurobi.QuadExpr: y[4]   y[5]   -1.0 y[6]   -1.0 y[7]   [ y[1] * d   -1.0 d * u[2]
 ]> >= 0
c4: <gurobi.QuadExpr: -1.0 y[2]   -1.0 y[3]   -1.0 y[4]   -1.0 y[5]   y[6]   y[7]  
 -1.0 d   x[1]   x[2]   [ -1.0 y[1] * d   d * u[1]   d * u[2] ]> >= 0

CodePudding user response:

Here is a concise answer to your question but please keep in mind that this is very unsafe and error-prone and should never be used in actual code:

import gurobipy as gp

m = gp.Model()
variables = ["y1", "y2", "y3", "y4", "y5", "y6", "y7"]
constraints = ["y1 3*y2-y3>=2", "y4 y5 y6 y7==2"]

for v in variables:
    exec(f"{v} = m.addVar(name = '{v}')")

for c in constraints:
    exec(f"m.addConstr({c})")

m.write("out.lp")

out.lp:

Minimize
 
Subject To
 R0: y1   3 y2 - y3 >= 2
 R1: y4   y5   y6   y7 = 2
Bounds
End

Similar questions have been asked multiple times on SO (e.g. here and in the linked duplicates) and the best way to work with "strings as variable names" simply is a dictionary.

Using addVars() is the correct way of creating the set of variables according to your specified list of names:

v = m.addVars(variables)
m.update()
print(v)

output:

{'y1': <gurobi.Var C0>, 'y2': <gurobi.Var C1>, 'y3': <gurobi.Var C2>, 
 'y4': <gurobi.Var C3>, 'y5': <gurobi.Var C4>, 'y6': <gurobi.Var C5>,
 'y7': <gurobi.Var C6>
}

This is not possible for the list constraints, though.

  •  Tags:  
  • Related