Skip to content

Commit 874352c

Browse files
authored
Merge pull request #3440 from emma58/fme-del-fix
Disallow calling `del_component` with ComponentData arguments
2 parents f48c551 + a2b0af8 commit 874352c

File tree

5 files changed

+66
-22
lines changed

5 files changed

+66
-22
lines changed

pyomo/contrib/fme/fourier_motzkin_elimination.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -730,12 +730,9 @@ def post_process_fme_constraints(
730730
continue
731731
# deactivate the constraint
732732
projected_constraints[i].deactivate()
733-
m.del_component(obj)
734-
# make objective to maximize its infeasibility
735-
obj = Objective(
736-
expr=projected_constraints[i].body - projected_constraints[i].lower
737-
)
738-
m.add_component(obj_name, obj)
733+
# Our constraint looks like: 0 <= a^Tx - b, so make objective to
734+
# maximize its infeasibility
735+
obj.expr = projected_constraints[i].body - projected_constraints[i].lower
739736
results = solver_factory.solve(m)
740737
if results.solver.termination_condition == TerminationCondition.unbounded:
741738
obj_val = -float('inf')
@@ -753,7 +750,6 @@ def post_process_fme_constraints(
753750
obj_val = value(obj)
754751
# if we couldn't make it infeasible, it's useless
755752
if obj_val >= tolerance:
756-
m.del_component(projected_constraints[i])
757753
del projected_constraints[i]
758754
else:
759755
projected_constraints[i].activate()

pyomo/contrib/parmest/utils/model_utils.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,7 @@ def convert_params_to_vars(model, param_names=None, fix_vars=False):
134134
v.name in param_names for v in identify_mutable_parameters(expr)
135135
):
136136
new_expr = replace_expressions(expr=expr, substitution_map=substitution_map)
137-
model.del_component(expr)
138-
model.add_component(expr.name, pyo.Expression(rule=new_expr))
137+
expr.expr = new_expr
139138

140139
# Convert Params to Vars in Constraint expressions
141140
num_constraints = len(list(model.component_objects(pyo.Constraint, active=True)))

pyomo/core/base/block.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from pyomo.common.timing import ConstructionTimer
3535
from pyomo.core.base.component import (
3636
Component,
37+
ComponentData,
3738
ActiveComponentData,
3839
ModelComponentFactory,
3940
)
@@ -1125,16 +1126,36 @@ def del_component(self, name_or_object):
11251126
"""
11261127
Delete a component from this block.
11271128
"""
1128-
obj = self.component(name_or_object)
1129-
# FIXME: Is this necessary? Should this raise an exception?
1130-
if obj is None:
1131-
return
1132-
1133-
# FIXME: Is this necessary? Should this raise an exception?
1134-
# if name not in self._decl:
1135-
# return
1129+
# in-lining self.component(name_or_object) so that we can add the
1130+
# additional check of whether or not name_or_object is a ComponentData
1131+
obj = None
1132+
if isinstance(name_or_object, str):
1133+
if name_or_object in self._decl:
1134+
obj = self._decl_order[self._decl[name_or_object]][0]
1135+
else:
1136+
# Maintaining current behavior, but perhaps this should raise an
1137+
# exception?
1138+
return
1139+
else:
1140+
try:
1141+
obj = name_or_object.parent_component()
1142+
except AttributeError:
1143+
# Maintaining current behavior, but perhaps this should raise an
1144+
# exception?
1145+
return
1146+
if obj is not name_or_object:
1147+
raise ValueError(
1148+
"Argument '%s' to del_component is a ComponentData object. "
1149+
"Please use the Python 'del' function to delete members of "
1150+
"indexed Pyomo components. The del_component function can "
1151+
"only be used to delete IndexedComponents and "
1152+
"ScalarComponents." % name_or_object.local_name
1153+
)
1154+
if obj.parent_block() is not self:
1155+
return
11361156

11371157
name = obj.local_name
1158+
11381159
if name in self._Block_reserved_words:
11391160
raise ValueError(
11401161
"Attempting to delete a reserved block component:\n\t%s" % (obj.name,)

pyomo/core/base/component.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -732,11 +732,7 @@ class ComponentData(ComponentBase):
732732
This is the base class for the component data used
733733
in Pyomo modeling components. Subclasses of ComponentData are
734734
used in indexed components, and this class assumes that indexed
735-
components are subclasses of IndexedComponent. Note that
736-
ComponentData instances do not store their index. This makes
737-
some operations significantly more expensive, but these are (a)
738-
associated with I/O generation and (b) this cost can be managed
739-
with caches.
735+
components are subclasses of IndexedComponent.
740736
741737
Constructor arguments:
742738
owner The component that owns this data object

pyomo/core/tests/unit/test_block.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
ComponentUID,
5151
Any,
5252
)
53+
from pyomo.common.collections import ComponentSet
5354
from pyomo.common.log import LoggingIntercept
5455
from pyomo.common.tempfiles import TempfileManager
5556
from pyomo.core.base.block import (
@@ -1345,6 +1346,37 @@ def test_add_del_component(self):
13451346
self.assertFalse('x' in m.__dict__)
13461347
self.assertIs(m.component('x'), None)
13471348

1349+
def test_del_component_data(self):
1350+
m = ConcreteModel()
1351+
self.assertFalse(m.contains_component(Var))
1352+
x = m.x = Var([1, 2, 3])
1353+
self.assertTrue(m.contains_component(Var))
1354+
self.assertIs(m.component('x'), x)
1355+
del m.x[1]
1356+
self.assertTrue(m.contains_component(Var))
1357+
self.assertTrue('x' in m.__dict__)
1358+
self.assertEqual(len(m.x), 2)
1359+
self.assertIn(m.x[2], ComponentSet(m.x.values()))
1360+
self.assertIn(m.x[3], ComponentSet(m.x.values()))
1361+
1362+
# This fails:
1363+
with self.assertRaisesRegex(
1364+
ValueError,
1365+
r"Argument 'x\[2\]' to del_component is a ComponentData object. "
1366+
r"Please use the Python 'del' function to delete members of "
1367+
r"indexed Pyomo components. The del_component function can "
1368+
r"only be used to delete IndexedComponents and "
1369+
r"ScalarComponents.",
1370+
):
1371+
m.del_component(m.x[2])
1372+
1373+
# But we can use del
1374+
del m.x[2]
1375+
self.assertTrue(m.contains_component(Var))
1376+
self.assertTrue('x' in m.__dict__)
1377+
self.assertEqual(len(m.x), 1)
1378+
self.assertIn(m.x[3], ComponentSet(m.x.values()))
1379+
13481380
def test_reclassify_component(self):
13491381
m = Block()
13501382
m.a = Var()

0 commit comments

Comments
 (0)