Repository: Pyomo/pyomo

Issue:
Discretization leads to ampl preprocessing error
Hi,
this small example

```
from pyomo.environ import (ConcreteModel, Constraint,
                           Objective, Param,
                           Var, minimize, SolverFactory)

from pyomo.environ import TransformationFactory
from pyomo.dae import ContinuousSet
from pyomo.dae import DerivativeVar
from pyomo.environ import ConstraintList

# model m
m = ConcreteModel()

# Time definitions
m.tf = Param(initialize=1)
m.t = ContinuousSet(bounds=(0, m.tf))

# Variable definition
m.x = Var(m.t, bounds=(0, 10))

# Derivative definition
m.dx = DerivativeVar(m.x, wrt=m.t)

# initial values
def _init(m):
    yield m.x[m.t.first()] == 1


m.init_conditions = ConstraintList(rule=_init)


# Differential Equation 4
def _xdot(m, t):
    if t == m.t.first():
        return Constraint.Skip
    else:
        return m.dx[t] == - (m.x[t])


m.xdotcon = Constraint(m.t, rule=_xdot)

# objective function
m.obj = Objective(expr=-m.x[m.tf], sense=minimize)

# Discretization
discretizer = TransformationFactory('dae.collocation')
discretizer.apply_to(m, nfe=10, ncp=3, scheme='LAGRANGE-RADAU')

# Solving
solver = SolverFactory('ipopt')
results = solver.solve(m, tee=True)
```
leads to a not informative error (KeyError: 2915115411248 after trying to generate the expression in line 993 of ampl_.py) error while trying to solve it with IPOPT. When trying to use other solvers (e.g. BARON or GAMS) it works well, pointing that it may be a bug in the ampl parser. Interesting enough, the error does not appear in installations of Pyomo 5.5, while the GAMS solver fails to transform the model as in Issue #340 . Maybe while solving that issue, the ampl solver was affected.



Discussion:
This is related to a different known issue in the solver interfaces. I am pretty sure that you are seeing the consequences of https://github.com/Pyomo/pyomo/issues/350.

Edit: nope, not this problem. Different one.
I think this is a bug related to #17. If you replace the `m.tf` Param with a Python constant or use `value(m.tf)` as the index in the objective function everything seems to work.
This solves the issue. I think that it is not good to have several issues pointing to similar error, i.e. #17 and #340, therefore I will close this one. Thanks for the fix!
Here is the full stack trace: 

```
Traceback (most recent call last):
  File "disc_ex.py", line 54, in <module>
    results = solver.solve(m, tee=True)
  File "/home/blnicho/Research/pyomo/pyomo/opt/base/solvers.py", line 569, in solve
    self._presolve(*args, **kwds)
  File "/home/blnicho/Research/pyomo/pyomo/opt/solver/shellcmd.py", line 198, in _presolve
    OptSolver._presolve(self, *args, **kwds)
  File "/home/blnicho/Research/pyomo/pyomo/opt/base/solvers.py", line 669, in _presolve
    **kwds)
  File "/home/blnicho/Research/pyomo/pyomo/opt/base/solvers.py", line 740, in _convert_problem
    **kwds)
  File "/home/blnicho/Research/pyomo/pyomo/opt/base/convert.py", line 105, in convert_problem
    problem_files, symbol_map = converter.apply(*tmp, **tmpkw)
  File "/home/blnicho/Research/pyomo/pyomo/solvers/plugins/converter/model.py", line 191, in apply
    io_options=io_options)
  File "/home/blnicho/Research/pyomo/pyomo/core/base/block.py", line 1716, in write
    io_options)
  File "/home/blnicho/Research/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 378, in __call__
    include_all_variable_bounds=include_all_variable_bounds)
  File "/home/blnicho/Research/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 993, in _print_model_NL
    list(self_varID_map[id(var)] for var in linear_vars),
  File "/home/blnicho/Research/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 993, in <genexpr>
    list(self_varID_map[id(var)] for var in linear_vars),
KeyError: 140715256845152
```


Resolving patch:
diff --git a/pyomo/core/base/indexed_component.py b/pyomo/core/base/indexed_component.py
--- a/pyomo/core/base/indexed_component.py
+++ b/pyomo/core/base/indexed_component.py
@@ -318,10 +318,18 @@ def __getitem__(self, index):
         try:
             obj = self._data.get(index, _NotFound)
         except TypeError:
-            obj = _NotFound
             index = self._processUnhashableIndex(index)
             if index.__class__ is _IndexedComponent_slice:
                 return index
+            # The index could have contained constant but nonhashable
+            # objects (e.g., scalar immutable Params).
+            # _processUnhashableIndex will evaluate those constants, so
+            # if it made any changes to the index, we need to re-check
+            # the _data dict for membership.
+            try:
+                obj = self._data.get(index, _NotFound)
+            except TypeError:
+                obj = _NotFound
 
         if obj is _NotFound:
             # Not good: we have to defer this import to now
@@ -564,7 +572,7 @@ def _processUnhashableIndex(self, idx):
 
                 except TemplateExpressionError:
                     #
-                    # The index is a template expression, so return the 
+                    # The index is a template expression, so return the
                     # templatized expression.
                     #
                     from pyomo.core.expr import current as EXPR


Test patch:
diff --git a/pyomo/core/tests/unit/test_indexed.py b/pyomo/core/tests/unit/test_indexed.py
--- a/pyomo/core/tests/unit/test_indexed.py
+++ b/pyomo/core/tests/unit/test_indexed.py
@@ -100,6 +100,7 @@ def test_index_by_constant_simpleComponent(self):
         m.x = Var([1,2,3], initialize=lambda m,x: 2*x)
         self.assertEqual(m.x[2], 4)
         self.assertEqual(m.x[m.i], 4)
+        self.assertIs(m.x[2], m.x[m.i])
 
     def test_index_by_multiple_constant_simpleComponent(self):
         m = ConcreteModel()
@@ -110,6 +111,9 @@ def test_index_by_multiple_constant_simpleComponent(self):
         self.assertEqual(m.x[m.i,3], 12)
         self.assertEqual(m.x[m.i,m.j], 12)
         self.assertEqual(m.x[2,m.j], 12)
+        self.assertIs(m.x[2,3], m.x[m.i,3])
+        self.assertIs(m.x[2,3], m.x[m.i,m.j])
+        self.assertIs(m.x[2,3], m.x[2,m.j])
 
     def test_index_by_fixed_simpleComponent(self):
         m = ConcreteModel()