Variable as an index of a dictionary
AnsweredHello!
I know I can't use a variable to index another variable or parameter in the model. So I have to use auxiliary binary variables to solve the problem, as suggested. But I am not able to do it.
I have a dictionary:
dict={'a':[12,24,36], 'b':[10,20,30, 40]...} which is known a priori.
I would like to set the value of a variable "var2" like this:
var2=m.addVars(I, vtype=GRB.INTEGER,name ="var2" )
m.addConstrs((dict['b'][var1] == var2[i] for i in I) , "c1")
where var1 is calculated with many other constraints (maybe the simplification of the code is not totally correct but I wanted to avoid the long code).
But obviously I get the error: TypeError: list indices must be integers or slices, not Var
How could I solve this problem? Many thanks in advance.

Hi Ana,
Since variable names are unique, you can use the variable's name as a dictionary key. This means that you could build and access your dictionary via
dict['b'][var1.VarName]
You will have to execute the update() function once after all variables have been added to ensure that you have access to updated variable attributes.
Best regards,
Jaromił 
Many thanks for your response, Jaromil.
Could I query the values of the attributes before the optimization is complete? I have tried to see (only for checking how it works) the name of the variables:
for v in m.getVars():
print('%s' % (v.varName)) but I don't get anything.If var1 depends on i,j,k, should I write var1[i,j,k].VarName ?? I have tried for example with "var1[0,0,0].VarName" but I get AttributeError: Index out of range for attribute 'VarName'
Thanks again.

Hi Ana,
Yes, you can access the attribute VarName before an optimization has been performed. Just make sure that you execute the update() function after adding the variables.
An example code would be
import gurobipy as gp
from gurobipy import GRB
m = gp.Model("test")
I = [0,1,2]
J = [3,4,5]
K = [6,7,8]
vars = m.addVars(I,J,K,vtype=GRB.BINARY,name="v")
m.update()
# print names using the vars tupledict returned by addVars
for i in I:
for j in J:
for k in K:
print('%s' % (vars[i,j,k].varName))
# print names using the list returned by getVars
for v in m.getVars():
print('%s' % (v.varName))Note that the getVars function returns a one dimensional list while the addVars function returns a Gurobi tupledict. That's why the accessing of the VarName attribute is different.
Best regards,
Jaromił 
Hi again, Jaromil!
Many thanks, it worked.
However, it does not solve my problem. Maybe I did not explain it well. What I want is to find the value of the variable. Imagine this:
dict={'a':[12,24,36], 'b':[10,20,30,40]...}
m.addConstrs((dict['b'][var1] == var2[i] for i in I) , "c1")
For example, if the value of var1 is equal to 2, then dict['b'][var1] =var2=30. So what I want is to work with the value of the variable, not the name. And this is the error I get when I put directly the variable: TypeError: list indices must be integers or slices, not Var.
I think that the problem is that I should I new variables as in https://support.gurobi.com/hc/enus/community/posts/360071776111useadecisionvariableasanindex?input_string=Variable%20as%20an%20index%20of%20a%20dictionary, but I don't know how to do it.
Many thanks again.

Hi Ana,
Thank you for clarifying. Now I understand the issue.
I think that the problem is that I should I new variables as in https://support.gurobi.com/hc/enus/community/posts/360071776111useadecisionvariableasanindex?input_string=Variable%20as%20an%20index%20of%20a%20dictionary, but I don't know how to do it.
Yes, you will have to introduce additional variables and constraints in order to model this relationship.
In you specific case, it seems like the values \(\texttt{dict['a'][var1]}\) are always multiples of integer values, e.g., the values of \(\texttt{dict['a']}\) are integer multiples of \(12\). In the following, for simplicity, I will assume that your \(\texttt{dict}\) only consists of
dict={'a':[12,24,36], 'b':[10,20,30,40]}
You could model the above as following:
\[\begin{align}
\sum_{i \in \{a,b\}} b_i &= 1\\
v_a &\leq 3 b_a \\
v_b &\leq 4 b_b \\
v_2 &= 12 v_a + 10 v_b\\
v_a &\in \{0,1,2,3\}\\
v_b &\in \{0,1,2,3,4\}\\
b_a,b_b &\in \{0,1\}
\end{align}\] You add binary variables \(b_a,b_b,\dots\) which are 1 when the first index of the dictionary equals the specific character and 0 otherwise. So \(b_a=1\) would mean that you are interested in the values of \(\texttt{dict['a']}\).
 The sum over \(b_i\) constraint makes sure that you only choose exactly one dict entry.
 You then add integer variables \(v_a,v_b,\dots\) which are only \(>0\) if the corresponding \(b_i=1\).
 The value of \(v_2\) is then given as the sum over all \(v_i\) with appropriate coefficients.
Best regards,
Jaromił 
Hi Ana,
Modeling without any specific sequence of values makes the formulation a bit more complex so it is always better to use all knowledge given. Anyway, for the case where the values of \(\texttt{dict['a']}\) and \(\texttt{dict['b']}\) do not follow any sequence, you have to introduce a binary variable for every value. In the specific example
dict={'a':[1,1,2,2,1,1], 'b':[1,2,3,5,6]}
you would need 11 additional binary variables. Let's assume that the values in the lists are given as constants \(A_i, B_i\), i.e., \(A_0 = 1, B_4 = 6\).
One possibility to model your problem would then be
\[\begin{align}
\sum_{i \in \{a,b\}} b_i &= 1\\
\sum_{i \in \{0,\dots,5\}} b_{a,i} &= b_a \\
\sum_{j \in \{0,\dots,4\}} b_{b,j} &= b_b \\
\sum_{i \in \{0,\dots,5\}} A_i b_{a,i} + \sum_{j \in \{0,\dots,4\}} B_j b_{b,j} &= v_2\\
b_{a,i}, b_{b,j} &\in \{0,1\} \forall i \in \{0,\dots,5\}, j \in \{0,\dots,4\}\\
b_a,b_b &\in \{0,1\}
\end{align}\] The first equality is as in my previous message
 The following two equality constraints force to chose a value from either \(\texttt{dict['a']}\) or \(\texttt{dict['b']}\)
 The fourth equality then computes the value of \(v_2\) given the constant values \(A_i, B_i\)
Please note that this is only one possible formulation and there is no guarantee that this is the best one.
Best regards,
Jaromił
Please sign in to leave a comment.
Comments
8 comments