From b7d132dc72fb80347242bb50b5c84e6df9991d00 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Thu, 9 Mar 2023 09:11:17 -0700 Subject: [PATCH 1/3] Correct indexing by unhashable objects convertable to tuple --- pyomo/core/base/indexed_component.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pyomo/core/base/indexed_component.py b/pyomo/core/base/indexed_component.py index 38c10190114..eac2effbc4e 100644 --- a/pyomo/core/base/indexed_component.py +++ b/pyomo/core/base/indexed_component.py @@ -805,7 +805,6 @@ def _processUnhashableIndex(self, idx): fixed = {} sliced = {} ellipsis = None - _found_numeric = False # # Setup the slice template (in fixed) # @@ -844,8 +843,6 @@ def _processUnhashableIndex(self, idx): # should raise a TemplateExpressionError try: val = EXPR.evaluate_expression(val, constant=True) - _found_numeric = True - except TemplateExpressionError: # # The index is a template expression, so return the @@ -937,7 +934,7 @@ def _processUnhashableIndex(self, idx): IndexedComponent_slice._getitem_args_to_str(list(idx)), self.name, set_dim, slice_dim)) return IndexedComponent_slice(self, fixed, sliced, ellipsis) - elif _found_numeric: + elif len(idx) == len(fixed): if len(idx) == 1: return fixed[0] else: From 978aac9ccf91e23e55bfeefa723169bdb9405a9f Mon Sep 17 00:00:00 2001 From: John Siirola Date: Thu, 9 Mar 2023 09:11:40 -0700 Subject: [PATCH 2/3] Add tests for indexing by list --- pyomo/core/tests/unit/test_indexed.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pyomo/core/tests/unit/test_indexed.py b/pyomo/core/tests/unit/test_indexed.py index 7308c908d3d..f45a5dd999e 100644 --- a/pyomo/core/tests/unit/test_indexed.py +++ b/pyomo/core/tests/unit/test_indexed.py @@ -223,9 +223,26 @@ def test_index_var_by_tuple_with_variables(self): def test_index_by_unhashable_type(self): m = ConcreteModel() m.x = Var([1,2,3], initialize=lambda m,x: 2*x) + # Indexing by a dict raises an error self.assertRaisesRegex( TypeError, '.*', m.x.__getitem__, {}) + # Indexing by lists works... + # ... scalar + self.assertIs(m.x[[1]], m.x[1]) + # ... "tuple" + m.y = Var([(1, 1), (1, 2)]) + self.assertIs(m.y[[1, 1]], m.y[1, 1]) + m.y[[1, 2]] = 5 + y12 = m.y[[1, 2]] + self.assertEqual(y12.value, 5) + m.y[[1, 2]] = 15 + self.assertIs(y12, m.y[[1, 2]]) + self.assertEqual(y12.value, 15) + with self.assertRaisesRegex( + KeyError, r"Index '\(2, 2\)' is not valid for indexed component 'y'"): + m.y[[2, 2]] = 5 + def test_ordered_keys(self): m = ConcreteModel() From bdbc14ef8762d732c2922583093466b44667203c Mon Sep 17 00:00:00 2001 From: John Siirola Date: Thu, 9 Mar 2023 09:11:55 -0700 Subject: [PATCH 3/3] Improve developer error message information --- pyomo/core/base/indexed_component.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyomo/core/base/indexed_component.py b/pyomo/core/base/indexed_component.py index eac2effbc4e..01bd6f5fd07 100644 --- a/pyomo/core/base/indexed_component.py +++ b/pyomo/core/base/indexed_component.py @@ -802,6 +802,7 @@ def _processUnhashableIndex(self, idx): # Iterate through the index and look for slices and constant # components # + orig_idx = idx fixed = {} sliced = {} ellipsis = None @@ -942,7 +943,7 @@ def _processUnhashableIndex(self, idx): else: raise DeveloperError( "Unknown problem encountered when trying to retrieve " - "index for component %s" % (self.name,) ) + f"index '{orig_idx}' for component '{self.name}'") def _getitem_when_not_present(self, index): """Returns/initializes a value when the index is not in the _data dict.