From ea6621b95c835f9d83e944959697a715290c2adb Mon Sep 17 00:00:00 2001 From: Robert Parker Date: Tue, 13 Aug 2024 11:18:30 -0600 Subject: [PATCH] scale named expressions; don't remove from constraints when applying transformation --- pyomo/core/plugins/transform/scaling.py | 25 ++++++++++++++++------ pyomo/core/tests/transform/test_scaling.py | 15 +++++++++++++ 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/pyomo/core/plugins/transform/scaling.py b/pyomo/core/plugins/transform/scaling.py index 11d4ac8c493..434a81e2247 100644 --- a/pyomo/core/plugins/transform/scaling.py +++ b/pyomo/core/plugins/transform/scaling.py @@ -10,7 +10,7 @@ # ___________________________________________________________________________ from pyomo.common.collections import ComponentMap -from pyomo.core.base import Block, Var, Constraint, Objective, Suffix, value +from pyomo.core.base import Block, Var, Constraint, Objective, Suffix, value, Expression from pyomo.core.plugins.transform.hierarchy import Transformation from pyomo.core.base import TransformationFactory from pyomo.core.base.suffix import SuffixFinder @@ -174,7 +174,7 @@ def _apply_to(self, model, rename=True): already_scaled = set() for component in model.component_objects( - ctype=(Constraint, Objective), descend_into=True + ctype=(Constraint, Objective, Expression), descend_into=True ): if component.is_reference(): # Skip any references - these should get picked up when @@ -187,13 +187,13 @@ def _apply_to(self, model, rename=True): continue already_scaled.add(id(c)) # perform the constraint/objective scaling and variable sub - scaling_factor = component_scaling_factor_map[c] if c.ctype is Constraint: + scaling_factor = component_scaling_factor_map[c] body = scaling_factor * replace_expressions( expr=c.body, substitution_map=variable_substitution_dict, - descend_into_named_expressions=True, - remove_named_expressions=True, + descend_into_named_expressions=False, + remove_named_expressions=False, ) # scale the rhs @@ -218,11 +218,22 @@ def _apply_to(self, model, rename=True): c.set_value((lower, body, upper)) elif c.ctype is Objective: + scaling_factor = component_scaling_factor_map[c] c.expr = scaling_factor * replace_expressions( expr=c.expr, substitution_map=variable_substitution_dict, - descend_into_named_expressions=True, - remove_named_expressions=True, + descend_into_named_expressions=False, + remove_named_expressions=False, + ) + elif c.ctype is Expression: + # Note that we don't have a scaling factor for Expression objects. + # If we want to add this, Expressions would need to be added to + # component_scaling_factor_map, defined above. + c.expr = replace_expressions( + expr=c.expr, + substitution_map=variable_substitution_dict, + descend_into_named_expressions=False, + remove_named_expressions=False, ) else: raise NotImplementedError( diff --git a/pyomo/core/tests/transform/test_scaling.py b/pyomo/core/tests/transform/test_scaling.py index d0fbfab61bd..494b4d12552 100644 --- a/pyomo/core/tests/transform/test_scaling.py +++ b/pyomo/core/tests/transform/test_scaling.py @@ -690,6 +690,21 @@ def test_get_float_scaling_factor_intermediate_level(self): sf = ScaleModel()._get_float_scaling_factor(m.b1.b2.b3.v3) assert sf == float(0.3) + def test_scaled_named_expressions(self): + m = pyo.ConcreteModel() + m.x = pyo.Var(initialize=10) + m.y = pyo.Var(initialize=0.1) + m.inner_expr = pyo.Expression(expr=3 + m.x**2 + m.y) + m.sum = pyo.Expression(expr=m.x + m.y + m.inner_expr) + m.scaling_factor = pyo.Suffix(direction=pyo.Suffix.EXPORT) + m.scaling_factor[m.x] = 0.1 + m.scaling_factor[m.y] = 10 + m_scaled = pyo.TransformationFactory('core.scale_model').create_using( + m, rename=False + ) + self.assertAlmostEqual(pyo.value(m.sum), 113.2) + self.assertAlmostEqual(pyo.value(m_scaled.sum), 113.2) + if __name__ == "__main__": unittest.main()