Gurobipy  Model.addVars()
AnsweredHi,
Modeling with gurobipy, is there a way to create variables with the same keys (multiple variables with the same indexes) ?
Thank you for your answer !
Emma

Hi Emma,
Since the title of your post includes "addVars", I would say you would call addVars() multiple times using the same sets, e.g.
startTime = model.addVars(vehicles, depots, name="startTime")
endTime = model.addVars(vehicles, depots, name="endTime")But I'm not sure if that's what you're looking for?
Kind regards,
Ronald0 
Hi Ronald,
Thanks a lot for your answer.
I realize I wasn't very clear in my previous message.
I have a list of tuples :
R = [('b', 'b', 2), ('b', 'b', 2), ('e', 'e', 2), ('d', 'd', 2), ('c', 'c', 2)]
and I want to create a variable for each element in R. As you can see; two of these elements are the same ('b', 'b', 2) but I still want two variables.
I hope this is clearer !
Thanks
Emma
0 
Hi Emma,
Thanks a lot for clarifying! Unfortunately you would then need to iterate over R yourself and call addVar() without the "s" to create variables one by one. When you use addVars(), the result is stored in a "tupledict" which is an extension of a standard Python dictionary and won't allow duplicate keys.
Kind regards,
Ronald0 
Thank you very much for your answer !
Would this be the correct way to do it ?
for r in R:
x = model.addVar(r, vtype=gp.GRB.BINARY)0 
Almost; you would leave out the r argument since Gurobi doesn't need to do anything with that information. And you would store each individual variable somewhere else (right now you overwrite x in every iteration of the loop).
I would consider storing the variables in a list with the same length as your R list. Then it's easier to relate them to each other: element i from the variable list belongs to tuple i from the original R list.
In fact, that would allow you to use addVars again, but using a list of integers 0..len(R)1 as the indices:
x = model.addVars(range(len(R)), vtype=gp.GRB.BINARY)
0 
Hi Ronald,
Could you give me an example ?
Suppose I have two lists of tuples :
R = [('e', 'e'), ('e', 'e'), ('a', 'a'), ('e', 'e'), ('d', 'd')]
L = [('d', 'd'), ('e', 'e'), ('d', 'd'), ('c', 'c'), ('b', 'b')]I want to create binary variables with two indexes (x_rl), the first index (r) is every tuple in R and the second is every tuple in L. There will thus be multiple variables with the same indexes.
Thank you for you answer !
Emma
0 
Hi Emma,
You would generate the combinations of elements from R and L "outside Gurobi" first and then call addVars() requesting the right number of variables, returning them as a list.
For example:
RL = [(r, l) for r in R for l in L]
x = model.addVars(len(RL), vtype=GRB.BINARY)This gives you a list RL of 25 combinations of elements from R and L, and another list x with 25 binary variables. When you're looking for the variable of the 15th element from RL (e.g. the 15th (r, l) tuple) then you'd use the 15th variable in the x list.
Just to be sure (also for other readers): normally you would call model.addVars(R, L, vtype=GRB.BINARY) and get a nice dictionary mapping (r, l) tuples to variables. The reason we use a different approach here, is that the mapping from (r, l) tuples to variables cannot be stored in a Python dictionary as normal since the keys would not be unique. Not so much a Gurobi limitation, but given by the fact that dictionary keys must be unique.
Kind regards,
Ronald0 
Thank you for your answer. The only issue I have is that I won't be able to refer to my variables using the tuples but I would have to use the 15th elemnt for example.
In order to refer to my variables using the tuples, would something like this work ?
x = {}
for r in R[t]:
for l in L[t]:
x[r,l[:2]] = model.addVar(vtype=gp.GRB.BINARY)
Thanks !0 
I may have misunderstood your question as we looked at multiple cases. The main point is, if the keys (coming from one list or combinations from two lists) are unique, you can just pass the list(s) to addVars and the result is a nice dictionary; if they are not unique, you need a workaround as described.
 When you had a single list R with tuples that had 3 values each, and some tuples were the same, one could not use a dictionary since the keys would not be unique.
 In the second example with R and L (each having tuples of 2 values), if every combination of a tuple from R with a tuple from L would be unique, you could just call model.addVars(R, L) because the resulting keys like (('e', 'e'), ('d', 'd')) would be unique. If there are duplicate elements in R or L, you would need to use the workaround I mentioned again.
