Python call to chgCoef yields attribute error for __cindex__
AnsweredIn the Gurobi Python API, I am trying to change one coefficient within a linear constraint of a quadratic optimization model.
I call
grbModel.chgCoeff(constr, grbx[j], newvalue)
Where
grbModel is a my optimization model
constr is a constraint within that model
grbx is a onedimensional matrix variable within that model
j is a valid index into that matrix variable
newvalue is some float
My program bombs with this rather inscrutable error:
File "model.pxi", line 4915, in gurobipy.Model.chgCoeff
File "mvar.pxi", line 67, in gurobipy.MVar.__getattr__
File "mvar.pxi", line 186, in gurobipy.MVar.getAttr
File "attrutil.pxi", line 23, in gurobipy.__getattrinfo
AttributeError: 'gurobipy.MVar' object has no attribute '__cindex__'
What am I doing wrong?
If I want to use chgCoef, can I only use individual variables, not matrix variables?
I am very experienced in both programming and optimization, but this is my first time using this particular API.

Hi Jonathan,
Can you try calling grbModel.update() before changing the coefficient? You can read more about this in the "Lazy Updates" section here. Essentially, if you want to immediately access a variable/constraint attribute or change some part of the model, you should first update the model.
If this doesn't work, could you post a small working code example that reproduces the problem? Thanks!
Eli
0 
Yes, I did update the model first. I'll try to post a selfcontained example soon.
0 
Here is a 12line program that produces the same error. Possibly I am doing something really dumb, but it is my first time with this API. Optimizing the model instead of updating doesn't change the error.
Thanks!  Jonatha
import numpy
import gurobipy as gp
from gurobipy import GRBn = 10
m = gp.Model()
grbx = m.addMVar(shape=n, lb=1.0, ub=1.0, vtype=GRB.CONTINUOUS, name="grbx")
randomMatrix = numpy.random.rand(20,10)
Q = numpy.transpose(randomMatrix) @ randomMatrix
m.setObjective(0.5*(grbx @ Q @ grbx))
constr = m.addConstr(grbx[0] + grbx[1] + grbx[2] <= 1.0)
m.update()m.chgCoeff(constr,grbx[1],2.0)
0 
Hi Jonathan,
Ah, yes, you're right that this is because you are passing an \( \texttt{MVar} \) object instead of a \( \texttt{Var} \) object. I missed that in my first (hasty) reading of your post. I don't believe there is an analogous method for \( \texttt{MVar} \) objects. I'll look into this and get back to you.
Thanks,
Eli
0 
OK, thanks! Please let me know what you find out.
Setting up my objective function with only scalar variables would be hellish. I suppose that I could make big pile of scalar variables, each with an equality constraint to an element of the matrix variable. That would not be pretty. Or I guess I could simply delete all my constraints and then add in new ones.
I have to solve a bunch of QP problems, each of which some different variable bounds and linear constraint coefficients.
0 
Hello Jonathan,
as written by Eli there currently is no way to change coefficents w.r.t. an Mvar object, but I have registered this in our backlog of future improvements to the matrixfriendly API. Your suggestion to delete the old constraints, modify the coefficients on the ndarray level, and add them back to the model seems to be the most practical workaround to me.
Robert
0 
OK, thanks for checking. I will just delete the constraints and add back modified ones. I suspect that will be slow, but this is a prototype, proofofconcept implementation, so its speed is not absolutely critical.
0 
Dear Robert and Eli,
does this feature (change coefficients w.r.t. an Mvar object) already exist in the latest version of Gurobi (9.1)?
Thank you!
Chris.
0 
Unfortunately not. However, there is an easy workaround available in Gurobi 9.1. Namely, you can use MVar.tolist() to convert MVar objects to Var objects for use in Model.chgCoeff(). Similarly, you can convert MConstr objects to Constr objects with MConstr.tolist(). For example:
import numpy as np
import gurobipy as gp
m = gp.Model()
x = m.addMVar(shape=(3,), name='x')
a = np.array([1.0, 2.0, 3.0])
# Add constraint x[0] + 2 x[1] + 3 x[2] <= 1
c = m.addConstr(a @ x <= 1)
m.write('model1.lp')
# Change to 5 x[0] + 2 x[1] + 3 x[2] <= 1
m.chgCoeff(c.tolist()[0], x.tolist()[0], 5)
m.write('model2.lp')0 
That said, the workaround should no longer be necessary starting in the next feature release after 9.1, where you will be able to just do
m.chgCoeff(c[0], x[0], 5)
This works because indexing an MVar/MConstr will yield a Var/Constr object instead of a (1,) slice.
0 
Thank you both! I am looking forward to the new release but for the time being Eli's suggested workaround will do it too.
0 
Hi Chris,
Gurobi 9.5 was recently released. Included in this release is a new feature where indexing an MVar/MConstr returns a Var/Constr object.
We hope this fix works well for you. Please let us know if you see any other issues using this.
Best regards,
Maliheh
0 
Hi Maliheh Aramon,
I am still getting the same error as discussed above with gurobi 10.0. Is the update you mentioned still intact?
Regards,
Satender
0 
Hi Satender,
Yes, sorry about this. The Python Matrix API went through an overhaul in the latest Gurobi version 10.0. You can check out our recent webinar on this.
In Gurobi 10.0, indexing an MVar/MConstr returns a 0dim object of the same type. To access a Var/Constr object from an MVar/MConstr, you need to call the method MVar.item()/MConstr.item(). As an example:
m.chgCoeff(c.item(), x[0].item(), 5)
Best regards,
Maliheh
0 
Thank you Maliheh Aramon.
0
Please sign in to leave a comment.
Comments
15 comments