Introduction
Pyomo [1]  is a popular Python based modeling tool. In [2] a question is posed about a situation where a certain constraint takes more than 8 hours to generate. As we shall see, the reason is that extra indices are used.
The constraint \[y_i = \sum_j x_{i,j} \>\>\>\forall i,j\] is really malformed. The extra \(\forall j\) is problematic. What does this mean? There are two obvious interpretations:
Journal papers are often typeset using LaTeX. Not much checking there, and as a result authors are free to write equations like [3]:

Here we have the same thing. We have a summation over \(i\) as well as a \(\forall i\).
The GAMS fragment corresponding to this example, shows GAMS will object to this construct:
 
AMPL also gives a syntax error. The message could be improved a bit:
 
The Pyomo equivalent can look like:
 
This fragment is a bit more difficult to read, largely due to syntactic clutter. But in any case: Python and Pyomo accepts this constraint as written. To see what is generated, we can use
 
This will show something like:
 
We see for each \(i\) we have three duplicate constraints. Instead of 6 constraints, we just should have 2. The way to fix this is to remove the function argument \(j\) from eqRule:
 
After this, model.Eq.pprint() produces
 
This looks much better.
The constraint in the original question was:
 
Using the knowledge of the previous paragraph we know this should really be:
A simple example
The constraint \[y_i = \sum_j x_{i,j} \>\>\>\forall i,j\] is really malformed. The extra \(\forall j\) is problematic. What does this mean? There are two obvious interpretations:
- One could say, this is just wrong. Below we will see that GAMS and AMPL follow this approach.
 - We can also interpret this differently. Assume the inner \(j\) is scoped (i.e. local). Then we could read this as: repeat the constraint \(y_i = \sum_j x_{i,j}\), \(n\) times. Here \(n=|J|\) is the cardinality of set \(J\). This is what Pyomo is doing.
 
LaTeX: no checks
Journal papers are often typeset using LaTeX. Not much checking there, and as a result authors are free to write equations like [3]:
Here we have the same thing. We have a summation over \(i\) as well as a \(\forall i\).
GAMS: syntax error
The GAMS fragment corresponding to this example, shows GAMS will object to this construct:
 
  11  equation e(i,j); 
 
  12  e(i,j)..  y(i) =e= sum(j, x(i,j)); 
****                          $125 
**** 125  Set is under control already 
 
  13    
**** 1 ERROR(S)   0 WARNING(S) 
 | 
 
AMPL: syntax error
AMPL also gives a syntax error. The message could be improved a bit:
D:\etc>ampl x.mod 
d:x.mod, line 8 (offset 155): 
        syntax error 
context:  e{i in I, j in J}: Y[i] = sum{j  >>> in  <<< J} X[i,j]; 
 | 
 
Pyomo: create duplicate constraints
The Pyomo equivalent can look like:
def eqRule(m,i,j): 
   
  return m.Y[i] == sum(m.X[i,j] for j in m.J); 
model.Eq =
  Constraint(model.I,model.J,rule=eqRule) 
 | 
 
This fragment is a bit more difficult to read, largely due to syntactic clutter. But in any case: Python and Pyomo accepts this constraint as written. To see what is generated, we can use
model.Eq.pprint() 
 | 
 
This will show something like:
Eq : Size=6,
  Index=Eq_index, Active=True 
    Key          : Lower : Body                                     : Upper
  : Active 
    ('i1', 'j1') :   0.0 : Y[i1] - (X[i1,j1] + X[i1,j2] +
  X[i1,j3]) :   0.0 :   True 
    ('i1', 'j2') :   0.0 : Y[i1] - (X[i1,j1] + X[i1,j2] +
  X[i1,j3]) :   0.0 :   True 
    ('i1', 'j3') :   0.0 : Y[i1] - (X[i1,j1] + X[i1,j2] +
  X[i1,j3]) :   0.0 :   True 
    ('i2', 'j1') :   0.0 : Y[i2] - (X[i2,j1] + X[i2,j2] +
  X[i2,j3]) :   0.0 :   True 
    ('i2', 'j2') :   0.0 : Y[i2] - (X[i2,j1] + X[i2,j2] +
  X[i2,j3]) :   0.0 :   True 
    ('i2', 'j3') :   0.0 : Y[i2] - (X[i2,j1] + X[i2,j2] +
  X[i2,j3]) :   0.0 :   True 
 | 
 
We see for each \(i\) we have three duplicate constraints. Instead of 6 constraints, we just should have 2. The way to fix this is to remove the function argument \(j\) from eqRule:
def eqRule(m,i): 
   
  return m.Y[i] == sum(m.X[i,j] for j in m.J); 
model.Eq = Constraint(model.I,rule=eqRule) 
 | 
 
After this, model.Eq.pprint() produces
Eq : Size=2,
  Index=I, Active=True 
    Key : Lower : Body                                     : Upper
  : Active 
     i1 :  
  0.0 : Y[i1] - (X[i1,j3] + X[i1,j2] + X[i1,j1]) :   0.0 :  
  True 
     i2 :  
  0.0 : Y[i2] - (X[i2,j3] + X[i2,j2] + X[i2,j1]) :   0.0 :  
  True 
 | 
 
This looks much better.
The original problem
The constraint in the original question was:
def
  period_capacity_dept(m, e, j, t, dp): 
    return sum(a[e, j, dp, t]*m.y[e,j,t] for
  (e,j) in model.EJ)<= K[dp,t] + m.R[t,dp] 
model.period_capacity_dept
  = Constraint(E, J, T, DP, rule=period_capacity_dept) 
 | 
 
Using the knowledge of the previous paragraph we know this should really be:
def
  period_capacity_dept(m, t, dp): 
    return sum(a[e, j, dp, t]*m.y[e,j,t] for
  (e,j) in model.EJ)<= K[dp,t] + m.R[t,dp] 
model.period_capacity_dept
  = Constraint(T, DP, rule=period_capacity_dept) 
 | 
 
Pyomo mixes mathematical notation with programming. I think that is one of the reasons this bug is more difficult to see. In normal programming, adding an argument to a function has an obvious meaning. However in this case, adding e,j means in effect: \(\forall e,j\). If \(e\) and \(j\) belong to large sets, we can easily create a large number of duplicates.
Notes: Debugging Pyomo models
When debugging Pyomo models there are a few tricks to know about:
- Have a small data set available. Debugging with large data sets is very difficult and plain unpleasant.
 - Use model.symbol.pprint()
 - Create an LP file of the model. When using the pyomo script, use the convert option (don't forget the --symbolic-solver-labels flag). From Python code, use model.write(filename, io_options={'symbolic_solver_labels': True}).
 
References
- http://www.pyomo.org
 - Pyomo: Simple inequality constraint takes unreasonable time to build, https://stackoverflow.com/questions/58034244/pyomo-simple-inequality-constraint-takes-unreasonable-time-to-build
 - Checking math, https://yetanothermathprogrammingconsultant.blogspot.com/2012/03/checking-math.html