# Possibility of indexing decision variables with 2 indices using a set of tuples in Pyomo

Operations Research Asked on November 24, 2021

I am currently attempting to solve a network problem that is not fully connected.Thus, I have attempted to do some preprocessing of data so as to form a set of tuples, e.g. $${(a,b), (c,e),ldots}$$, i.e. from $$a$$ to $$b$$, from $$c$$ to $$e$$.

I am able to declare binary decision variables with keys such as $$(a,b)$$, $$(c,e)$$ via using the set of tuples for indexing.

However, when I tried to use rules to declare constraints, with decision variables such as x[i][j], errors are thrown stating that $$(a,b)$$ is an invalid index.

Hence, I would like to ask if tuples can be used as indices for decision variables.

If not, is there a way to only declare the only decision variables that are needed, rather than declaring all, and then setting those unneeded to 0.

Thank you!

Yes. Totally doable. Here are 2 examples using either tuples in a pyomo set or just making some up on-the-fly and passing them to a rule-based constraint to make the appropriate number of sparse constraints (seen in the result).

# subsets in tuples

import pyomo.environ as pyo

mdl = pyo.ConcreteModel()

# sets
mdl.I = pyo.Set(initialize=range(4))
mdl.J = pyo.Set(initialize=range(3))
mdl.select_combos = pyo.Set(within = mdl.I * mdl.J, initialize = [(1,2), (3,1)])

# vars
mdl.X = pyo.Var(mdl.I, mdl.J, domain=pyo.NonNegativeReals)

# constraint with rule and tuples from pyomo Set
def c1(self, i, j):
return mdl.X[i, j] <= 2
mdl.c1 = pyo.Constraint(mdl.select_combos, rule=c1)

# or make a set of tuples of interest on the fly
my_combos = {(i, j) for i in mdl.I for j in mdl.J if
i <=2 and
j >=2 }
def c2(self, i, j):
return mdl.X[i, j] >= 1
mdl.C2 = pyo.Constraint(my_combos, rule=c2)

mdl.pprint()

### Output:

6 Set Declarations
C2_index : Dim=0, Dimen=2, Size=3, Domain=None, Ordered=False, Bounds=None
[(0, 2), (1, 2), (2, 2)]
I : Dim=0, Dimen=1, Size=4, Domain=None, Ordered=False, Bounds=(0, 3)
[0, 1, 2, 3]
J : Dim=0, Dimen=1, Size=3, Domain=None, Ordered=False, Bounds=(0, 2)
[0, 1, 2]
X_index : Dim=0, Dimen=2, Size=12, Domain=None, Ordered=False, Bounds=None
Virtual
select_combos : Dim=0, Dimen=2, Size=2, Domain=select_combos_domain, Ordered=False, Bounds=None
[(1, 2), (3, 1)]
select_combos_domain : Dim=0, Dimen=2, Size=12, Domain=None, Ordered=False, Bounds=None
Virtual

1 Var Declarations
X : Size=12, Index=X_index
Key    : Lower : Value : Upper : Fixed : Stale : Domain
(0, 0) :     0 :  None :  None : False :  True : NonNegativeReals
(0, 1) :     0 :  None :  None : False :  True : NonNegativeReals
(0, 2) :     0 :  None :  None : False :  True : NonNegativeReals
(1, 0) :     0 :  None :  None : False :  True : NonNegativeReals
(1, 1) :     0 :  None :  None : False :  True : NonNegativeReals
(1, 2) :     0 :  None :  None : False :  True : NonNegativeReals
(2, 0) :     0 :  None :  None : False :  True : NonNegativeReals
(2, 1) :     0 :  None :  None : False :  True : NonNegativeReals
(2, 2) :     0 :  None :  None : False :  True : NonNegativeReals
(3, 0) :     0 :  None :  None : False :  True : NonNegativeReals
(3, 1) :     0 :  None :  None : False :  True : NonNegativeReals
(3, 2) :     0 :  None :  None : False :  True : NonNegativeReals

2 Constraint Declarations
C2 : Size=3, Index=C2_index, Active=True
Key    : Lower : Body   : Upper : Active
(0, 2) :   1.0 : X[0,2] :  +Inf :   True
(1, 2) :   1.0 : X[1,2] :  +Inf :   True
(2, 2) :   1.0 : X[2,2] :  +Inf :   True
c1 : Size=2, Index=select_combos, Active=True
Key    : Lower : Body   : Upper : Active
(1, 2) :  -Inf : X[1,2] :   2.0 :   True
(3, 1) :  -Inf : X[3,1] :   2.0 :   True

9 Declarations: I J select_combos_domain select_combos X_index X c1 C2_index C2
[Finished in 2.5s]

Answered by AirSquid on November 24, 2021

An example is as follows:

##First, create the set of tuples needed for filtering

#Op_Machine: set of (operation, machine) tuples created to avoid redundancy in decision variable declaration Op_Machine=list() for machine_id, op_proctime in Machine_Op_Time.items():
for op in op_proctime.keys():
print(Op_Machine)
print((op,machine_id))
Op_Machine.append((op,machine_id))
print(Op_Machine)

##Next, invoke the rule using the if statement to filter across all possible indices accepting those combinations that are aligned with the tuples within the set
##Use Constraint.Skip to Skip creating constraints that do not belong to the set of tuples

def F1_rule(model,i,k):
if (i,k) in Op_Machine:
##print(i,k)
return model.Cmax>=model.completion_time[i,k]
else:
return Constraint.Skip

#model.makespan= Constraint(model.op_set, model.mach_set, rule=Cmax_rule) model.F1= Constraint(Operation_Set, Machine_Set, rule=F1_rule)

Note that the sets Operation_Set, Machine_Set function as the universal set as it comprises all combinations of operations and machines. Hence the statement model.F1= Constraint(Operation_Set, Machine_Set, rule=F1_rule) can be thought a for loop that iterates over all combinations while the if statement within the def function acts a filter to generate the needed constraints.

Answered by Mike on November 24, 2021

It is possible to use tuples as the indices of your variables. If the tuple like $$(a_1, a_2)$$ is not defined in the index set, errors will be thrown but you can skip those undefined indices by using:

Constraint.Skip

checking if the tuple is defined or not. An example of implementation would be as follow:

model.cons = ConstraintList()
for i in model.nodes:
for j in model.nodes:
if [i,j] in tuples_list: