1

Using gurobi and python I am trying to solve a water balance(similar to the classic transportation problem) linear programming problem in the form of:

minimize c'x subject to:

Ax=b

lb<=x<=ub

A, L are sparse crs scipy matrices, c,b,lb,ub are vectors.

My problem should be updated for a number of steps and some elements are updated with new values. Specifically A is fixed, and all other elements get new values at each step. The following snippet works perfectly and is the basis I used so far (ignore the "self", as the model is embedded in a solver class, while "water_network is the graph object holding values and properties for each step):

### Snippet 1: Formulating/initializing the problem
# unitC is the c vector
# Bounds holds both lb and ub values for each x

self.model = gurobipy.Model()
rows, cols = len(self.water_network.node_list), len(self.water_network.edge_name_list)
self.x1 = []
for j in range(cols):
    self.x1.append(self.model.addVar(lb=self.water_network.Bounds[j,0], ub=self.water_network.Bounds[j,1],obj=self.water_network.unitC[j]))
self.model.update()

self.EqualityConstraintA=[]
for i in range(rows):
    start = self.water_network.A_sparse.indptr[i]
    end = self.water_network.A_sparse.indptr[i+1]
    variables = [self.x1[j] for j in self.water_network.A_sparse.indices[start:end]]
    coeff = self.water_network.A_sparse.data[start:end]
    expr = gurobipy.LinExpr(coeff, variables)
    self.EqualityConstraintA.append(self.model.addConstr(lhs=expr, sense=gurobipy.GRB.EQUAL, rhs=self.water_network.b [i],name='A'+str(i)))

self.model.update()
self.model.ModelSense = 1
self.model.optimize()

The following simple snippet is used to update the problem at each step. Note i use the getConstrs function:

#### Snippet 2: Updating the constraints, working ok for every step. 

self.model.setAttr("LB",self.model.getVars(), self.water_network.Bounds[:,0])
self.model.setAttr("UB", self.model.getVars(), self.water_network.Bounds[:,1])
self.model.setAttr("OBJ", self.model.getVars(), self.water_network.unitC)
self.model.setAttr("RHS", self.model.getConstrs(),self.water_network.b)

The problem arised when a new set of constraints should be added to the problem, in the form of:

Lx=0 where L is a sparse matrix that is updated every step! Now in the formulation I add the following just after the snippet 1:

self.EqualityConstraintL=[]
leakrows= len(self.water_network.ZeroVector)
for i in range(leakrows):
    start = self.water_network.L_sparse.indptr[i]
    end=self.water_network.L_sparse.indptr[i+1]
    variables=[self.x1[j] for j in self.water_network.L_sparse.indices[start:end]]
    coeff=self.water_network.L_sparse.data[start:end]
    expr = gurobipy.LinExpr(coeff, variables)
    self.EqualityConstraintL.append(self.model.addConstr(lhs=expr, sense=gurobipy.GRB.EQUAL, rhs=self.water_network.ZeroVector[i],name='L'+str(i)))

However, I can no longer use the getConstrs to update all constraints at once, as some need only the RHS changed and others need only the LHS changed. So I did the following for the update (Snippet 3):

self.model.setAttr("LB",self.model.getVars(), self.water_network.Bounds[:,0])
self.model.setAttr("UB", self.model.getVars(), self.water_network.Bounds[:,1])
self.model.setAttr("OBJ", self.model.getVars(), self.water_network.unitC)
# Update A rhs...
for i in range(len(self.water_network.edge_name_list)):
    self.model.setAttr("RHS", self.model.getConstrs()[i],self.water_network.b[i])

# Update L expr...
x1=self.model.getVars()
n=len(self.water_network.node_list) # because there are n rows in the A constrains, and L constraints are added after

# Now i rebuild the LHS expressions
for i in range(len(self.water_network.ZeroVector)):
    start = self.water_network.L_sparse.indptr[i]
    end=self.water_network.L_sparse.indptr[i+1]
    variables=[x1[j] for j in self.water_network.L_sparse.indices[start:end]]
    coeff=self.water_network.L_sparse.data[start:end]
    expr = gurobipy.LinExpr(coeff, variables)
    self.model.setAttr("LHS",self.model.getConstrs()[n+i],expr)

self.model.update()
self.model.optimize()

When I run the problem, it initializes fine, but at the second step it returns this error:

File "model.pxi", line 1709, in gurobipy.Model.setAttr

TypeError: object of type 'Constr' has no len()

and the offending line is:

self.model.setAttr("RHS", self.model.getConstrs()[i],self.water_network.b[i])

Two questions: 1) why is that happening? replacing getConstrs()[i] with getConstrByName('A'+str(i)) also fails with the exact same error. How to update the RHS/LHS of a specific constraint?

2) Is there a way to more efficiently update the RHS on the constraints contained in the self.EqualityConstraintA list and then the LHS on the other constraints contained in the self.EqualityConstraintL list ?

Many thanks in advance!

Di

DiNik
  • 117
  • 1
  • 1
  • 7

1 Answers1

2

The setAttr function on the model object is for

  • setting attributes globally on the model
  • setting attributes for a list of variables
  • setting attributes for a list of constraints

The individual constraint and variable objects have their own setAttr functions to set attributes on single variables and constraints. In your case,

for i in range(len(self.water_network.edge_name_list)):
    self.model.getConstrs()[i].setAttr('RHS', self.water_network.b[i])

Which could be replaced by the more pythonic (and likely more efficient)

m = self.model
constrs = m.getConstrs()[:len(self.water_network.edge_name_list)]
m.setAttr('RHS', constrs, self.water_network.b)
David Nehme
  • 21,379
  • 8
  • 78
  • 117
  • 1
    Thank you! The solution works perfectly, and the more pythonic notation is faster, as it avoids the for loop! – DiNik Oct 23 '19 at 07:22