0 
Thank you very much ! This is clearer ! I followed your advice and proceeded as follows :
RL = [(r, l) for r in R for l in L]
with gp.Env() as env, gp.Model(env=env) as model:
x = model.addVars(len(RL), vtype=gp.GRB.BINARY)
model.setObjective(gp.quicksum((c[r, l[:2], l[2]]  s_v[l[:2], l[2] + 1])*x[RL.index((r,l))] for r in R for l in L), gp.GRB.MAXIMIZE)However, I get the following error message : File "src\gurobipy\model.pxi", line 1452, in gurobipy.Model.setObjective
File "src\gurobipy\util.pxi", line 44, in gurobipy.__simpleexpr
gurobipy.GurobiError: Unable to convert argument to an expressionDo you have an idea what I did wrong ?
Thanks a lot !
0 
Not sure, could you update your message to include the full (or at least reproducible) code ?
One thing I did notice, is that RL.index((r, l)) would only return the first occurrence of the (r, l) pair while you might have duplicates. For the data you provided, we would have 6 times the same combination (('e', 'e'), ('d', 'd')) in RL. In total we have 25 elements in RL so 25 variables in x. But for all the six times we see the specific combination I mentioned, RL.index((r, l)) would always return the same index and so you would reuse the same variable from x 6 times and ignore the other 5 relevant variables.
When you add your code, could you also briefly explain what you're trying to model? It may help us come up with easier alternatives that would achieve the same goal.
0 
Each tuple in R corresponds to a driver, with the first element in each tuple being the origin of the driver and the second element being the destination of the driver.
Each tuple in L corresponds to a parcel, with the first element in each tuple being the origin of the parcel and the second element being the destination of the parcel.
Duplicates just mean that we have two (or more) parcels and/or drivers with the same origin and destination. We therefore need to consider them separately.
A parcel can only be assignd to at most one driver and one driver can only be assigned to at most one parcel. c[r,l] is the benefit from assigning a parcel l to a driver r.
The goal of the model is to assign parcels to drivers.
Here is a simplified version of the code :
R = [('e', 'e'), ('e', 'e'), ('a', 'a'), ('c', 'c'), ('b', 'b')]
L = [('d', 'd'), ('e', 'e'), ('a', 'a'), ('d', 'd'), ('b', 'b')]
RL = [(r, l) for r in R for l in L]
with gp.Env() as env, gp.Model(env=env) as model:
x = model.addVars(len(RL), vtype=gp.GRB.BINARY)
model.setObjective(gp.quicksum(c[r, l]*x[RL.index((r, l))] for r in R for l in L), gp.GRB.MAXIMIZE)
model.addConstrs((gp.quicksum(x[r, l] for r in R) <= 1 for l in L))
model.addConstrs((gp.quicksum(x[r, l] for l in L) <= 1 for r in R))
model.optimize()
for r in R:
for l in L:
if x[r, l].X == 1:
x_R.append(r)
x_L.append(l)0 
In this case, note that the information in R and L is mostly irrelevant, except when you need to find the cost for a certain combination of a driver and a parcel. So, instead of using elements from R and L directly as indices for the variables, I would just use the position of the driver and parcel within the R and L list.
In other words, the dimensions of your variables become range(len(R)) and range(len(L)). Then when you need the cost for a particular driver with index i and parcel with index j, you can find the driver with R[i], the parcel with L[j] hence the cost with c[R[i], L[j]].
0
Please sign in to leave a comment.
Comments
13 comments