From bf78e46e3aff3bd9a4402bc082d56f959c2e0de7 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 10:14:26 -0700 Subject: [PATCH 01/43] Delete Suffix APIs deprecated since Pyomo 4.1.x --- pyomo/core/base/suffix.py | 100 -------------------------------------- 1 file changed, 100 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 46a87523001..a056c2bbaef 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -262,13 +262,6 @@ def direction(self, direction): ) self._direction = direction - @deprecated( - 'Suffix.exportEnabled is replaced with Suffix.export_enabled.', - version='4.1.10486', - ) - def exportEnabled(self): - return self.export_enabled() - def export_enabled(self): """ Returns True when this suffix is enabled for export to @@ -276,13 +269,6 @@ def export_enabled(self): """ return bool(self._direction & Suffix.EXPORT) - @deprecated( - 'Suffix.importEnabled is replaced with Suffix.import_enabled.', - version='4.1.10486', - ) - def importEnabled(self): - return self.import_enabled() - def import_enabled(self): """ Returns True when this suffix is enabled for import from @@ -290,13 +276,6 @@ def import_enabled(self): """ return bool(self._direction & Suffix.IMPORT) - @deprecated( - 'Suffix.updateValues is replaced with Suffix.update_values.', - version='4.1.10486', - ) - def updateValues(self, data, expand=True): - return self.update_values(data, expand) - def update_values(self, data, expand=True): """ Updates the suffix data given a list of component,value @@ -316,12 +295,6 @@ def update_values(self, data, expand=True): # As implemented by MutableMapping self.update(data) - @deprecated( - 'Suffix.setValue is replaced with Suffix.set_value.', version='4.1.10486' - ) - def setValue(self, component, value, expand=True): - return self.set_value(component, value, expand) - def set_value(self, component, value, expand=True): """ Sets the value of this suffix on the specified component. @@ -339,13 +312,6 @@ def set_value(self, component, value, expand=True): else: self[component] = value - @deprecated( - 'Suffix.setAllValues is replaced with Suffix.set_all_values.', - version='4.1.10486', - ) - def setAllValues(self, value): - return self.set_all_values(value) - def set_all_values(self, value): """ Sets the value of this suffix on all components. @@ -353,12 +319,6 @@ def set_all_values(self, value): for ndx in self: self[ndx] = value - @deprecated( - 'Suffix.clearValue is replaced with Suffix.clear_value.', version='4.1.10486' - ) - def clearValue(self, component, expand=True): - return self.clear_value(component, expand) - def clear_value(self, component, expand=True): """ Clears suffix information for a component. @@ -375,25 +335,12 @@ def clear_value(self, component, expand=True): except KeyError: pass - @deprecated( - 'Suffix.clearAllValues is replaced with Suffix.clear_all_values.', - version='4.1.10486', - ) - def clearAllValues(self): - return self.clear_all_values() - def clear_all_values(self): """ Clears all suffix data. """ self.clear() - @deprecated( - 'Suffix.setDatatype is replaced with Suffix.set_datatype.', version='4.1.10486' - ) - def setDatatype(self, datatype): - return self.set_datatype(datatype) - def set_datatype(self, datatype): """ Set the suffix datatype. @@ -406,25 +353,12 @@ def set_datatype(self, datatype): ) self._datatype = datatype - @deprecated( - 'Suffix.getDatatype is replaced with Suffix.get_datatype.', version='4.1.10486' - ) - def getDatatype(self): - return self.get_datatype() - def get_datatype(self): """ Return the suffix datatype. """ return self._datatype - @deprecated( - 'Suffix.setDirection is replaced with Suffix.set_direction.', - version='4.1.10486', - ) - def setDirection(self, direction): - return self.set_direction(direction) - def set_direction(self, direction): """ Set the suffix direction. @@ -437,13 +371,6 @@ def set_direction(self, direction): ) self._direction = direction - @deprecated( - 'Suffix.getDirection is replaced with Suffix.get_direction.', - version='4.1.10486', - ) - def getDirection(self): - return self.get_direction() - def get_direction(self): """ Return the suffix direction. @@ -471,33 +398,6 @@ def _pprint(self): lambda k, v: [v], ) - # TODO: delete - @deprecated( - 'Suffix.getValue is replaced with the dict-interface method Suffix.get.', - version='4.1.10486', - ) - def getValue(self, component, *args): - """ - Returns the current value of this suffix for the specified - component. - """ - # As implemented by MutableMapping - return self.get(component, *args) - - # TODO: delete - @deprecated( - 'Suffix.extractValues() is replaced with ' - 'the dict-interface method Suffix.items().', - version='4.1.10486', - ) - def extractValues(self): - """ - Extract all data stored on this Suffix into a list of - component, value tuples. - """ - # As implemented by MutableMapping - return list(self.items()) - # # Override a few methods to make sure the ActiveComponent versions are # called. We can't just switch the inheritance order due to From eb486b6572513aa462ae3d42bbd07b582335954a Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 10:15:05 -0700 Subject: [PATCH 02/43] Remove unreachable code --- pyomo/core/base/suffix.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index a056c2bbaef..4f1e5e93eb4 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -377,16 +377,6 @@ def get_direction(self): """ return self._direction - def __str__(self): - """ - Return a string representation of the suffix. If the name - attribute is None, then return '' - """ - name = self.name - if name is None: - return '' - return name - def _pprint(self): return ( [ From f503beefda0112ab519b03e3e7c1964a5d2ad2f5 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 11:54:52 -0700 Subject: [PATCH 03/43] Convert Suffix direction, datatype to Enums --- pyomo/core/base/suffix.py | 89 +++++++++++----------------- pyomo/core/tests/unit/test_suffix.py | 77 +++++++++--------------- 2 files changed, 64 insertions(+), 102 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 4f1e5e93eb4..6d406be6116 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -134,6 +134,22 @@ def suffix_generator(a_block, datatype=False): yield name, suffix +class SuffixDataType(enum.IntEnum): + INT = 0 + FLOAT = 4 + + +class SuffixDirection(enum.IntEnum): + LOCAL = 0 + EXPORT = 1 + IMPORT = 2 + IMPORT_EXPORT = 3 + + +_SuffixDataTypeDomain = In(SuffixDataType) +_SuffixDirectionDomain = In(SuffixDirection) + + @ModelComponentFactory.register("Declare a container for extraneous model data") class Suffix(ComponentMap, ActiveComponent): """A model suffix, representing extraneous model data""" @@ -148,28 +164,17 @@ class Suffix(ComponentMap, ActiveComponent): """ # Suffix Directions: - # If more directions are added be sure to update the error message - # in the setDirection method - # neither sent to solver or received from solver - LOCAL = 0 - # sent to solver or other external location - EXPORT = 1 - # obtained from solver or other external source - IMPORT = 2 - IMPORT_EXPORT = 3 # both - - SuffixDirections = (LOCAL, EXPORT, IMPORT, IMPORT_EXPORT) - SuffixDirectionToStr = { - LOCAL: 'Suffix.LOCAL', - EXPORT: 'Suffix.EXPORT', - IMPORT: 'Suffix.IMPORT', - IMPORT_EXPORT: 'Suffix.IMPORT_EXPORT', - } - # Suffix Datatypes - FLOAT = 4 - INT = 0 - SuffixDatatypes = (FLOAT, INT, None) - SuffixDatatypeToStr = {FLOAT: 'Suffix.FLOAT', INT: 'Suffix.INT', None: str(None)} + # - neither sent to solver or received from solver + LOCAL = SuffixDirection.LOCAL + # - sent to solver or other external location + EXPORT = SuffixDirection.EXPORT + # - obtained from solver or other external source + IMPORT = SuffixDirection.IMPORT + # - both import and export + IMPORT_EXPORT = SuffixDirection.IMPORT_EXPORT + + FLOAT = SuffixDataType.FLOAT + INT = SuffixDataType.INT @overload def __init__( @@ -239,11 +244,8 @@ def datatype(self): @datatype.setter def datatype(self, datatype): """Set the suffix datatype.""" - if datatype not in self.SuffixDatatypeToStr: - raise ValueError( - "Suffix datatype must be one of: %s. \n" - "Value given: %s" % (list(self.SuffixDatatypeToStr.values()), datatype) - ) + if datatype is not None: + datatype = _SuffixDataTypeDomain(datatype) self._datatype = datatype @property @@ -254,12 +256,8 @@ def direction(self): @direction.setter def direction(self, direction): """Set the suffix direction.""" - if direction not in self.SuffixDirectionToStr: - raise ValueError( - "Suffix direction must be one of: %s. \n" - "Value given: %s" - % (list(self.SuffixDirectionToStr.values()), direction) - ) + if direction is not None: + direction = _SuffixDirectionDomain(direction) self._direction = direction def export_enabled(self): @@ -345,44 +343,29 @@ def set_datatype(self, datatype): """ Set the suffix datatype. """ - if datatype not in self.SuffixDatatypes: - raise ValueError( - "Suffix datatype must be one of: %s. \n" - "Value given: %s" - % (list(Suffix.SuffixDatatypeToStr.values()), datatype) - ) - self._datatype = datatype + self.datatype = datatype def get_datatype(self): """ Return the suffix datatype. """ - return self._datatype + return self.datatype def set_direction(self, direction): """ Set the suffix direction. """ - if direction not in self.SuffixDirections: - raise ValueError( - "Suffix direction must be one of: %s. \n" - "Value given: %s" - % (list(self.SuffixDirectionToStr.values()), direction) - ) - self._direction = direction + self.direction = direction def get_direction(self): """ Return the suffix direction. """ - return self._direction + return self.direction def _pprint(self): return ( - [ - ('Direction', self.SuffixDirectionToStr[self._direction]), - ('Datatype', self.SuffixDatatypeToStr[self._datatype]), - ], + [('Direction', str(self._direction)), ('Datatype', str(self._datatype))], ((str(k), v) for k, v in self._dict.values()), ("Value",), lambda k, v: [v], diff --git a/pyomo/core/tests/unit/test_suffix.py b/pyomo/core/tests/unit/test_suffix.py index 131e2054284..ddfa9385f5a 100644 --- a/pyomo/core/tests/unit/test_suffix.py +++ b/pyomo/core/tests/unit/test_suffix.py @@ -55,19 +55,6 @@ def simple_obj_rule(model, i): class TestSuffixMethods(unittest.TestCase): - # test __init__ - def test_init(self): - model = ConcreteModel() - # no keywords - model.junk = Suffix() - model.del_component('junk') - - for direction, datatype in itertools.product( - Suffix.SuffixDirections, Suffix.SuffixDatatypes - ): - model.junk = Suffix(direction=direction, datatype=datatype) - model.del_component('junk') - # test import_enabled def test_import_enabled(self): model = ConcreteModel() @@ -853,45 +840,37 @@ def test_clear_all_values(self): def test_set_datatype_get_datatype(self): model = ConcreteModel() model.junk = Suffix(datatype=Suffix.FLOAT) - self.assertTrue(model.junk.get_datatype() is Suffix.FLOAT) - model.junk.set_datatype(Suffix.INT) - self.assertTrue(model.junk.get_datatype() is Suffix.INT) - model.junk.set_datatype(None) - self.assertTrue(model.junk.get_datatype() is None) - - # test that calling set_datatype with a bad value fails - def test_set_datatype_badvalue(self): - model = ConcreteModel() - model.junk = Suffix() - try: - model.junk.set_datatype(1.0) - except ValueError: - pass - else: - self.fail("Calling set_datatype with a bad type should fail.") + self.assertEqual(model.junk.datatype, Suffix.FLOAT) + model.junk.datatype = Suffix.INT + self.assertEqual(model.junk.datatype, Suffix.INT) + model.junk.datatype = None + self.assertEqual(model.junk.datatype, None) + model.junk.datatype = 'FLOAT' + self.assertEqual(model.junk.datatype, Suffix.FLOAT) + model.junk.datatype = 'INT' + self.assertEqual(model.junk.datatype, Suffix.INT) + model.junk.datatype = 4 + self.assertEqual(model.junk.datatype, Suffix.FLOAT) + model.junk.datatype = 0 + self.assertEqual(model.junk.datatype, Suffix.INT) + + with self.assertRaisesRegex(ValueError, "1.0 is not a valid SuffixDataType"): + model.junk.datatype = 1.0 # test set_direction and get_direction def test_set_direction_get_direction(self): model = ConcreteModel() model.junk = Suffix(direction=Suffix.LOCAL) - self.assertTrue(model.junk.get_direction() is Suffix.LOCAL) - model.junk.set_direction(Suffix.EXPORT) - self.assertTrue(model.junk.get_direction() is Suffix.EXPORT) - model.junk.set_direction(Suffix.IMPORT) - self.assertTrue(model.junk.get_direction() is Suffix.IMPORT) - model.junk.set_direction(Suffix.IMPORT_EXPORT) - self.assertTrue(model.junk.get_direction() is Suffix.IMPORT_EXPORT) + self.assertEqual(model.junk.direction, Suffix.LOCAL) + model.junk.direction = Suffix.EXPORT + self.assertEqual(model.junk.direction, Suffix.EXPORT) + model.junk.direction = Suffix.IMPORT + self.assertEqual(model.junk.direction, Suffix.IMPORT) + model.junk.direction = Suffix.IMPORT_EXPORT + self.assertEqual(model.junk.direction, Suffix.IMPORT_EXPORT) - # test that calling set_direction with a bad value fails - def test_set_direction_badvalue(self): - model = ConcreteModel() - model.junk = Suffix() - try: - model.junk.set_direction('a') - except ValueError: - pass - else: - self.fail("Calling set_datatype with a bad type should fail.") + with self.assertRaisesRegex(ValueError, "'a' is not a valid SuffixDirection"): + model.junk.direction = 'a' # test __str__ def test_str(self): @@ -905,11 +884,11 @@ def test_pprint(self): model.junk = Suffix(direction=Suffix.EXPORT) output = StringIO() model.junk.pprint(ostream=output) - model.junk.set_direction(Suffix.IMPORT) + model.junk.direction = Suffix.IMPORT model.junk.pprint(ostream=output) - model.junk.set_direction(Suffix.LOCAL) + model.junk.direction = Suffix.LOCAL model.junk.pprint(ostream=output) - model.junk.set_direction(Suffix.IMPORT_EXPORT) + model.junk.direction = Suffix.IMPORT_EXPORT model.junk.pprint(ostream=output) model.pprint(ostream=output) From 35d612786436997768bb31f4d892738759485635 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 11:56:40 -0700 Subject: [PATCH 04/43] Promote _pop_from_kwargs() utility from IndexComponent to Component --- pyomo/core/base/component.py | 21 +++++++++++++++++++++ pyomo/core/base/indexed_component.py | 21 --------------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/pyomo/core/base/component.py b/pyomo/core/base/component.py index a8550f8f469..bb855bd6f8d 100644 --- a/pyomo/core/base/component.py +++ b/pyomo/core/base/component.py @@ -723,6 +723,27 @@ def get_suffix_value(self, suffix_or_name, default=None): else: return suffix_or_name.get(self, default) + def _pop_from_kwargs(self, name, kwargs, namelist, notset=None): + args = [ + arg + for arg in (kwargs.pop(name, notset) for name in namelist) + if arg is not notset + ] + if len(args) == 1: + return args[0] + elif not args: + return notset + else: + argnames = "%s%s '%s='" % ( + ', '.join("'%s='" % _ for _ in namelist[:-1]), + ',' if len(namelist) > 2 else '', + namelist[-1], + ) + raise ValueError( + "Duplicate initialization: %s() only accepts one of %s" + % (name, argnames) + ) + class ActiveComponent(Component): """A Component that makes semantic sense to activate or deactivate diff --git a/pyomo/core/base/indexed_component.py b/pyomo/core/base/indexed_component.py index b474281f5b9..b1a158b5e18 100644 --- a/pyomo/core/base/indexed_component.py +++ b/pyomo/core/base/indexed_component.py @@ -746,27 +746,6 @@ def __delitem__(self, index): self._data[index]._component = None del self._data[index] - def _pop_from_kwargs(self, name, kwargs, namelist, notset=None): - args = [ - arg - for arg in (kwargs.pop(name, notset) for name in namelist) - if arg is not notset - ] - if len(args) == 1: - return args[0] - elif not args: - return notset - else: - argnames = "%s%s '%s='" % ( - ', '.join("'%s='" % _ for _ in namelist[:-1]), - ',' if len(namelist) > 2 else '', - namelist[-1], - ) - raise ValueError( - "Duplicate initialization: %s() only accepts one of %s" - % (name, argnames) - ) - def _construct_from_rule_using_setitem(self): if self._rule is None: return From 001ca4c45f23d6ce3fd601506566dfd4eb77ff5c Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 11:57:17 -0700 Subject: [PATCH 05/43] Remove mostly repeated code --- pyomo/core/base/suffix.py | 135 +++++++++++--------------------------- 1 file changed, 39 insertions(+), 96 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 6d406be6116..074d5ad4d2e 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -36,102 +36,45 @@ # - suffix_generator -def active_export_suffix_generator(a_block, datatype=False): - if datatype is False: - for name, suffix in a_block.component_map(Suffix, active=True).items(): - if suffix.export_enabled() is True: - yield name, suffix - else: - for name, suffix in a_block.component_map(Suffix, active=True).items(): - if (suffix.export_enabled() is True) and ( - suffix.get_datatype() is datatype - ): - yield name, suffix - - -def export_suffix_generator(a_block, datatype=False): - if datatype is False: - for name, suffix in a_block.component_map(Suffix).items(): - if suffix.export_enabled() is True: - yield name, suffix - else: - for name, suffix in a_block.component_map(Suffix).items(): - if (suffix.export_enabled() is True) and ( - suffix.get_datatype() is datatype - ): - yield name, suffix - - -def active_import_suffix_generator(a_block, datatype=False): - if datatype is False: - for name, suffix in a_block.component_map(Suffix, active=True).items(): - if suffix.import_enabled() is True: - yield name, suffix - else: - for name, suffix in a_block.component_map(Suffix, active=True).items(): - if (suffix.import_enabled() is True) and ( - suffix.get_datatype() is datatype - ): - yield name, suffix - - -def import_suffix_generator(a_block, datatype=False): - if datatype is False: - for name, suffix in a_block.component_map(Suffix).items(): - if suffix.import_enabled() is True: - yield name, suffix - else: - for name, suffix in a_block.component_map(Suffix).items(): - if (suffix.import_enabled() is True) and ( - suffix.get_datatype() is datatype - ): - yield name, suffix - - -def active_local_suffix_generator(a_block, datatype=False): - if datatype is False: - for name, suffix in a_block.component_map(Suffix, active=True).items(): - if suffix.get_direction() is Suffix.LOCAL: - yield name, suffix - else: - for name, suffix in a_block.component_map(Suffix, active=True).items(): - if (suffix.get_direction() is Suffix.LOCAL) and ( - suffix.get_datatype() is datatype - ): - yield name, suffix - - -def local_suffix_generator(a_block, datatype=False): - if datatype is False: - for name, suffix in a_block.component_map(Suffix).items(): - if suffix.get_direction() is Suffix.LOCAL: - yield name, suffix - else: - for name, suffix in a_block.component_map(Suffix).items(): - if (suffix.get_direction() is Suffix.LOCAL) and ( - suffix.get_datatype() is datatype - ): - yield name, suffix - - -def active_suffix_generator(a_block, datatype=False): - if datatype is False: - for name, suffix in a_block.component_map(Suffix, active=True).items(): - yield name, suffix - else: - for name, suffix in a_block.component_map(Suffix, active=True).items(): - if suffix.get_datatype() is datatype: - yield name, suffix - - -def suffix_generator(a_block, datatype=False): - if datatype is False: - for name, suffix in a_block.component_map(Suffix).items(): - yield name, suffix - else: - for name, suffix in a_block.component_map(Suffix).items(): - if suffix.get_datatype() is datatype: - yield name, suffix +def suffix_generator(a_block, datatype=NOTSET, direction=NOTSET, active=None): + _iter = a_block.component_map(Suffix, active=active).items() + if direction is not NOTSET: + direction = _SuffixDirectionDomain(direction) + if not direction: + _iter = filter(lambda item: item[1].direction == direction, _iter) + else: + _iter = filter(lambda item: item[1].direction & direction, _iter) + if datatype is not NOTSET: + _iter = filter(lambda item: item[1].datatype == datatype, _iter) + return _iter + + +def active_export_suffix_generator(a_block, datatype=NOTSET): + return suffix_generator(a_block, datatype, SuffixDirection.EXPORT, True) + + +def export_suffix_generator(a_block, datatype=NOTSET): + return suffix_generator(a_block, datatype, SuffixDirection.EXPORT) + + +def active_import_suffix_generator(a_block, datatype=NOTSET): + return suffix_generator(a_block, datatype, SuffixDirection.IMPORT, True) + + +def import_suffix_generator(a_block, datatype=NOTSET): + return suffix_generator(a_block, datatype, SuffixDirection.IMPORT) + + +def active_local_suffix_generator(a_block, datatype=NOTSET): + return suffix_generator(a_block, datatype, SuffixDirection.LOCAL, True) + + +def local_suffix_generator(a_block, datatype=NOTSET): + return suffix_generator(a_block, datatype, SuffixDirection.LOCAL) + + +def active_suffix_generator(a_block, datatype=NOTSET): + return suffix_generator(a_block, datatype, active=True) class SuffixDataType(enum.IntEnum): From 3b6cfa44dc5a33bbd250d757a6c6af6a39169b9a Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 11:58:16 -0700 Subject: [PATCH 06/43] Move to using Initializer() in Suffix --- pyomo/core/base/suffix.py | 43 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 074d5ad4d2e..4f9d299a90a 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -11,15 +11,18 @@ __all__ = ('Suffix', 'active_export_suffix_generator', 'active_import_suffix_generator') +import enum import logging -from pyomo.common.pyomo_typing import overload from pyomo.common.collections import ComponentMap +from pyomo.common.config import In +from pyomo.common.deprecation import deprecated from pyomo.common.log import is_debug_set +from pyomo.common.modeling import NOTSET +from pyomo.common.pyomo_typing import overload from pyomo.common.timing import ConstructionTimer from pyomo.core.base.component import ActiveComponent, ModelComponentFactory - -from pyomo.common.deprecation import deprecated +from pyomo.core.base.initializer import Initializer logger = logging.getLogger('pyomo.core') @@ -132,31 +135,28 @@ def __init__( ): ... - def __init__(self, **kwds): + def __init__(self, **kwargs): # Suffix type information self._direction = None self._datatype = None self._rule = None - # The suffix direction - direction = kwds.pop('direction', Suffix.LOCAL) + # The suffix direction (note the setter performs error chrcking) + self.direction = kwargs.pop('direction', Suffix.LOCAL) - # The suffix datatype - datatype = kwds.pop('datatype', Suffix.FLOAT) + # The suffix datatype (note the setter performs error chrcking) + self.datatype = kwargs.pop('datatype', Suffix.FLOAT) # The suffix construction rule # TODO: deprecate the use of 'rule' - self._rule = kwds.pop('rule', None) - self._rule = kwds.pop('initialize', self._rule) - - # Check that keyword values make sense (these function have - # internal error checking). - self.set_direction(direction) - self.set_datatype(datatype) + self._rule = Initializer( + self._pop_from_kwargs('Suffix', kwargs, ('rule', 'initialize'), None), + treat_sequences_as_mappings=False, + ) # Initialize base classes - kwds.setdefault('ctype', Suffix) - ActiveComponent.__init__(self, **kwds) + kwargs.setdefault('ctype', Suffix) + ActiveComponent.__init__(self, **kwargs) ComponentMap.__init__(self) if self._rule is None: @@ -176,7 +176,14 @@ def construct(self, data=None): self._constructed = True if self._rule is not None: - self.update_values(self._rule(self._parent())) + rule = self._rule + block = self.parent_block() + if rule.contains_indices(): + # The index is coming in externally; we need to validate it + for index in rule.indices(): + self[index] = rule(block, index) + else: + self.update_values(rule(block, None)) timer.report() @property From 6e5f8ee6a26087012beaedc637345997388bdb2e Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 11:58:45 -0700 Subject: [PATCH 07/43] Deprecate use of explicit datatype, direction setters and getters --- pyomo/core/base/suffix.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 4f9d299a90a..6f497b6e5fc 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -289,24 +289,40 @@ def clear_all_values(self): """ self.clear() + @deprecated( + 'Suffix.set_datatype is replaced with the Suffix.datatype property', + version='6.7.1.dev0', + ) def set_datatype(self, datatype): """ Set the suffix datatype. """ self.datatype = datatype + @deprecated( + 'Suffix.get_datatype is replaced with the Suffix.datatype property', + version='6.7.1.dev0', + ) def get_datatype(self): """ Return the suffix datatype. """ return self.datatype + @deprecated( + 'Suffix.set_direction is replaced with the Suffix.direction property', + version='6.7.1.dev0', + ) def set_direction(self, direction): """ Set the suffix direction. """ self.direction = direction + @deprecated( + 'Suffix.set_direction is replaced with the Suffix.direction property', + version='6.7.1.dev0', + ) def get_direction(self): """ Return the suffix direction. From aaf64289b6a62135cbc0c778cb9b36de9fcd65ac Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 11:59:13 -0700 Subject: [PATCH 08/43] Remove fragile use of 'is' --- pyomo/core/tests/unit/test_suffix.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/pyomo/core/tests/unit/test_suffix.py b/pyomo/core/tests/unit/test_suffix.py index ddfa9385f5a..6bd14c8f70b 100644 --- a/pyomo/core/tests/unit/test_suffix.py +++ b/pyomo/core/tests/unit/test_suffix.py @@ -58,33 +58,39 @@ class TestSuffixMethods(unittest.TestCase): # test import_enabled def test_import_enabled(self): model = ConcreteModel() + model.test_implicit = Suffix() + self.assertFalse(model.test_implicit.import_enabled()) + model.test_local = Suffix(direction=Suffix.LOCAL) - self.assertTrue(model.test_local.import_enabled() is False) + self.assertFalse(model.test_local.import_enabled()) model.test_out = Suffix(direction=Suffix.IMPORT) - self.assertTrue(model.test_out.import_enabled() is True) + self.assertTrue(model.test_out.import_enabled()) model.test_in = Suffix(direction=Suffix.EXPORT) - self.assertTrue(model.test_in.import_enabled() is False) + self.assertFalse(model.test_in.import_enabled()) model.test_inout = Suffix(direction=Suffix.IMPORT_EXPORT) - self.assertTrue(model.test_inout.import_enabled() is True) + self.assertTrue(model.test_inout.import_enabled()) # test export_enabled def test_export_enabled(self): model = ConcreteModel() + model.test_implicit = Suffix() + self.assertFalse(model.test_implicit.export_enabled()) + model.test_local = Suffix(direction=Suffix.LOCAL) - self.assertTrue(model.test_local.export_enabled() is False) + self.assertFalse(model.test_local.export_enabled()) model.test_out = Suffix(direction=Suffix.IMPORT) - self.assertTrue(model.test_out.export_enabled() is False) + self.assertFalse(model.test_out.export_enabled()) model.test_in = Suffix(direction=Suffix.EXPORT) - self.assertTrue(model.test_in.export_enabled() is True) + self.assertTrue(model.test_in.export_enabled()) model.test_inout = Suffix(direction=Suffix.IMPORT_EXPORT) - self.assertTrue(model.test_inout.export_enabled() is True) + self.assertTrue(model.test_inout.export_enabled()) # test set_value and getValue # and if Var arrays are correctly expanded From a0fce3de856996f04fecc6a30d6f7aaf1f886377 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 12:15:37 -0700 Subject: [PATCH 09/43] Test deprecated getters/setters --- pyomo/core/base/suffix.py | 2 +- pyomo/core/tests/unit/test_suffix.py | 33 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 6f497b6e5fc..25f15b890c3 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -320,7 +320,7 @@ def set_direction(self, direction): self.direction = direction @deprecated( - 'Suffix.set_direction is replaced with the Suffix.direction property', + 'Suffix.get_direction is replaced with the Suffix.direction property', version='6.7.1.dev0', ) def get_direction(self): diff --git a/pyomo/core/tests/unit/test_suffix.py b/pyomo/core/tests/unit/test_suffix.py index 6bd14c8f70b..c5b80a0cedd 100644 --- a/pyomo/core/tests/unit/test_suffix.py +++ b/pyomo/core/tests/unit/test_suffix.py @@ -20,6 +20,7 @@ currdir = dirname(abspath(__file__)) + os.sep import pyomo.common.unittest as unittest +from pyomo.common.log import LoggingIntercept from pyomo.core.base.suffix import ( active_export_suffix_generator, export_suffix_generator, @@ -860,6 +861,22 @@ def test_set_datatype_get_datatype(self): model.junk.datatype = 0 self.assertEqual(model.junk.datatype, Suffix.INT) + with LoggingIntercept() as LOG: + model.junk.set_datatype(None) + self.assertEqual(model.junk.datatype, None) + self.assertRegex( + LOG.getvalue().replace("\n", " "), + "^DEPRECATED: Suffix.set_datatype is replaced with the Suffix.datatype property", + ) + + model.junk.datatype = 'FLOAT' + with LoggingIntercept() as LOG: + self.assertEqual(model.junk.get_datatype(), Suffix.FLOAT) + self.assertRegex( + LOG.getvalue().replace("\n", " "), + "^DEPRECATED: Suffix.get_datatype is replaced with the Suffix.datatype property", + ) + with self.assertRaisesRegex(ValueError, "1.0 is not a valid SuffixDataType"): model.junk.datatype = 1.0 @@ -875,6 +892,22 @@ def test_set_direction_get_direction(self): model.junk.direction = Suffix.IMPORT_EXPORT self.assertEqual(model.junk.direction, Suffix.IMPORT_EXPORT) + with LoggingIntercept() as LOG: + model.junk.set_direction(None) + self.assertEqual(model.junk.direction, None) + self.assertRegex( + LOG.getvalue().replace("\n", " "), + "^DEPRECATED: Suffix.set_direction is replaced with the Suffix.direction property", + ) + + model.junk.direction = 'IMPORT' + with LoggingIntercept() as LOG: + self.assertEqual(model.junk.get_direction(), Suffix.IMPORT) + self.assertRegex( + LOG.getvalue().replace("\n", " "), + "^DEPRECATED: Suffix.get_direction is replaced with the Suffix.direction property", + ) + with self.assertRaisesRegex(ValueError, "'a' is not a valid SuffixDirection"): model.junk.direction = 'a' From 1a3f9407e90a0ec9fda00c53fc4e3fad23b9fdd3 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 12:15:58 -0700 Subject: [PATCH 10/43] NFC: documentation --- pyomo/core/base/suffix.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 25f15b890c3..70141353a16 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -81,11 +81,37 @@ def active_suffix_generator(a_block, datatype=NOTSET): class SuffixDataType(enum.IntEnum): + """Suffix data types + + AMPL only supports two data types for Suffixes: int and float. The + numeric values here are specific to the NL file format and should + not be changed without checking/updating the NL writer. + + """ + INT = 0 FLOAT = 4 class SuffixDirection(enum.IntEnum): + """Suffix data flow definition. + + This identifies if the specific Suffix is to be sent to the solver, + read from the solver output, both, or neither: + + - LOCAL: Suffix is local to Pyomo and should not be sent to or read + from the solver. + + - EXPORT: Suffix should be sent tot he solver as supplemental model + information. + + - IMPORT: Suffix values will be returned from the solver and should + be read from the solver output. + + - IMPORT_EXPORT: The Suffix is both an EXPORT and IMPORT suffix. + + """ + LOCAL = 0 EXPORT = 1 IMPORT = 2 @@ -109,6 +135,11 @@ class Suffix(ComponentMap, ActiveComponent): suffix. """ + # + # The following local (class) aliases are provided for backwards + # compatibility + # + # Suffix Directions: # - neither sent to solver or received from solver LOCAL = SuffixDirection.LOCAL From 8da7dbda8249514d9d70442779716ed5bf7fc09e Mon Sep 17 00:00:00 2001 From: John Siirola Date: Sun, 10 Dec 2023 12:37:05 -0700 Subject: [PATCH 11/43] Test (and fix) Suffix initialization from rule --- pyomo/core/base/suffix.py | 3 +- pyomo/core/tests/unit/test_suffix.py | 79 ++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 70141353a16..fb5de94381c 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -183,6 +183,7 @@ def __init__(self, **kwargs): self._rule = Initializer( self._pop_from_kwargs('Suffix', kwargs, ('rule', 'initialize'), None), treat_sequences_as_mappings=False, + allow_generators=True, ) # Initialize base classes @@ -212,7 +213,7 @@ def construct(self, data=None): if rule.contains_indices(): # The index is coming in externally; we need to validate it for index in rule.indices(): - self[index] = rule(block, index) + self.set_value(index, rule(block, index)) else: self.update_values(rule(block, None)) timer.report() diff --git a/pyomo/core/tests/unit/test_suffix.py b/pyomo/core/tests/unit/test_suffix.py index c5b80a0cedd..70f71002c9b 100644 --- a/pyomo/core/tests/unit/test_suffix.py +++ b/pyomo/core/tests/unit/test_suffix.py @@ -20,6 +20,7 @@ currdir = dirname(abspath(__file__)) + os.sep import pyomo.common.unittest as unittest +from pyomo.common.collections import ComponentMap from pyomo.common.log import LoggingIntercept from pyomo.core.base.suffix import ( active_export_suffix_generator, @@ -56,6 +57,84 @@ def simple_obj_rule(model, i): class TestSuffixMethods(unittest.TestCase): + def test_suffix_rule(self): + m = ConcreteModel() + m.I = Set(initialize=[1, 2, 3]) + m.x = Var(m.I) + m.y = Var(m.I) + m.c = Constraint(m.I, rule=lambda m, i: m.x[i] >= i) + m.d = Constraint(m.I, rule=lambda m, i: m.x[i] <= -i) + + _dict = {m.c[1]: 10, m.c[2]: 20, m.c[3]: 30, m.d: 100} + m.suffix_dict = Suffix(initialize=_dict) + self.assertEqual(len(m.suffix_dict), 6) + self.assertEqual(m.suffix_dict[m.c[1]], 10) + self.assertEqual(m.suffix_dict[m.c[2]], 20) + self.assertEqual(m.suffix_dict[m.c[3]], 30) + self.assertEqual(m.suffix_dict[m.d[1]], 100) + self.assertEqual(m.suffix_dict[m.d[2]], 100) + self.assertEqual(m.suffix_dict[m.d[3]], 100) + + # check double-construction + _dict[m.c[1]] = 1000 + m.suffix_dict.construct() + self.assertEqual(len(m.suffix_dict), 6) + self.assertEqual(m.suffix_dict[m.c[1]], 10) + + m.suffix_cmap = Suffix( + initialize=ComponentMap( + [(m.x[1], 10), (m.x[2], 20), (m.x[3], 30), (m.y, 100)] + ) + ) + self.assertEqual(len(m.suffix_dict), 6) + self.assertEqual(m.suffix_cmap[m.x[1]], 10) + self.assertEqual(m.suffix_cmap[m.x[2]], 20) + self.assertEqual(m.suffix_cmap[m.x[3]], 30) + self.assertEqual(m.suffix_cmap[m.y[1]], 100) + self.assertEqual(m.suffix_cmap[m.y[2]], 100) + self.assertEqual(m.suffix_cmap[m.y[3]], 100) + + m.suffix_list = Suffix( + initialize=[(m.x[1], 10), (m.x[2], 20), (m.x[3], 30), (m.y, 100)] + ) + self.assertEqual(len(m.suffix_dict), 6) + self.assertEqual(m.suffix_list[m.x[1]], 10) + self.assertEqual(m.suffix_list[m.x[2]], 20) + self.assertEqual(m.suffix_list[m.x[3]], 30) + self.assertEqual(m.suffix_list[m.y[1]], 100) + self.assertEqual(m.suffix_list[m.y[2]], 100) + self.assertEqual(m.suffix_list[m.y[3]], 100) + + def gen_init(): + yield (m.x[1], 10) + yield (m.x[2], 20) + yield (m.x[3], 30) + yield (m.y, 100) + + m.suffix_generator = Suffix(initialize=gen_init()) + self.assertEqual(len(m.suffix_dict), 6) + self.assertEqual(m.suffix_generator[m.x[1]], 10) + self.assertEqual(m.suffix_generator[m.x[2]], 20) + self.assertEqual(m.suffix_generator[m.x[3]], 30) + self.assertEqual(m.suffix_generator[m.y[1]], 100) + self.assertEqual(m.suffix_generator[m.y[2]], 100) + self.assertEqual(m.suffix_generator[m.y[3]], 100) + + def genfcn_init(m, i): + yield (m.x[1], 10) + yield (m.x[2], 20) + yield (m.x[3], 30) + yield (m.y, 100) + + m.suffix_generator_fcn = Suffix(initialize=genfcn_init) + self.assertEqual(len(m.suffix_dict), 6) + self.assertEqual(m.suffix_generator_fcn[m.x[1]], 10) + self.assertEqual(m.suffix_generator_fcn[m.x[2]], 20) + self.assertEqual(m.suffix_generator_fcn[m.x[3]], 30) + self.assertEqual(m.suffix_generator_fcn[m.y[1]], 100) + self.assertEqual(m.suffix_generator_fcn[m.y[2]], 100) + self.assertEqual(m.suffix_generator_fcn[m.y[3]], 100) + # test import_enabled def test_import_enabled(self): model = ConcreteModel() From ef37f10c80dbccf14d0dc82926d0353d6cb98172 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Wed, 13 Dec 2023 06:28:06 -0700 Subject: [PATCH 12/43] Update pprint of Suffix enums --- .../pyomobook/pyomo-components-ch/suffix_declaration.txt | 6 +++--- pyomo/core/base/suffix.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/pyomobook/pyomo-components-ch/suffix_declaration.txt b/examples/pyomobook/pyomo-components-ch/suffix_declaration.txt index d5e38a44dcc..e0237e3ef46 100644 --- a/examples/pyomobook/pyomo-components-ch/suffix_declaration.txt +++ b/examples/pyomobook/pyomo-components-ch/suffix_declaration.txt @@ -1,9 +1,9 @@ *** suffixsimple *** 2 Suffix Declarations - dual : Direction=Suffix.IMPORT_EXPORT, Datatype=Suffix.FLOAT + dual : Direction=IMPORT_EXPORT, Datatype=FLOAT Key : Value - priority : Direction=Suffix.EXPORT, Datatype=Suffix.INT + priority : Direction=EXPORT, Datatype=INT Key : Value 2 Declarations: priority dual @@ -16,7 +16,7 @@ Not constructed 1 Suffix Declarations - foo : Direction=Suffix.LOCAL, Datatype=Suffix.FLOAT + foo : Direction=LOCAL, Datatype=FLOAT Not constructed 3 Declarations: x c foo diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index fb5de94381c..0c3ba6741f0 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -363,7 +363,10 @@ def get_direction(self): def _pprint(self): return ( - [('Direction', str(self._direction)), ('Datatype', str(self._datatype))], + [ + ('Direction', str(self._direction.name)), + ('Datatype', str(self._datatype.name)), + ], ((str(k), v) for k, v in self._dict.values()), ("Value",), lambda k, v: [v], From 73ab378a5e7bbc502cc31d7a29c6951cae5bc3b8 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Wed, 13 Dec 2023 08:22:03 -0700 Subject: [PATCH 13/43] Improve robustness, performance of ComponentMap, COmponentSet.__eq__ --- pyomo/common/collections/component_map.py | 25 +++++++++++++------ pyomo/common/collections/component_set.py | 8 +++--- .../tests/unit/kernel/test_component_map.py | 14 +++++++++++ 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/pyomo/common/collections/component_map.py b/pyomo/common/collections/component_map.py index 47b88ce5914..4bb9e88e9af 100644 --- a/pyomo/common/collections/component_map.py +++ b/pyomo/common/collections/component_map.py @@ -98,16 +98,25 @@ def update(self, *args, **kwargs): return self._dict.update(args[0]._dict) return super().update(*args, **kwargs) - # We want to avoid generating Pyomo expressions due to - # comparison of values, so we convert both objects to a - # plain dictionary mapping key->(type(val), id(val)) and - # compare that instead. + # We want to avoid generating Pyomo expressions due to comparing the + # keys, so look up each entry from other in this dict. def __eq__(self, other): - if not isinstance(other, collections_Mapping): + if self is other: + return True + if not isinstance(other, collections_Mapping) or len(self) != len(other): return False - return {(type(key), id(key)): val for key, val in self.items()} == { - (type(key), id(key)): val for key, val in other.items() - } + # Note we have already verified the dicts are the same size + for key, val in other.items(): + other_id = id(key) + if other_id not in self._dict: + return False + self_val = self._dict[other_id][1] + # Note: check "is" first to help avoid creation of Pyomo + # expressions (for the case that the values contain the same + # pyomo component) + if self_val is not val and self_val != val: + return False + return True def __ne__(self, other): return not (self == other) diff --git a/pyomo/common/collections/component_set.py b/pyomo/common/collections/component_set.py index 0b16acd00be..ad13baced44 100644 --- a/pyomo/common/collections/component_set.py +++ b/pyomo/common/collections/component_set.py @@ -104,11 +104,11 @@ def discard(self, val): # plain dictionary mapping key->(type(val), id(val)) and # compare that instead. def __eq__(self, other): - if not isinstance(other, collections_Set): + if self is other: + return True + if not isinstance(other, collections_Set) or len(self) != len(other): return False - return set((type(val), id(val)) for val in self) == set( - (type(val), id(val)) for val in other - ) + return all(id(key) in self._data for key in other) def __ne__(self, other): return not (self == other) diff --git a/pyomo/core/tests/unit/kernel/test_component_map.py b/pyomo/core/tests/unit/kernel/test_component_map.py index 64ba700895e..6d19743c3fe 100644 --- a/pyomo/core/tests/unit/kernel/test_component_map.py +++ b/pyomo/core/tests/unit/kernel/test_component_map.py @@ -234,6 +234,20 @@ def test_eq(self): self.assertTrue(cmap1 != cmap2) self.assertNotEqual(cmap1, cmap2) + cmap2 = ComponentMap(self._components) + o = objective() + cmap1[o] = 10 + cmap2[o] = 10 + self.assertEqual(cmap1, cmap2) + cmap2[o] = 20 + self.assertNotEqual(cmap1, cmap2) + cmap2[o] = 10 + self.assertEqual(cmap1, cmap2) + del cmap2[o] + self.assertNotEqual(cmap1, cmap2) + cmap2[objective()] = 10 + self.assertNotEqual(cmap1, cmap2) + if __name__ == "__main__": unittest.main() From 098c7856e3d48f21c0f88b834649c96c9da8e79a Mon Sep 17 00:00:00 2001 From: John Siirola Date: Wed, 13 Dec 2023 08:51:18 -0700 Subject: [PATCH 14/43] None is not a valid Suffix direction --- pyomo/core/base/suffix.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 0c3ba6741f0..d218eef4234 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -238,9 +238,7 @@ def direction(self): @direction.setter def direction(self, direction): """Set the suffix direction.""" - if direction is not None: - direction = _SuffixDirectionDomain(direction) - self._direction = direction + self._direction = _SuffixDirectionDomain(direction) def export_enabled(self): """ @@ -365,7 +363,7 @@ def _pprint(self): return ( [ ('Direction', str(self._direction.name)), - ('Datatype', str(self._datatype.name)), + ('Datatype', getattr(self._datatype, 'name', 'None')), ], ((str(k), v) for k, v in self._dict.values()), ("Value",), From ed5dac8788b80d646211aeaeceaa87dc6e881337 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Wed, 13 Dec 2023 08:51:46 -0700 Subject: [PATCH 15/43] NFC: comments, logging --- pyomo/core/base/suffix.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index d218eef4234..2e0e6191f75 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -136,8 +136,8 @@ class Suffix(ComponentMap, ActiveComponent): """ # - # The following local (class) aliases are provided for backwards - # compatibility + # The following local (class) aliases are provided for convenience + # and backwards compatibility with The Book, 3rd ed # # Suffix Directions: @@ -199,7 +199,7 @@ def construct(self, data=None): Constructs this component, applying rule if it exists. """ if is_debug_set(logger): - logger.debug("Constructing suffix %s", self.name) + logger.debug("Constructing Suffix '%s'", self.name) if self._constructed is True: return From 9274444c2e2fd0f60d78dd89a01d5f9e034c56f9 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Wed, 13 Dec 2023 08:52:19 -0700 Subject: [PATCH 16/43] Minor Suffix performance improvements --- pyomo/core/base/suffix.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 2e0e6191f75..713bcbf086a 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -209,13 +209,15 @@ def construct(self, data=None): if self._rule is not None: rule = self._rule - block = self.parent_block() if rule.contains_indices(): - # The index is coming in externally; we need to validate it + # The rule contains explicit indices (e.g., is a dict). + # Iterate over the indices, expand them, and store the + # result + block = self.parent_block() for index in rule.indices(): - self.set_value(index, rule(block, index)) + self.set_value(index, rule(block, index), expand=True) else: - self.update_values(rule(block, None)) + self.update_values(rule(self.parent_block(), None), expand=True) timer.report() @property @@ -303,15 +305,9 @@ def clear_value(self, component, expand=True): """ if expand and component.is_indexed(): for component_ in component.values(): - try: - del self[component_] - except KeyError: - pass + self.pop(component_, None) else: - try: - del self[component] - except KeyError: - pass + self.pop(component, None) def clear_all_values(self): """ From 5f00df14887caf6b91d0473d495ca81ee7cfad35 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Wed, 13 Dec 2023 08:52:39 -0700 Subject: [PATCH 17/43] Remove unneeded method overrides --- pyomo/core/base/suffix.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 713bcbf086a..b21c9cce4e8 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -378,18 +378,6 @@ def pprint(self, *args, **kwds): def __str__(self): return ActiveComponent.__str__(self) - # - # Override NotImplementedError messages on ComponentMap base class - # - - def __eq__(self, other): - """Not implemented.""" - raise NotImplementedError("Suffix components are not comparable") - - def __ne__(self, other): - """Not implemented.""" - raise NotImplementedError("Suffix components are not comparable") - class SuffixFinder(object): def __init__(self, name, default=None): From e2268f273e844c06b4dc530163fa8bba0168735a Mon Sep 17 00:00:00 2001 From: John Siirola Date: Wed, 13 Dec 2023 08:52:57 -0700 Subject: [PATCH 18/43] Additional Suffix testing --- pyomo/core/tests/unit/test_suffix.py | 148 ++++++++++++++++++++++++--- 1 file changed, 133 insertions(+), 15 deletions(-) diff --git a/pyomo/core/tests/unit/test_suffix.py b/pyomo/core/tests/unit/test_suffix.py index 70f71002c9b..9a5a900a2fd 100644 --- a/pyomo/core/tests/unit/test_suffix.py +++ b/pyomo/core/tests/unit/test_suffix.py @@ -14,6 +14,7 @@ import os import itertools +import logging import pickle from os.path import abspath, dirname @@ -57,6 +58,23 @@ def simple_obj_rule(model, i): class TestSuffixMethods(unittest.TestCase): + def test_suffix_debug(self): + with LoggingIntercept(level=logging.DEBUG) as OUT: + m = ConcreteModel() + m.s = Suffix() + m.foo = Suffix(rule=[]) + print(OUT.getvalue()) + self.assertEqual( + OUT.getvalue(), + "Constructing ConcreteModel 'ConcreteModel', from data=None\n" + "Constructing Suffix 'Suffix'\n" + "Constructing Suffix 'foo' on [Model] from data=None\n" + "Constructing Suffix 'foo'\n" + "Constructed component ''[Model].foo'':\n" + "foo : Direction=LOCAL, Datatype=FLOAT\n" + " Key : Value\n\n", + ) + def test_suffix_rule(self): m = ConcreteModel() m.I = Set(initialize=[1, 2, 3]) @@ -846,20 +864,33 @@ def test_set_all_values3(self): self.assertEqual(model.z[1].get_suffix_value(model.junk), 3.0) # test update_values - def test_update_values1(self): + def test_update_values(self): model = ConcreteModel() model.junk = Suffix() model.x = Var() model.y = Var() - model.z = Var() + model.z = Var([1, 2]) model.junk.set_value(model.x, 0.0) self.assertEqual(model.junk.get(model.x), 0.0) self.assertEqual(model.junk.get(model.y), None) self.assertEqual(model.junk.get(model.z), None) + self.assertEqual(model.junk.get(model.z[1]), None) + self.assertEqual(model.junk.get(model.z[2]), None) model.junk.update_values([(model.x, 1.0), (model.y, 2.0), (model.z, 3.0)]) self.assertEqual(model.junk.get(model.x), 1.0) self.assertEqual(model.junk.get(model.y), 2.0) + self.assertEqual(model.junk.get(model.z), None) + self.assertEqual(model.junk.get(model.z[1]), 3.0) + self.assertEqual(model.junk.get(model.z[2]), 3.0) + model.junk.clear() + model.junk.update_values( + [(model.x, 1.0), (model.y, 2.0), (model.z, 3.0)], expand=False + ) + self.assertEqual(model.junk.get(model.x), 1.0) + self.assertEqual(model.junk.get(model.y), 2.0) self.assertEqual(model.junk.get(model.z), 3.0) + self.assertEqual(model.junk.get(model.z[1]), None) + self.assertEqual(model.junk.get(model.z[2]), None) # test clear_value def test_clear_value(self): @@ -875,26 +906,66 @@ def test_clear_value(self): model.junk.set_value(model.z, 2.0) model.junk.set_value(model.z[1], 4.0) - self.assertTrue(model.junk.get(model.x) == -1.0) - self.assertTrue(model.junk.get(model.y) == None) - self.assertTrue(model.junk.get(model.y[1]) == -2.0) + self.assertEqual(model.junk.get(model.x), -1.0) + self.assertEqual(model.junk.get(model.y), None) + self.assertEqual(model.junk.get(model.y[1]), -2.0) self.assertEqual(model.junk.get(model.y[2]), 1.0) self.assertEqual(model.junk.get(model.z), None) self.assertEqual(model.junk.get(model.z[2]), 2.0) self.assertEqual(model.junk.get(model.z[1]), 4.0) model.junk.clear_value(model.y) + + self.assertEqual(model.junk.get(model.x), -1.0) + self.assertEqual(model.junk.get(model.y), None) + self.assertEqual(model.junk.get(model.y[1]), None) + self.assertEqual(model.junk.get(model.y[2]), None) + self.assertEqual(model.junk.get(model.z), None) + self.assertEqual(model.junk.get(model.z[2]), 2.0) + self.assertEqual(model.junk.get(model.z[1]), 4.0) + model.junk.clear_value(model.x) + + self.assertEqual(model.junk.get(model.x), None) + self.assertEqual(model.junk.get(model.y), None) + self.assertEqual(model.junk.get(model.y[1]), None) + self.assertEqual(model.junk.get(model.y[2]), None) + self.assertEqual(model.junk.get(model.z), None) + self.assertEqual(model.junk.get(model.z[2]), 2.0) + self.assertEqual(model.junk.get(model.z[1]), 4.0) + + # Clearing a scalar that is not there does not raise an error + model.junk.clear_value(model.x) + + self.assertEqual(model.junk.get(model.x), None) + self.assertEqual(model.junk.get(model.y), None) + self.assertEqual(model.junk.get(model.y[1]), None) + self.assertEqual(model.junk.get(model.y[2]), None) + self.assertEqual(model.junk.get(model.z), None) + self.assertEqual(model.junk.get(model.z[2]), 2.0) + self.assertEqual(model.junk.get(model.z[1]), 4.0) + model.junk.clear_value(model.z[1]) - self.assertTrue(model.junk.get(model.x) is None) - self.assertTrue(model.junk.get(model.y) is None) - self.assertTrue(model.junk.get(model.y[1]) is None) + self.assertEqual(model.junk.get(model.x), None) + self.assertEqual(model.junk.get(model.y), None) + self.assertEqual(model.junk.get(model.y[1]), None) self.assertEqual(model.junk.get(model.y[2]), None) self.assertEqual(model.junk.get(model.z), None) self.assertEqual(model.junk.get(model.z[2]), 2.0) self.assertEqual(model.junk.get(model.z[1]), None) + # Clearing an indexed component with missing indices does not raise an error + model.junk.clear_value(model.z) + + self.assertEqual(model.junk.get(model.x), None) + self.assertEqual(model.junk.get(model.y), None) + self.assertEqual(model.junk.get(model.y[1]), None) + self.assertEqual(model.junk.get(model.y[2]), None) + self.assertEqual(model.junk.get(model.z), None) + self.assertEqual(model.junk.get(model.z[2]), None) + self.assertEqual(model.junk.get(model.z[1]), None) + # test clear_value no args def test_clear_all_values(self): model = ConcreteModel() @@ -945,7 +1016,8 @@ def test_set_datatype_get_datatype(self): self.assertEqual(model.junk.datatype, None) self.assertRegex( LOG.getvalue().replace("\n", " "), - "^DEPRECATED: Suffix.set_datatype is replaced with the Suffix.datatype property", + "^DEPRECATED: Suffix.set_datatype is replaced with the " + "Suffix.datatype property", ) model.junk.datatype = 'FLOAT' @@ -953,7 +1025,8 @@ def test_set_datatype_get_datatype(self): self.assertEqual(model.junk.get_datatype(), Suffix.FLOAT) self.assertRegex( LOG.getvalue().replace("\n", " "), - "^DEPRECATED: Suffix.get_datatype is replaced with the Suffix.datatype property", + "^DEPRECATED: Suffix.get_datatype is replaced with the " + "Suffix.datatype property", ) with self.assertRaisesRegex(ValueError, "1.0 is not a valid SuffixDataType"): @@ -972,11 +1045,12 @@ def test_set_direction_get_direction(self): self.assertEqual(model.junk.direction, Suffix.IMPORT_EXPORT) with LoggingIntercept() as LOG: - model.junk.set_direction(None) - self.assertEqual(model.junk.direction, None) + model.junk.set_direction(1) + self.assertEqual(model.junk.direction, Suffix.EXPORT) self.assertRegex( LOG.getvalue().replace("\n", " "), - "^DEPRECATED: Suffix.set_direction is replaced with the Suffix.direction property", + "^DEPRECATED: Suffix.set_direction is replaced with the " + "Suffix.direction property", ) model.junk.direction = 'IMPORT' @@ -984,11 +1058,15 @@ def test_set_direction_get_direction(self): self.assertEqual(model.junk.get_direction(), Suffix.IMPORT) self.assertRegex( LOG.getvalue().replace("\n", " "), - "^DEPRECATED: Suffix.get_direction is replaced with the Suffix.direction property", + "^DEPRECATED: Suffix.get_direction is replaced with the " + "Suffix.direction property", ) with self.assertRaisesRegex(ValueError, "'a' is not a valid SuffixDirection"): model.junk.direction = 'a' + # None is allowed for datatype, but not direction + with self.assertRaisesRegex(ValueError, "None is not a valid SuffixDirection"): + model.junk.direction = None # test __str__ def test_str(self): @@ -1002,13 +1080,44 @@ def test_pprint(self): model.junk = Suffix(direction=Suffix.EXPORT) output = StringIO() model.junk.pprint(ostream=output) + self.assertEqual( + output.getvalue(), + "junk : Direction=EXPORT, Datatype=FLOAT\n Key : Value\n", + ) model.junk.direction = Suffix.IMPORT + output = StringIO() model.junk.pprint(ostream=output) + self.assertEqual( + output.getvalue(), + "junk : Direction=IMPORT, Datatype=FLOAT\n Key : Value\n", + ) model.junk.direction = Suffix.LOCAL + model.junk.datatype = None + output = StringIO() model.junk.pprint(ostream=output) + self.assertEqual( + output.getvalue(), + "junk : Direction=LOCAL, Datatype=None\n Key : Value\n", + ) model.junk.direction = Suffix.IMPORT_EXPORT + model.junk.datatype = Suffix.INT + output = StringIO() model.junk.pprint(ostream=output) + self.assertEqual( + output.getvalue(), + "junk : Direction=IMPORT_EXPORT, Datatype=INT\n Key : Value\n", + ) + output = StringIO() model.pprint(ostream=output) + self.assertEqual( + output.getvalue(), + """1 Suffix Declarations + junk : Direction=IMPORT_EXPORT, Datatype=INT + Key : Value + +1 Declarations: junk +""", + ) # test pprint(verbose=True) def test_pprint_verbose(self): @@ -1026,7 +1135,16 @@ def test_pprint_verbose(self): output = StringIO() model.junk.pprint(ostream=output, verbose=True) - model.pprint(ostream=output, verbose=True) + self.assertEqual( + output.getvalue(), + """junk : Direction=LOCAL, Datatype=FLOAT + Key : Value + s.B[1] : 2.0 + s.B[2] : 3.0 + s.B[3] : 1.0 + s.b : 3.0 +""", + ) def test_active_export_suffix_generator(self): model = ConcreteModel() From 2c865d6b1c1c8122fa55c0b65830e9e092eecb13 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Mon, 18 Dec 2023 09:41:01 -0700 Subject: [PATCH 19/43] Declare AbstractSuffix (with disabled public API) --- pyomo/core/base/suffix.py | 36 ++++++++++++++++++++++++++-- pyomo/core/tests/unit/test_suffix.py | 2 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index b21c9cce4e8..49bac51e903 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -22,10 +22,23 @@ from pyomo.common.pyomo_typing import overload from pyomo.common.timing import ConstructionTimer from pyomo.core.base.component import ActiveComponent, ModelComponentFactory +from pyomo.core.base.disable_methods import disable_methods from pyomo.core.base.initializer import Initializer logger = logging.getLogger('pyomo.core') +_SUFFIX_API = ( + ('__contains__', 'test membership in'), + ('__iter__', 'iterate over'), + '__getitem__', + '__setitem__', + 'set_value', + 'set_all_values', + 'clear_value', + 'clear_all_values', + 'update_values', +) + # A list of convenient suffix generators, including: # - active_export_suffix_generator # **(used by problem writers) @@ -153,6 +166,20 @@ class Suffix(ComponentMap, ActiveComponent): FLOAT = SuffixDataType.FLOAT INT = SuffixDataType.INT + def __new__(cls, *args, **kwargs): + if cls is not Suffix: + return super().__new__(cls) + return super().__new__(AbstractSuffix) + + def __setstate__(self, state): + super().__setstate__(state) + # As the concrete class *is* the "Suffix" base class, the normal + # implementation of deepcopy (through get/setstate) will create + # the new Suffix, and __new__ will map it to AbstractSuffix. We + # need to map constructed Suffixes back to Suffix: + if self._constructed and self.__class__ is AbstractSuffix: + self.__class__ = Suffix + @overload def __init__( self, @@ -162,7 +189,7 @@ def __init__( initialize=None, rule=None, name=None, - doc=None + doc=None, ): ... @@ -199,7 +226,7 @@ def construct(self, data=None): Constructs this component, applying rule if it exists. """ if is_debug_set(logger): - logger.debug("Constructing Suffix '%s'", self.name) + logger.debug(f"Constructing %s '%s'", self.__class__.__name__, self.name) if self._constructed is True: return @@ -379,6 +406,11 @@ def __str__(self): return ActiveComponent.__str__(self) +@disable_methods(_SUFFIX_API) +class AbstractSuffix(Suffix): + pass + + class SuffixFinder(object): def __init__(self, name, default=None): """This provides an efficient utility for finding suffix values on a diff --git a/pyomo/core/tests/unit/test_suffix.py b/pyomo/core/tests/unit/test_suffix.py index 9a5a900a2fd..1ec1af9d919 100644 --- a/pyomo/core/tests/unit/test_suffix.py +++ b/pyomo/core/tests/unit/test_suffix.py @@ -68,7 +68,7 @@ def test_suffix_debug(self): OUT.getvalue(), "Constructing ConcreteModel 'ConcreteModel', from data=None\n" "Constructing Suffix 'Suffix'\n" - "Constructing Suffix 'foo' on [Model] from data=None\n" + "Constructing AbstractSuffix 'foo' on [Model] from data=None\n" "Constructing Suffix 'foo'\n" "Constructed component ''[Model].foo'':\n" "foo : Direction=LOCAL, Datatype=FLOAT\n" From 6ed1f3ffc0ed08ae3dcf81b23831014ce999bfa1 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Wed, 20 Dec 2023 11:37:25 -0700 Subject: [PATCH 20/43] NFC: fix comment typos --- pyomo/common/collections/component_map.py | 2 +- pyomo/core/base/suffix.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pyomo/common/collections/component_map.py b/pyomo/common/collections/component_map.py index 4bb9e88e9af..41796876d7c 100644 --- a/pyomo/common/collections/component_map.py +++ b/pyomo/common/collections/component_map.py @@ -113,7 +113,7 @@ def __eq__(self, other): self_val = self._dict[other_id][1] # Note: check "is" first to help avoid creation of Pyomo # expressions (for the case that the values contain the same - # pyomo component) + # Pyomo component) if self_val is not val and self_val != val: return False return True diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 49bac51e903..30d79d57b8c 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -199,10 +199,10 @@ def __init__(self, **kwargs): self._datatype = None self._rule = None - # The suffix direction (note the setter performs error chrcking) + # The suffix direction (note the setter performs error checking) self.direction = kwargs.pop('direction', Suffix.LOCAL) - # The suffix datatype (note the setter performs error chrcking) + # The suffix datatype (note the setter performs error checking) self.datatype = kwargs.pop('datatype', Suffix.FLOAT) # The suffix construction rule From e27281796968c96e05b3ad761e47196ff8d29738 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Tue, 16 Jan 2024 15:15:07 -0700 Subject: [PATCH 21/43] Reorganize ComponentSet.__eq__ to clarify logic --- pyomo/common/collections/component_set.py | 4 ++-- pyomo/core/tests/unit/kernel/test_component_set.py | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pyomo/common/collections/component_set.py b/pyomo/common/collections/component_set.py index ad13baced44..e205773220f 100644 --- a/pyomo/common/collections/component_set.py +++ b/pyomo/common/collections/component_set.py @@ -106,9 +106,9 @@ def discard(self, val): def __eq__(self, other): if self is other: return True - if not isinstance(other, collections_Set) or len(self) != len(other): + if not isinstance(other, collections_Set): return False - return all(id(key) in self._data for key in other) + return len(self) == len(other) and all(id(key) in self._data for key in other) def __ne__(self, other): return not (self == other) diff --git a/pyomo/core/tests/unit/kernel/test_component_set.py b/pyomo/core/tests/unit/kernel/test_component_set.py index 10a7b27e59e..30f2cf72716 100644 --- a/pyomo/core/tests/unit/kernel/test_component_set.py +++ b/pyomo/core/tests/unit/kernel/test_component_set.py @@ -264,6 +264,14 @@ def test_eq(self): self.assertTrue(cset1 != cset2) self.assertNotEqual(cset1, cset2) + cset2.add(variable()) + self.assertFalse(cset2 == cset1) + self.assertTrue(cset2 != cset1) + self.assertNotEqual(cset2, cset1) + self.assertFalse(cset1 == cset2) + self.assertTrue(cset1 != cset2) + self.assertNotEqual(cset1, cset2) + if __name__ == "__main__": unittest.main() From d699864132114ebb60119ba696365ec1f07a4d27 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Tue, 16 Jan 2024 23:27:42 -0700 Subject: [PATCH 22/43] Fix import in assertExpressionsStructurallyEqual --- pyomo/common/unittest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/common/unittest.py b/pyomo/common/unittest.py index 9bf68e53314..1ed26f72320 100644 --- a/pyomo/common/unittest.py +++ b/pyomo/common/unittest.py @@ -563,7 +563,7 @@ def assertExpressionsEqual(self, a, b, include_named_exprs=True, places=None): def assertExpressionsStructurallyEqual( self, a, b, include_named_exprs=True, places=None ): - from pyomo.core.expr.compare import assertExpressionsEqual + from pyomo.core.expr.compare import assertExpressionsStructurallyEqual return assertExpressionsStructurallyEqual( self, a, b, include_named_exprs, places From 73820cc2c3b4c9fc0d591f34bee6a9af8fe62cfe Mon Sep 17 00:00:00 2001 From: John Siirola Date: Tue, 16 Jan 2024 23:28:07 -0700 Subject: [PATCH 23/43] Switch numeric expression tests to use assertExpression* from TestCase --- pyomo/core/tests/unit/test_numeric_expr.py | 235 +++++++++------------ 1 file changed, 94 insertions(+), 141 deletions(-) diff --git a/pyomo/core/tests/unit/test_numeric_expr.py b/pyomo/core/tests/unit/test_numeric_expr.py index 13af5adc9bb..82f1e5c11c4 100644 --- a/pyomo/core/tests/unit/test_numeric_expr.py +++ b/pyomo/core/tests/unit/test_numeric_expr.py @@ -104,10 +104,6 @@ MinExpression, _balanced_parens, ) -from pyomo.core.expr.compare import ( - assertExpressionsEqual, - assertExpressionsStructurallyEqual, -) from pyomo.core.expr.relational_expr import RelationalExpression, EqualityExpression from pyomo.core.expr.relational_expr import RelationalExpression, EqualityExpression from pyomo.common.errors import PyomoException @@ -642,8 +638,7 @@ def test_simpleSum(self): m.b = Var() e = m.a + m.b # - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [MonomialTermExpression((1, m.a)), MonomialTermExpression((1, m.b))] @@ -658,8 +653,7 @@ def test_simpleSum_API(self): m.b = Var() e = m.a + m.b e += 2 * m.a - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -675,12 +669,12 @@ def test_constSum(self): m = AbstractModel() m.a = Var() # - assertExpressionsEqual( - self, m.a + 5, LinearExpression([MonomialTermExpression((1, m.a)), 5]) + self.assertExpressionsEqual( + m.a + 5, LinearExpression([MonomialTermExpression((1, m.a)), 5]) ) - assertExpressionsEqual( - self, 5 + m.a, LinearExpression([5, MonomialTermExpression((1, m.a))]) + self.assertExpressionsEqual( + 5 + m.a, LinearExpression([5, MonomialTermExpression((1, m.a))]) ) def test_nestedSum(self): @@ -702,8 +696,7 @@ def test_nestedSum(self): # a b e1 = m.a + m.b e = e1 + 5 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [MonomialTermExpression((1, m.a)), MonomialTermExpression((1, m.b)), 5] @@ -717,8 +710,7 @@ def test_nestedSum(self): # a b e1 = m.a + m.b e = 5 + e1 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [MonomialTermExpression((1, m.a)), MonomialTermExpression((1, m.b)), 5] @@ -732,8 +724,7 @@ def test_nestedSum(self): # a b e1 = m.a + m.b e = e1 + m.c - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -751,8 +742,7 @@ def test_nestedSum(self): # a b e1 = m.a + m.b e = m.c + e1 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -772,8 +762,7 @@ def test_nestedSum(self): e2 = m.c + m.d e = e1 + e2 # - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -807,8 +796,7 @@ def test_nestedSum2(self): e1 = m.a + m.b e = 2 * e1 + m.c - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -840,8 +828,7 @@ def test_nestedSum2(self): e1 = m.a + m.b e = 3 * (2 * e1 + m.c) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, ProductExpression( ( @@ -903,8 +890,7 @@ def test_sumOf_nestedTrivialProduct(self): e1 = m.a * 5 e = e1 + m.b # - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [MonomialTermExpression((5, m.a)), MonomialTermExpression((1, m.b))] @@ -918,8 +904,7 @@ def test_sumOf_nestedTrivialProduct(self): # a 5 e = m.b + e1 # - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [MonomialTermExpression((1, m.b)), MonomialTermExpression((5, m.a))] @@ -934,8 +919,7 @@ def test_sumOf_nestedTrivialProduct(self): e2 = m.b + m.c e = e1 + e2 # - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -954,8 +938,7 @@ def test_sumOf_nestedTrivialProduct(self): e2 = m.b + m.c e = e2 + e1 # - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -978,8 +961,7 @@ def test_simpleDiff(self): # / \ # a b e = m.a - m.b - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [MonomialTermExpression((1, m.a)), MonomialTermExpression((-1, m.b))] @@ -996,15 +978,15 @@ def test_constDiff(self): # - # / \ # a 5 - assertExpressionsEqual( - self, m.a - 5, LinearExpression([MonomialTermExpression((1, m.a)), -5]) + self.assertExpressionsEqual( + m.a - 5, LinearExpression([MonomialTermExpression((1, m.a)), -5]) ) # - # / \ # 5 a - assertExpressionsEqual( - self, 5 - m.a, LinearExpression([5, MonomialTermExpression((-1, m.a))]) + self.assertExpressionsEqual( + 5 - m.a, LinearExpression([5, MonomialTermExpression((-1, m.a))]) ) def test_paramDiff(self): @@ -1019,8 +1001,7 @@ def test_paramDiff(self): # / \ # a p e = m.a - m.p - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [MonomialTermExpression((1, m.a)), NPV_NegationExpression((m.p,))] @@ -1031,8 +1012,8 @@ def test_paramDiff(self): # / \ # m.p a e = m.p - m.a - assertExpressionsEqual( - self, e, LinearExpression([m.p, MonomialTermExpression((-1, m.a))]) + self.assertExpressionsEqual( + e, LinearExpression([m.p, MonomialTermExpression((-1, m.a))]) ) def test_constparamDiff(self): @@ -1076,8 +1057,8 @@ def test_termDiff(self): e = 5 - 2 * m.a - assertExpressionsEqual( - self, e, LinearExpression([5, MonomialTermExpression((-2, m.a))]) + self.assertExpressionsEqual( + e, LinearExpression([5, MonomialTermExpression((-2, m.a))]) ) def test_nestedDiff(self): @@ -1097,8 +1078,7 @@ def test_nestedDiff(self): # a b e1 = m.a - m.b e = e1 - 5 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -1116,8 +1096,7 @@ def test_nestedDiff(self): # a b e1 = m.a - m.b e = 5 - e1 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -1143,8 +1122,7 @@ def test_nestedDiff(self): # a b e1 = m.a - m.b e = e1 - m.c - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -1162,8 +1140,7 @@ def test_nestedDiff(self): # a b e1 = m.a - m.b e = m.c - e1 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -1190,8 +1167,7 @@ def test_nestedDiff(self): e1 = m.a - m.b e2 = m.c - m.d e = e1 - e2 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -1233,8 +1209,8 @@ def test_negation_mutableparam(self): m = AbstractModel() m.p = Param(mutable=True, initialize=1.0) e = -m.p - assertExpressionsEqual(self, e, NPV_NegationExpression((m.p,))) - assertExpressionsEqual(self, -e, m.p) + self.assertExpressionsEqual(e, NPV_NegationExpression((m.p,))) + self.assertExpressionsEqual(-e, m.p) def test_negation_terms(self): # @@ -1244,15 +1220,15 @@ def test_negation_terms(self): m.v = Var() m.p = Param(mutable=True, initialize=1.0) e = -m.p * m.v - assertExpressionsEqual( - self, e, MonomialTermExpression((NPV_NegationExpression((m.p,)), m.v)) + self.assertExpressionsEqual( + e, MonomialTermExpression((NPV_NegationExpression((m.p,)), m.v)) ) - assertExpressionsEqual(self, -e, MonomialTermExpression((m.p, m.v))) + self.assertExpressionsEqual(-e, MonomialTermExpression((m.p, m.v))) # e = -5 * m.v - assertExpressionsEqual(self, e, MonomialTermExpression((-5, m.v))) - assertExpressionsEqual(self, -e, MonomialTermExpression((5, m.v))) + self.assertExpressionsEqual(e, MonomialTermExpression((-5, m.v))) + self.assertExpressionsEqual(-e, MonomialTermExpression((5, m.v))) def test_trivialDiff(self): # @@ -1389,8 +1365,7 @@ def test_sumOf_nestedTrivialProduct2(self): # a 5 e1 = m.a * m.p e = e1 - m.b - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [MonomialTermExpression((m.p, m.a)), MonomialTermExpression((-1, m.b))] @@ -1404,8 +1379,7 @@ def test_sumOf_nestedTrivialProduct2(self): # a 5 e1 = m.a * m.p e = m.b - e1 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -1423,8 +1397,7 @@ def test_sumOf_nestedTrivialProduct2(self): e1 = m.a * m.p e2 = m.b - m.c e = e1 - e2 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -1452,8 +1425,7 @@ def test_sumOf_nestedTrivialProduct2(self): e2 = m.b - m.c e = e2 - e1 self.maxDiff = None - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -1624,8 +1596,7 @@ def test_nestedProduct2(self): e3 = e1 + m.d e = e2 * e3 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, ProductExpression( ( @@ -1671,8 +1642,7 @@ def test_nestedProduct2(self): inner = LinearExpression( [MonomialTermExpression((1, m.a)), MonomialTermExpression((1, m.b))] ) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, ProductExpression( (ProductExpression((m.c, inner)), ProductExpression((inner, m.d))) @@ -1711,8 +1681,8 @@ def test_nestedProduct3(self): # a b e1 = m.a * m.b e = e1 * 5 - assertExpressionsEqual( - self, e, MonomialTermExpression((NPV_ProductExpression((m.a, 5)), m.b)) + self.assertExpressionsEqual( + e, MonomialTermExpression((NPV_ProductExpression((m.a, 5)), m.b)) ) # * @@ -1801,37 +1771,37 @@ def test_trivialProduct(self): m.q = Param(initialize=1) e = m.a * 0 - assertExpressionsEqual(self, e, MonomialTermExpression((0, m.a))) + self.assertExpressionsEqual(e, MonomialTermExpression((0, m.a))) e = 0 * m.a - assertExpressionsEqual(self, e, MonomialTermExpression((0, m.a))) + self.assertExpressionsEqual(e, MonomialTermExpression((0, m.a))) e = m.a * m.p - assertExpressionsEqual(self, e, MonomialTermExpression((0, m.a))) + self.assertExpressionsEqual(e, MonomialTermExpression((0, m.a))) e = m.p * m.a - assertExpressionsEqual(self, e, MonomialTermExpression((0, m.a))) + self.assertExpressionsEqual(e, MonomialTermExpression((0, m.a))) # # Check that multiplying by one gives the original expression # e = m.a * 1 - assertExpressionsEqual(self, e, m.a) + self.assertExpressionsEqual(e, m.a) e = 1 * m.a - assertExpressionsEqual(self, e, m.a) + self.assertExpressionsEqual(e, m.a) e = m.a * m.q - assertExpressionsEqual(self, e, m.a) + self.assertExpressionsEqual(e, m.a) e = m.q * m.a - assertExpressionsEqual(self, e, m.a) + self.assertExpressionsEqual(e, m.a) # # Check that numeric constants are simply muliplied out # e = NumericConstant(3) * NumericConstant(2) - assertExpressionsEqual(self, e, 6) + self.assertExpressionsEqual(e, 6) self.assertIs(type(e), int) self.assertEqual(e, 6) @@ -1996,19 +1966,19 @@ def test_trivialDivision(self): # Check that dividing zero by anything non-zero gives zero # e = 0 / m.a - assertExpressionsEqual(self, e, DivisionExpression((0, m.a))) + self.assertExpressionsEqual(e, DivisionExpression((0, m.a))) # # Check that dividing by one 1 gives the original expression # e = m.a / 1 - assertExpressionsEqual(self, e, m.a) + self.assertExpressionsEqual(e, m.a) # # Check the structure dividing 1 by an expression # e = 1 / m.a - assertExpressionsEqual(self, e, DivisionExpression((1, m.a))) + self.assertExpressionsEqual(e, DivisionExpression((1, m.a))) # # Check the structure dividing 1 by an expression @@ -3782,8 +3752,7 @@ def tearDown(self): def test_summation1(self): e = sum_product(self.m.a) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -3798,8 +3767,7 @@ def test_summation1(self): def test_summation2(self): e = sum_product(self.m.p, self.m.a) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -3814,8 +3782,7 @@ def test_summation2(self): def test_summation3(self): e = sum_product(self.m.q, self.m.a) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -3830,8 +3797,7 @@ def test_summation3(self): def test_summation4(self): e = sum_product(self.m.a, self.m.b) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -3846,8 +3812,7 @@ def test_summation4(self): def test_summation5(self): e = sum_product(self.m.b, denom=self.m.a) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -3862,8 +3827,7 @@ def test_summation5(self): def test_summation6(self): e = sum_product(self.m.a, denom=self.m.p) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -3888,8 +3852,7 @@ def test_summation6(self): def test_summation7(self): e = sum_product(self.m.p, self.m.q, index=self.m.I) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, NPV_SumExpression( [ @@ -3906,8 +3869,7 @@ def test_summation_compression(self): e1 = sum_product(self.m.a) e2 = sum_product(self.m.b) e = e1 + e2 - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -3948,8 +3910,7 @@ def test_deprecation(self): r"DEPRECATED: The quicksum\(linear=...\) argument is deprecated " r"and ignored.", ) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -3965,8 +3926,7 @@ def test_deprecation(self): def test_summation1(self): e = quicksum((self.m.a[i] for i in self.m.a)) self.assertEqual(e(), 25) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -3982,8 +3942,7 @@ def test_summation1(self): def test_summation2(self): e = quicksum(self.m.p[i] * self.m.a[i] for i in self.m.a) self.assertEqual(e(), 25) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -3999,8 +3958,7 @@ def test_summation2(self): def test_summation3(self): e = quicksum(self.m.q[i] * self.m.a[i] for i in self.m.a) self.assertEqual(e(), 75) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -4016,8 +3974,7 @@ def test_summation3(self): def test_summation4(self): e = quicksum(self.m.a[i] * self.m.b[i] for i in self.m.a) self.assertEqual(e(), 250) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -4033,8 +3990,7 @@ def test_summation4(self): def test_summation5(self): e = quicksum(self.m.b[i] / self.m.a[i] for i in self.m.a) self.assertEqual(e(), 10) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, SumExpression( [ @@ -4050,8 +4006,7 @@ def test_summation5(self): def test_summation6(self): e = quicksum(self.m.a[i] / self.m.p[i] for i in self.m.a) self.assertEqual(e(), 25) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, LinearExpression( [ @@ -4077,8 +4032,7 @@ def test_summation6(self): def test_summation7(self): e = quicksum((self.m.p[i] * self.m.q[i] for i in self.m.I), linear=False) self.assertEqual(e(), 15) - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, NPV_SumExpression( [ @@ -4453,7 +4407,7 @@ def test_Expr_if(self): # expr1 = Expr_if(IF=self.m.a + self.m.b < 20, THEN=self.m.a, ELSE=self.m.b) expr2 = expr1.clone() - assertExpressionsStructurallyEqual(self, expr1, expr2) + self.assertExpressionsStructurallyEqual(expr1, expr2) self.assertIsNot(expr1, expr2) self.assertIsNot(expr1.arg(0), expr2.arg(0)) @@ -5029,7 +4983,7 @@ def test_init(self): self.assertEqual(e.linear_vars, [m.x, m.y]) self.assertEqual(e.linear_coefs, [2, 3]) - assertExpressionsEqual(self, e, f) + self.assertExpressionsEqual(e, f) args = [10, MonomialTermExpression((4, m.y)), MonomialTermExpression((5, m.x))] with LoggingIntercept() as OUT: @@ -5116,7 +5070,7 @@ def test_sum_other(self): with linear_expression() as e: e = e - arg - assertExpressionsEqual(self, e, -arg) + self.assertExpressionsEqual(e, -arg) def test_mul_other(self): m = ConcreteModel() @@ -5255,13 +5209,12 @@ def test_pow_other(self): with linear_expression() as e: e += m.p e = 2**e - assertExpressionsEqual(self, e, NPV_PowExpression((2, m.p))) + self.assertExpressionsEqual(e, NPV_PowExpression((2, m.p))) with linear_expression() as e: e += m.v[0] + m.v[1] e = m.v[0] ** e - assertExpressionsEqual( - self, + self.assertExpressionsEqual( e, PowExpression( ( @@ -5545,7 +5498,7 @@ def test_simple(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_sum(self): M = ConcreteModel() @@ -5556,7 +5509,7 @@ def test_sum(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def Xtest_Sum(self): M = ConcreteModel() @@ -5566,7 +5519,7 @@ def Xtest_Sum(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_prod(self): M = ConcreteModel() @@ -5577,7 +5530,7 @@ def test_prod(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_negation(self): M = ConcreteModel() @@ -5586,7 +5539,7 @@ def test_negation(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_reciprocal(self): M = ConcreteModel() @@ -5597,7 +5550,7 @@ def test_reciprocal(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_multisum(self): M = ConcreteModel() @@ -5608,7 +5561,7 @@ def test_multisum(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_linear(self): M = ConcreteModel() @@ -5621,7 +5574,7 @@ def test_linear(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_linear_context(self): M = ConcreteModel() @@ -5634,7 +5587,7 @@ def test_linear_context(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_ExprIf(self): M = ConcreteModel() @@ -5643,7 +5596,7 @@ def test_ExprIf(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) def test_getitem(self): m = ConcreteModel() @@ -5656,7 +5609,7 @@ def test_getitem(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) self.assertEqual("x[{I} + P[{I} + 1]] + 3", str(e)) def test_abs(self): @@ -5666,7 +5619,7 @@ def test_abs(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) self.assertEqual(str(e), str(e_)) def test_sin(self): @@ -5676,7 +5629,7 @@ def test_sin(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) self.assertEqual(str(e), str(e_)) def test_external_fcn(self): @@ -5687,7 +5640,7 @@ def test_external_fcn(self): s = pickle.dumps(e) e_ = pickle.loads(s) self.assertIsNot(e, e_) - assertExpressionsStructurallyEqual(self, e, e_) + self.assertExpressionsStructurallyEqual(e, e_) # From 2e479276d92d55f1696018d69d5087ddc1845554 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Thu, 18 Jan 2024 16:48:54 -0700 Subject: [PATCH 24/43] Make SumExpression.args always immutable (always return a copy of the underlying list) --- pyomo/core/expr/numeric_expr.py | 131 ++++++++++++-------------------- 1 file changed, 48 insertions(+), 83 deletions(-) diff --git a/pyomo/core/expr/numeric_expr.py b/pyomo/core/expr/numeric_expr.py index 3eb7861e341..17f8a15d5cf 100644 --- a/pyomo/core/expr/numeric_expr.py +++ b/pyomo/core/expr/numeric_expr.py @@ -1162,13 +1162,30 @@ def nargs(self): @property def args(self): - if len(self._args_) != self._nargs: - self._args_ = self._args_[: self._nargs] - return self._args_ + # We unconditionally make a copy of the args to isolate the user + # from future possible updates to the underlying list + return self._args_[:self._nargs] def getname(self, *args, **kwds): return 'sum' + def _trunc_append(self, other): + _args = self._args_ + if len(_args) > self._nargs: + _args = _args[:self._nargs] + _args.append(other) + return self.__class__(_args) + + def _trunc_extend(self, other): + _args = self._args_ + if len(_args) > self._nargs: + _args = _args[:self._nargs] + if len(other._args_) == other._nargs: + _args.extend(other._args_) + else: + _args.extend(other._args_[:other._nargs]) + return self.__class__(_args) + def _apply_operation(self, result): return sum(result) @@ -1821,17 +1838,13 @@ def _add_native_monomial(a, b): def _add_native_linear(a, b): if not a: return b - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_native_sum(a, b): if not a: return b - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_native_other(a, b): @@ -1872,15 +1885,11 @@ def _add_npv_monomial(a, b): def _add_npv_linear(a, b): - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_npv_sum(a, b): - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_npv_other(a, b): @@ -1942,9 +1951,7 @@ def _add_param_linear(a, b): a = a.value if not a: return b - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_param_sum(a, b): @@ -1952,9 +1959,7 @@ def _add_param_sum(a, b): a = value(a) if not a: return b - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_param_other(a, b): @@ -1999,15 +2004,11 @@ def _add_var_monomial(a, b): def _add_var_linear(a, b): - args = b.args - args.append(MonomialTermExpression((1, a))) - return b.__class__(args) + return b._trunc_append(MonomialTermExpression((1, a))) def _add_var_sum(a, b): - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_var_other(a, b): @@ -2046,15 +2047,11 @@ def _add_monomial_monomial(a, b): def _add_monomial_linear(a, b): - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_monomial_sum(a, b): - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_monomial_other(a, b): @@ -2069,15 +2066,11 @@ def _add_monomial_other(a, b): def _add_linear_native(a, b): if not b: return a - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_linear_npv(a, b): - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_linear_param(a, b): @@ -2085,33 +2078,23 @@ def _add_linear_param(a, b): b = b.value if not b: return a - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_linear_var(a, b): - args = a.args - args.append(MonomialTermExpression((1, b))) - return a.__class__(args) + return a._trunc_append(MonomialTermExpression((1, b))) def _add_linear_monomial(a, b): - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_linear_linear(a, b): - args = a.args - args.extend(b.args) - return a.__class__(args) + return a._trunc_extend(b) def _add_linear_sum(a, b): - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_linear_other(a, b): @@ -2126,15 +2109,11 @@ def _add_linear_other(a, b): def _add_sum_native(a, b): if not b: return a - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_sum_npv(a, b): - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_sum_param(a, b): @@ -2142,39 +2121,27 @@ def _add_sum_param(a, b): b = b.value if not b: return a - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_sum_var(a, b): - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_sum_monomial(a, b): - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_sum_linear(a, b): - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) def _add_sum_sum(a, b): - args = a.args - args.extend(b.args) - return a.__class__(args) + return a._trunc_extend(b) def _add_sum_other(a, b): - args = a.args - args.append(b) - return a.__class__(args) + return a._trunc_append(b) # @@ -2213,9 +2180,7 @@ def _add_other_linear(a, b): def _add_other_sum(a, b): - args = b.args - args.append(a) - return b.__class__(args) + return b._trunc_append(a) def _add_other_other(a, b): @@ -2628,8 +2593,8 @@ def _neg_var(a): def _neg_monomial(a): - args = a.args - return MonomialTermExpression((-args[0], args[1])) + coef, var = a.args + return MonomialTermExpression((-coef, var)) def _neg_sum(a): From 00f24b8ef49f2d01a29afa657a10e74777871a5a Mon Sep 17 00:00:00 2001 From: John Siirola Date: Thu, 18 Jan 2024 16:49:52 -0700 Subject: [PATCH 25/43] Update tests to reflect change in when underlying lists are duplicated --- pyomo/core/tests/unit/test_numeric_expr.py | 6 +++--- pyomo/core/tests/unit/test_numeric_expr_api.py | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pyomo/core/tests/unit/test_numeric_expr.py b/pyomo/core/tests/unit/test_numeric_expr.py index 82f1e5c11c4..a4f3295441e 100644 --- a/pyomo/core/tests/unit/test_numeric_expr.py +++ b/pyomo/core/tests/unit/test_numeric_expr.py @@ -1618,9 +1618,9 @@ def test_nestedProduct2(self): ), ) # Verify shared args... - self.assertIsNot(e1._args_, e2._args_) - self.assertIs(e1._args_, e3._args_) - self.assertIs(e1._args_, e.arg(1)._args_) + self.assertIs(e1._args_, e2._args_) + self.assertIsNot(e1._args_, e3._args_) + self.assertIs(e1._args_, e.arg(0)._args_) self.assertIs(e.arg(0).arg(0), e.arg(1).arg(0)) self.assertIs(e.arg(0).arg(1), e.arg(1).arg(1)) diff --git a/pyomo/core/tests/unit/test_numeric_expr_api.py b/pyomo/core/tests/unit/test_numeric_expr_api.py index 0d85e959fa0..69cb43f3ad5 100644 --- a/pyomo/core/tests/unit/test_numeric_expr_api.py +++ b/pyomo/core/tests/unit/test_numeric_expr_api.py @@ -950,7 +950,14 @@ def test_sum(self): f = e.create_node_with_local_data(e.args) self.assertIsNot(f, e) self.assertIs(type(f), type(e)) - self.assertIs(f.args, e.args) + self.assertIsNot(f._args_, e._args_) + self.assertIsNot(f.args, e.args) + + f = e.create_node_with_local_data(e._args_) + self.assertIsNot(f, e) + self.assertIs(type(f), type(e)) + self.assertIs(f._args_, e._args_) + self.assertIsNot(f.args, e.args) f = e.create_node_with_local_data((m.x, 2, 3)) self.assertIsNot(f, e) From 049494c15f8db747824c280f1fe13bd641a672a5 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Mon, 22 Jan 2024 17:29:53 -0700 Subject: [PATCH 26/43] Add test for 3096 --- pyomo/core/tests/unit/test_derivs.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pyomo/core/tests/unit/test_derivs.py b/pyomo/core/tests/unit/test_derivs.py index 23a5a8bc7d1..7db284cb29a 100644 --- a/pyomo/core/tests/unit/test_derivs.py +++ b/pyomo/core/tests/unit/test_derivs.py @@ -322,6 +322,18 @@ def test_nested_named_expressions(self): self.assertAlmostEqual(derivs[m.y], pyo.value(symbolic[m.y]), tol + 3) self.assertAlmostEqual(derivs[m.y], approx_deriv(e, m.y), tol) + def test_linear_exprs_issue_3096(self): + m = pyo.ConcreteModel() + m.y1 = pyo.Var(initialize=10) + m.y2 = pyo.Var(initialize=100) + e = (m.y1 - 0.5) * (m.y1 - 0.5) + (m.y2 - 0.5) * (m.y2 - 0.5) + derivs = reverse_ad(e) + self.assertEqual(derivs[m.y1], 19) + self.assertEqual(derivs[m.y2], 199) + symbolic = reverse_sd(e) + self.assertExpressionsEqual(symbolic[m.y1], m.y1 - 0.5 + m.y1 - 0.5) + self.assertExpressionsEqual(symbolic[m.y2], m.y2 - 0.5 + m.y2 - 0.5) + class TestDifferentiate(unittest.TestCase): @unittest.skipUnless(sympy_available, "test requires sympy") From 3e2979558744e016b860d12d38e409cc2d87bf17 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Mon, 22 Jan 2024 17:41:32 -0700 Subject: [PATCH 27/43] NFC: Apply black --- pyomo/core/expr/numeric_expr.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyomo/core/expr/numeric_expr.py b/pyomo/core/expr/numeric_expr.py index 17f8a15d5cf..a638903236f 100644 --- a/pyomo/core/expr/numeric_expr.py +++ b/pyomo/core/expr/numeric_expr.py @@ -1164,7 +1164,7 @@ def nargs(self): def args(self): # We unconditionally make a copy of the args to isolate the user # from future possible updates to the underlying list - return self._args_[:self._nargs] + return self._args_[: self._nargs] def getname(self, *args, **kwds): return 'sum' @@ -1172,18 +1172,18 @@ def getname(self, *args, **kwds): def _trunc_append(self, other): _args = self._args_ if len(_args) > self._nargs: - _args = _args[:self._nargs] + _args = _args[: self._nargs] _args.append(other) return self.__class__(_args) def _trunc_extend(self, other): _args = self._args_ if len(_args) > self._nargs: - _args = _args[:self._nargs] + _args = _args[: self._nargs] if len(other._args_) == other._nargs: _args.extend(other._args_) else: - _args.extend(other._args_[:other._nargs]) + _args.extend(other._args_[: other._nargs]) return self.__class__(_args) def _apply_operation(self, result): From daff9aa7308c09d0e6e48b6146ecb0c92802ac67 Mon Sep 17 00:00:00 2001 From: John Siirola Date: Mon, 22 Jan 2024 21:11:01 -0700 Subject: [PATCH 28/43] Import pandas through pyomo.common.dependencies --- .../parmest/examples/reactor_design/bootstrap_example.py | 2 +- .../contrib/parmest/examples/reactor_design/datarec_example.py | 3 +-- .../parmest/examples/reactor_design/leaveNout_example.py | 3 +-- .../examples/reactor_design/likelihood_ratio_example.py | 3 +-- .../examples/reactor_design/multisensor_data_example.py | 2 +- .../examples/reactor_design/parameter_estimation_example.py | 2 +- .../contrib/parmest/examples/reactor_design/reactor_design.py | 2 +- .../parmest/examples/reactor_design/timeseries_data_example.py | 2 +- .../parmest/examples/rooney_biegler/bootstrap_example.py | 2 +- .../examples/rooney_biegler/likelihood_ratio_example.py | 3 +-- .../examples/rooney_biegler/parameter_estimation_example.py | 2 +- .../contrib/parmest/examples/rooney_biegler/rooney_biegler.py | 2 +- .../examples/rooney_biegler/rooney_biegler_with_constraint.py | 2 +- pyomo/contrib/parmest/examples/semibatch/parallel_example.py | 3 +-- .../pynumero/examples/callback/cyipopt_functor_callback.py | 2 +- .../examples/external_grey_box/param_est/generate_data.py | 2 +- .../examples/external_grey_box/param_est/perform_estimation.py | 2 +- pyomo/contrib/sensitivity_toolbox/examples/rooney_biegler.py | 2 +- 18 files changed, 18 insertions(+), 23 deletions(-) diff --git a/pyomo/contrib/parmest/examples/reactor_design/bootstrap_example.py b/pyomo/contrib/parmest/examples/reactor_design/bootstrap_example.py index e2d172f34f6..16ae9343dfd 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/bootstrap_example.py +++ b/pyomo/contrib/parmest/examples/reactor_design/bootstrap_example.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pandas as pd +from pyomo.common.dependencies import pandas as pd from os.path import join, abspath, dirname import pyomo.contrib.parmest.parmest as parmest from pyomo.contrib.parmest.examples.reactor_design.reactor_design import ( diff --git a/pyomo/contrib/parmest/examples/reactor_design/datarec_example.py b/pyomo/contrib/parmest/examples/reactor_design/datarec_example.py index cfd3891c00e..507a3ee7582 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/datarec_example.py +++ b/pyomo/contrib/parmest/examples/reactor_design/datarec_example.py @@ -9,8 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import numpy as np -import pandas as pd +from pyomo.common.dependencies import numpy as np, pandas as pd import pyomo.contrib.parmest.parmest as parmest from pyomo.contrib.parmest.examples.reactor_design.reactor_design import ( reactor_design_model, diff --git a/pyomo/contrib/parmest/examples/reactor_design/leaveNout_example.py b/pyomo/contrib/parmest/examples/reactor_design/leaveNout_example.py index 6952a7fc733..cda50ef3efd 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/leaveNout_example.py +++ b/pyomo/contrib/parmest/examples/reactor_design/leaveNout_example.py @@ -9,8 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import numpy as np -import pandas as pd +from pyomo.common.dependencies import numpy as np, pandas as pd from os.path import join, abspath, dirname import pyomo.contrib.parmest.parmest as parmest from pyomo.contrib.parmest.examples.reactor_design.reactor_design import ( diff --git a/pyomo/contrib/parmest/examples/reactor_design/likelihood_ratio_example.py b/pyomo/contrib/parmest/examples/reactor_design/likelihood_ratio_example.py index a0fe6f22305..448354f600a 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/likelihood_ratio_example.py +++ b/pyomo/contrib/parmest/examples/reactor_design/likelihood_ratio_example.py @@ -9,8 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import numpy as np -import pandas as pd +from pyomo.common.dependencies import numpy as np, pandas as pd from itertools import product from os.path import join, abspath, dirname import pyomo.contrib.parmest.parmest as parmest diff --git a/pyomo/contrib/parmest/examples/reactor_design/multisensor_data_example.py b/pyomo/contrib/parmest/examples/reactor_design/multisensor_data_example.py index a92ac626fae..10d56c8e457 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/multisensor_data_example.py +++ b/pyomo/contrib/parmest/examples/reactor_design/multisensor_data_example.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pandas as pd +from pyomo.common.dependencies import pandas as pd from os.path import join, abspath, dirname import pyomo.contrib.parmest.parmest as parmest from pyomo.contrib.parmest.examples.reactor_design.reactor_design import ( diff --git a/pyomo/contrib/parmest/examples/reactor_design/parameter_estimation_example.py b/pyomo/contrib/parmest/examples/reactor_design/parameter_estimation_example.py index 581d3904c04..43af4fbcb94 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/parameter_estimation_example.py +++ b/pyomo/contrib/parmest/examples/reactor_design/parameter_estimation_example.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pandas as pd +from pyomo.common.dependencies import pandas as pd from os.path import join, abspath, dirname import pyomo.contrib.parmest.parmest as parmest from pyomo.contrib.parmest.examples.reactor_design.reactor_design import ( diff --git a/pyomo/contrib/parmest/examples/reactor_design/reactor_design.py b/pyomo/contrib/parmest/examples/reactor_design/reactor_design.py index 16f65e236eb..e86446febd7 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/reactor_design.py +++ b/pyomo/contrib/parmest/examples/reactor_design/reactor_design.py @@ -12,7 +12,7 @@ Continuously stirred tank reactor model, based on pyomo/examples/doc/pyomobook/nonlinear-ch/react_design/ReactorDesign.py """ -import pandas as pd +from pyomo.common.dependencies import pandas as pd from pyomo.environ import ( ConcreteModel, Param, diff --git a/pyomo/contrib/parmest/examples/reactor_design/timeseries_data_example.py b/pyomo/contrib/parmest/examples/reactor_design/timeseries_data_example.py index da2ab1874c9..ff6c167f68d 100644 --- a/pyomo/contrib/parmest/examples/reactor_design/timeseries_data_example.py +++ b/pyomo/contrib/parmest/examples/reactor_design/timeseries_data_example.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pandas as pd +from pyomo.common.dependencies import pandas as pd from os.path import join, abspath, dirname import pyomo.contrib.parmest.parmest as parmest diff --git a/pyomo/contrib/parmest/examples/rooney_biegler/bootstrap_example.py b/pyomo/contrib/parmest/examples/rooney_biegler/bootstrap_example.py index f686bbd933d..1c82adb909a 100644 --- a/pyomo/contrib/parmest/examples/rooney_biegler/bootstrap_example.py +++ b/pyomo/contrib/parmest/examples/rooney_biegler/bootstrap_example.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pandas as pd +from pyomo.common.dependencies import pandas as pd import pyomo.contrib.parmest.parmest as parmest from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import ( rooney_biegler_model, diff --git a/pyomo/contrib/parmest/examples/rooney_biegler/likelihood_ratio_example.py b/pyomo/contrib/parmest/examples/rooney_biegler/likelihood_ratio_example.py index 5e54a33abda..7cd77166a4b 100644 --- a/pyomo/contrib/parmest/examples/rooney_biegler/likelihood_ratio_example.py +++ b/pyomo/contrib/parmest/examples/rooney_biegler/likelihood_ratio_example.py @@ -9,8 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import numpy as np -import pandas as pd +from pyomo.common.dependencies import numpy as np, pandas as pd from itertools import product import pyomo.contrib.parmest.parmest as parmest from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import ( diff --git a/pyomo/contrib/parmest/examples/rooney_biegler/parameter_estimation_example.py b/pyomo/contrib/parmest/examples/rooney_biegler/parameter_estimation_example.py index 9af33217fe4..9aa59be6a17 100644 --- a/pyomo/contrib/parmest/examples/rooney_biegler/parameter_estimation_example.py +++ b/pyomo/contrib/parmest/examples/rooney_biegler/parameter_estimation_example.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -import pandas as pd +from pyomo.common.dependencies import pandas as pd import pyomo.contrib.parmest.parmest as parmest from pyomo.contrib.parmest.examples.rooney_biegler.rooney_biegler import ( rooney_biegler_model, diff --git a/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler.py b/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler.py index 5a0e1238e85..7a48dcf190d 100644 --- a/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler.py +++ b/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler.py @@ -15,7 +15,7 @@ 47(8), 1794-1804. """ -import pandas as pd +from pyomo.common.dependencies import pandas as pd import pyomo.environ as pyo diff --git a/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler_with_constraint.py b/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler_with_constraint.py index 2582e3fe928..0ad65b1eb7a 100644 --- a/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler_with_constraint.py +++ b/pyomo/contrib/parmest/examples/rooney_biegler/rooney_biegler_with_constraint.py @@ -15,7 +15,7 @@ 47(8), 1794-1804. """ -import pandas as pd +from pyomo.common.dependencies import pandas as pd import pyomo.environ as pyo diff --git a/pyomo/contrib/parmest/examples/semibatch/parallel_example.py b/pyomo/contrib/parmest/examples/semibatch/parallel_example.py index ff1287811cf..ba69b9f2d06 100644 --- a/pyomo/contrib/parmest/examples/semibatch/parallel_example.py +++ b/pyomo/contrib/parmest/examples/semibatch/parallel_example.py @@ -14,8 +14,7 @@ parallel and save results to files for later analysis and graphics. Example command: mpiexec -n 4 python parallel_example.py """ -import numpy as np -import pandas as pd +from pyomo.common.dependencies import numpy as np, pandas as pd from itertools import product from os.path import join, abspath, dirname import pyomo.contrib.parmest.parmest as parmest diff --git a/pyomo/contrib/pynumero/examples/callback/cyipopt_functor_callback.py b/pyomo/contrib/pynumero/examples/callback/cyipopt_functor_callback.py index f977a2701a2..ca452f33c90 100644 --- a/pyomo/contrib/pynumero/examples/callback/cyipopt_functor_callback.py +++ b/pyomo/contrib/pynumero/examples/callback/cyipopt_functor_callback.py @@ -1,6 +1,6 @@ import pyomo.environ as pyo from pyomo.contrib.pynumero.examples.callback.reactor_design import model as m -import pandas as pd +from pyomo.common.dependencies import pandas as pd """ This example uses an iteration callback with a functor to store diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/param_est/generate_data.py b/pyomo/contrib/pynumero/examples/external_grey_box/param_est/generate_data.py index 3588ba3853d..5bf0defbb8d 100644 --- a/pyomo/contrib/pynumero/examples/external_grey_box/param_est/generate_data.py +++ b/pyomo/contrib/pynumero/examples/external_grey_box/param_est/generate_data.py @@ -1,7 +1,7 @@ import pyomo.environ as pyo import numpy.random as rnd import pyomo.contrib.pynumero.examples.external_grey_box.param_est.models as pm -import pandas as pd +from pyomo.common.dependencies import pandas as pd def generate_data(N, UA_mean, UA_std, seed=42): diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/param_est/perform_estimation.py b/pyomo/contrib/pynumero/examples/external_grey_box/param_est/perform_estimation.py index 29ca7145475..f27192f9281 100644 --- a/pyomo/contrib/pynumero/examples/external_grey_box/param_est/perform_estimation.py +++ b/pyomo/contrib/pynumero/examples/external_grey_box/param_est/perform_estimation.py @@ -1,7 +1,7 @@ import sys import pyomo.environ as pyo import numpy.random as rnd -import pandas as pd +from pyomo.common.dependencies import pandas as pd import pyomo.contrib.pynumero.examples.external_grey_box.param_est.models as po diff --git a/pyomo/contrib/sensitivity_toolbox/examples/rooney_biegler.py b/pyomo/contrib/sensitivity_toolbox/examples/rooney_biegler.py index 701d3f71bb4..f058e8189dc 100644 --- a/pyomo/contrib/sensitivity_toolbox/examples/rooney_biegler.py +++ b/pyomo/contrib/sensitivity_toolbox/examples/rooney_biegler.py @@ -15,7 +15,7 @@ model parameter uncertainty using nonlinear confidence regions. AIChE Journal, 47(8), 1794-1804. """ -import pandas as pd +from pyomo.common.dependencies import pandas as pd import pyomo.environ as pyo From 5e874ba2ed0e662c732ae6b83a3921b5753c7c70 Mon Sep 17 00:00:00 2001 From: Michael Bynum Date: Tue, 23 Jan 2024 09:24:05 -0700 Subject: [PATCH 29/43] try using scip for pyros tests --- .github/workflows/test_branches.yml | 2 +- .github/workflows/test_pr_and_main.yml | 2 +- pyomo/contrib/pyros/tests/test_grcs.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test_branches.yml b/.github/workflows/test_branches.yml index a55a5db2433..2fc2232d278 100644 --- a/.github/workflows/test_branches.yml +++ b/.github/workflows/test_branches.yml @@ -618,7 +618,7 @@ jobs: - name: Run Pyomo tests if: matrix.mpi == 0 run: | - $PYTHON_EXE -m pytest -v \ + $PYTHON_EXE -m pytest -rs -v \ -W ignore::Warning ${{matrix.category}} \ pyomo `pwd`/pyomo-model-libraries \ `pwd`/examples `pwd`/doc --junitxml="TEST-pyomo.xml" diff --git a/.github/workflows/test_pr_and_main.yml b/.github/workflows/test_pr_and_main.yml index ac7691d32ae..4a99d25c452 100644 --- a/.github/workflows/test_pr_and_main.yml +++ b/.github/workflows/test_pr_and_main.yml @@ -648,7 +648,7 @@ jobs: - name: Run Pyomo tests if: matrix.mpi == 0 run: | - $PYTHON_EXE -m pytest -v \ + $PYTHON_EXE -m pytest -rs -v \ -W ignore::Warning ${{matrix.category}} \ pyomo `pwd`/pyomo-model-libraries \ `pwd`/examples `pwd`/doc --junitxml="TEST-pyomo.xml" diff --git a/pyomo/contrib/pyros/tests/test_grcs.py b/pyomo/contrib/pyros/tests/test_grcs.py index 05cbdb849f4..11bdf46de75 100644 --- a/pyomo/contrib/pyros/tests/test_grcs.py +++ b/pyomo/contrib/pyros/tests/test_grcs.py @@ -4877,7 +4877,7 @@ def test_higher_order_decision_rules(self): ) @unittest.skipUnless( - baron_license_is_valid, "Global NLP solver is not available and licensed." + scip_available, "Global NLP solver is not available and licensed." ) def test_coefficient_matching_solve(self): # Write the deterministic Pyomo model @@ -4902,8 +4902,8 @@ def test_coefficient_matching_solve(self): pyros_solver = SolverFactory("pyros") # Define subsolvers utilized in the algorithm - local_subsolver = SolverFactory('baron') - global_subsolver = SolverFactory("baron") + local_subsolver = SolverFactory('scip') + global_subsolver = SolverFactory("scip") # Call the PyROS solver results = pyros_solver.solve( From f0f91525add5634c3dd35fbe80c34f2baaf63af1 Mon Sep 17 00:00:00 2001 From: Miranda Mundt <55767766+mrmundt@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:56:42 -0700 Subject: [PATCH 30/43] Change other failed tests to scip --- pyomo/contrib/pyros/tests/test_grcs.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pyomo/contrib/pyros/tests/test_grcs.py b/pyomo/contrib/pyros/tests/test_grcs.py index 11bdf46de75..16de6e19c04 100644 --- a/pyomo/contrib/pyros/tests/test_grcs.py +++ b/pyomo/contrib/pyros/tests/test_grcs.py @@ -4877,7 +4877,7 @@ def test_higher_order_decision_rules(self): ) @unittest.skipUnless( - scip_available, "Global NLP solver is not available and licensed." + scip_available, "Global NLP solver is not available." ) def test_coefficient_matching_solve(self): # Write the deterministic Pyomo model @@ -5010,8 +5010,8 @@ def test_coeff_matching_solver_insensitive(self): ) @unittest.skipUnless( - baron_license_is_valid and baron_version >= (23, 2, 27), - "BARON licensing and version requirements not met", + scip_available, + "NLP solver is not available.", ) def test_coefficient_matching_partitioning_insensitive(self): """ @@ -5023,7 +5023,7 @@ def test_coefficient_matching_partitioning_insensitive(self): m = self.create_mitsos_4_3() # instantiate BARON subsolver and PyROS solver - baron = SolverFactory("baron") + baron = SolverFactory("scip") pyros_solver = SolverFactory("pyros") # solve with PyROS @@ -5216,8 +5216,8 @@ def test_coefficient_matching_nonlinear_expr(self): @unittest.skipUnless( - baron_available and baron_license_is_valid, - "Global NLP solver is not available and licensed.", + scip_available, + "Global NLP solver is not available.", ) class testBypassingSeparation(unittest.TestCase): def test_bypass_global_separation(self): @@ -5241,7 +5241,7 @@ def test_bypass_global_separation(self): # Define subsolvers utilized in the algorithm local_subsolver = SolverFactory('ipopt') - global_subsolver = SolverFactory("baron") + global_subsolver = SolverFactory("scip") # Call the PyROS solver with LoggingIntercept(level=logging.WARNING) as LOG: @@ -5361,8 +5361,8 @@ def test_uninitialized_vars(self): @unittest.skipUnless( - baron_available and baron_license_is_valid, - "Global NLP solver is not available and licensed.", + scip_available, + "Global NLP solver is not available.", ) class testModelMultipleObjectives(unittest.TestCase): """ @@ -5398,7 +5398,7 @@ def test_multiple_objs(self): # Define subsolvers utilized in the algorithm local_subsolver = SolverFactory('ipopt') - global_subsolver = SolverFactory("baron") + global_subsolver = SolverFactory("scip") solve_kwargs = dict( model=m, From 25bd0c8c70edef26255f412385382402f97bd775 Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Tue, 23 Jan 2024 11:12:10 -0700 Subject: [PATCH 31/43] Apply black --- pyomo/contrib/pyros/tests/test_grcs.py | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/pyomo/contrib/pyros/tests/test_grcs.py b/pyomo/contrib/pyros/tests/test_grcs.py index 16de6e19c04..1690ba72f6f 100644 --- a/pyomo/contrib/pyros/tests/test_grcs.py +++ b/pyomo/contrib/pyros/tests/test_grcs.py @@ -4876,9 +4876,7 @@ def test_higher_order_decision_rules(self): msg="Returned termination condition is not return robust_optimal.", ) - @unittest.skipUnless( - scip_available, "Global NLP solver is not available." - ) + @unittest.skipUnless(scip_available, "Global NLP solver is not available.") def test_coefficient_matching_solve(self): # Write the deterministic Pyomo model m = ConcreteModel() @@ -5009,10 +5007,7 @@ def test_coeff_matching_solver_insensitive(self): ), ) - @unittest.skipUnless( - scip_available, - "NLP solver is not available.", - ) + @unittest.skipUnless(scip_available, "NLP solver is not available.") def test_coefficient_matching_partitioning_insensitive(self): """ Check that result for instance with constraint subject to @@ -5215,10 +5210,7 @@ def test_coefficient_matching_nonlinear_expr(self): ) -@unittest.skipUnless( - scip_available, - "Global NLP solver is not available.", -) +@unittest.skipUnless(scip_available, "Global NLP solver is not available.") class testBypassingSeparation(unittest.TestCase): def test_bypass_global_separation(self): """Test bypassing of global separation solve calls.""" @@ -5360,10 +5352,7 @@ def test_uninitialized_vars(self): ) -@unittest.skipUnless( - scip_available, - "Global NLP solver is not available.", -) +@unittest.skipUnless(scip_available, "Global NLP solver is not available.") class testModelMultipleObjectives(unittest.TestCase): """ This class contains tests for models with multiple From 8287ff03c97f62016fbd083b32b1919a1a6a13f5 Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Tue, 23 Jan 2024 12:47:40 -0700 Subject: [PATCH 32/43] Remove '-rs' flag because it's noisy --- .github/workflows/test_branches.yml | 2 +- .github/workflows/test_pr_and_main.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test_branches.yml b/.github/workflows/test_branches.yml index 2fc2232d278..a55a5db2433 100644 --- a/.github/workflows/test_branches.yml +++ b/.github/workflows/test_branches.yml @@ -618,7 +618,7 @@ jobs: - name: Run Pyomo tests if: matrix.mpi == 0 run: | - $PYTHON_EXE -m pytest -rs -v \ + $PYTHON_EXE -m pytest -v \ -W ignore::Warning ${{matrix.category}} \ pyomo `pwd`/pyomo-model-libraries \ `pwd`/examples `pwd`/doc --junitxml="TEST-pyomo.xml" diff --git a/.github/workflows/test_pr_and_main.yml b/.github/workflows/test_pr_and_main.yml index 4a99d25c452..ac7691d32ae 100644 --- a/.github/workflows/test_pr_and_main.yml +++ b/.github/workflows/test_pr_and_main.yml @@ -648,7 +648,7 @@ jobs: - name: Run Pyomo tests if: matrix.mpi == 0 run: | - $PYTHON_EXE -m pytest -rs -v \ + $PYTHON_EXE -m pytest -v \ -W ignore::Warning ${{matrix.category}} \ pyomo `pwd`/pyomo-model-libraries \ `pwd`/examples `pwd`/doc --junitxml="TEST-pyomo.xml" From bc875c2f3ca11a48d0ac0388d7a4bae625e66e02 Mon Sep 17 00:00:00 2001 From: Emma Johnson Date: Tue, 23 Jan 2024 15:02:07 -0700 Subject: [PATCH 33/43] Adding missing import to gdpopt plugins --- pyomo/contrib/gdpopt/plugins.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyomo/contrib/gdpopt/plugins.py b/pyomo/contrib/gdpopt/plugins.py index 3ebad88a626..9d729c63d9c 100644 --- a/pyomo/contrib/gdpopt/plugins.py +++ b/pyomo/contrib/gdpopt/plugins.py @@ -16,3 +16,4 @@ def load(): import pyomo.contrib.gdpopt.branch_and_bound import pyomo.contrib.gdpopt.loa import pyomo.contrib.gdpopt.ric + import pyomo.contrib.gdpopt.enumerate From 1b9ac7ec32f1d5967c802d9e7568af0f3b77b593 Mon Sep 17 00:00:00 2001 From: Bethany Nicholson Date: Wed, 24 Jan 2024 13:28:03 -0700 Subject: [PATCH 34/43] NFC: Fixing typo in docstring --- pyomo/core/base/suffix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 30d79d57b8c..084b0893809 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -115,7 +115,7 @@ class SuffixDirection(enum.IntEnum): - LOCAL: Suffix is local to Pyomo and should not be sent to or read from the solver. - - EXPORT: Suffix should be sent tot he solver as supplemental model + - EXPORT: Suffix should be sent to the solver as supplemental model information. - IMPORT: Suffix values will be returned from the solver and should From d024718991455519e09149ede53a38df1c067abe Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Fri, 26 Jan 2024 09:28:09 -0700 Subject: [PATCH 35/43] Black 24.1.0 release --- pyomo/common/config.py | 8 +- .../community_detection/community_graph.py | 12 +- .../contrib/community_detection/detection.py | 39 ++-- pyomo/contrib/cp/interval_var.py | 3 +- pyomo/contrib/fbbt/fbbt.py | 66 +++---- pyomo/contrib/gdp_bounds/info.py | 1 + .../gdp_bounds/tests/test_gdp_bounds.py | 1 + pyomo/contrib/gdpopt/branch_and_bound.py | 12 +- pyomo/contrib/gdpopt/gloa.py | 7 +- pyomo/contrib/gdpopt/loa.py | 7 +- pyomo/contrib/gdpopt/ric.py | 7 +- .../incidence_analysis/dulmage_mendelsohn.py | 2 +- pyomo/contrib/interior_point/interface.py | 7 +- pyomo/contrib/latex_printer/latex_printer.py | 12 +- pyomo/contrib/mcpp/pyomo_mcpp.py | 1 - pyomo/contrib/mindtpy/algorithm_base_class.py | 25 ++- pyomo/contrib/mindtpy/single_tree.py | 7 +- pyomo/contrib/mindtpy/tests/nonconvex3.py | 4 +- .../tests/test_mindtpy_solution_pool.py | 1 + pyomo/contrib/mindtpy/util.py | 8 +- pyomo/contrib/multistart/high_conf_stop.py | 1 + pyomo/contrib/multistart/reinit.py | 1 + pyomo/contrib/parmest/parmest.py | 27 ++- pyomo/contrib/parmest/tests/test_parmest.py | 4 +- .../preprocessing/plugins/int_to_binary.py | 1 + .../tests/test_bounds_to_vars_xfrm.py | 1 + .../tests/test_constraint_tightener.py | 1 + .../tests/test_detect_fixed_vars.py | 1 + .../tests/test_equality_propagate.py | 1 + .../preprocessing/tests/test_init_vars.py | 1 + .../preprocessing/tests/test_strip_bounds.py | 1 + .../tests/test_var_aggregator.py | 1 + .../tests/test_zero_sum_propagate.py | 1 + .../tests/test_zero_term_removal.py | 1 + .../tests/external_grey_box_models.py | 3 +- .../tests/test_external_pyomo_block.py | 8 +- .../tests/test_external_pyomo_model.py | 8 +- .../pynumero/sparse/mpi_block_vector.py | 6 +- pyomo/contrib/pyros/master_problem_methods.py | 10 +- .../contrib/pyros/pyros_algorithm_methods.py | 9 +- .../pyros/separation_problem_methods.py | 1 + pyomo/contrib/pyros/tests/test_grcs.py | 6 +- pyomo/contrib/pyros/uncertainty_sets.py | 1 - pyomo/contrib/pyros/util.py | 1 + pyomo/contrib/viewer/residual_table.py | 10 +- pyomo/core/base/block.py | 3 +- pyomo/core/base/boolean_var.py | 9 +- pyomo/core/base/componentuid.py | 8 +- pyomo/core/base/constraint.py | 3 +- pyomo/core/base/enums.py | 1 - pyomo/core/base/expression.py | 3 +- pyomo/core/base/external.py | 22 +-- pyomo/core/base/objective.py | 3 +- pyomo/core/base/param.py | 3 +- pyomo/core/base/piecewise.py | 14 +- pyomo/core/base/set.py | 20 +-- pyomo/core/base/suffix.py | 3 +- pyomo/core/base/var.py | 3 +- pyomo/core/expr/numeric_expr.py | 18 +- pyomo/core/expr/template_expr.py | 8 +- pyomo/core/expr/visitor.py | 1 - .../plugins/transform/expand_connectors.py | 22 ++- .../plugins/transform/logical_to_linear.py | 1 + .../plugins/transform/radix_linearization.py | 5 +- pyomo/core/tests/unit/kernel/test_block.py | 170 ++++++++++-------- pyomo/core/tests/unit/kernel/test_conic.py | 3 +- pyomo/core/tests/unit/test_units.py | 3 +- pyomo/dae/set_utils.py | 4 +- pyomo/dataportal/tests/test_dataportal.py | 20 +-- pyomo/duality/plugins.py | 13 +- pyomo/gdp/plugins/bigm.py | 7 +- pyomo/gdp/plugins/bound_pretransformation.py | 6 +- pyomo/gdp/plugins/cuttingplane.py | 10 +- pyomo/gdp/plugins/hull.py | 6 +- pyomo/gdp/plugins/multiple_bigm.py | 8 +- pyomo/gdp/tests/test_partition_disjuncts.py | 48 +++-- pyomo/neos/plugins/kestrel_plugin.py | 19 +- pyomo/opt/base/solvers.py | 16 +- pyomo/opt/plugins/sol.py | 6 +- pyomo/repn/plugins/ampl/ampl_.py | 6 +- pyomo/repn/plugins/baron_writer.py | 37 ++-- pyomo/repn/plugins/gams_writer.py | 8 +- pyomo/repn/plugins/nl_writer.py | 24 ++- pyomo/repn/tests/ampl/test_nlv2.py | 4 +- pyomo/solvers/plugins/solvers/GAMS.py | 12 +- pyomo/solvers/plugins/solvers/GUROBI.py | 12 +- .../solvers/plugins/solvers/direct_solver.py | 6 +- pyomo/solvers/plugins/solvers/mosek_direct.py | 26 +-- .../plugins/solvers/mosek_persistent.py | 20 +-- .../plugins/solvers/persistent_solver.py | 6 +- pyomo/solvers/tests/checks/test_GAMS.py | 24 +-- pyomo/solvers/tests/checks/test_cplex.py | 18 +- 92 files changed, 516 insertions(+), 512 deletions(-) diff --git a/pyomo/common/config.py b/pyomo/common/config.py index 61e4f682a2a..e3466eb7686 100644 --- a/pyomo/common/config.py +++ b/pyomo/common/config.py @@ -1396,9 +1396,11 @@ def _item_body(self, indent, obj): None, [ 'dict' if isinstance(obj, ConfigDict) else obj.domain_name(), - 'optional' - if obj._default is None - else f'default={repr(obj._default)}', + ( + 'optional' + if obj._default is None + else f'default={repr(obj._default)}' + ), ], ) ) diff --git a/pyomo/contrib/community_detection/community_graph.py b/pyomo/contrib/community_detection/community_graph.py index d4aa7e0b973..f0a1f9149bd 100644 --- a/pyomo/contrib/community_detection/community_graph.py +++ b/pyomo/contrib/community_detection/community_graph.py @@ -116,9 +116,9 @@ def generate_model_graph( ] # Update constraint_variable_map - constraint_variable_map[ - numbered_constraint - ] = numbered_variables_in_constraint_equation + constraint_variable_map[numbered_constraint] = ( + numbered_variables_in_constraint_equation + ) # Create a list of all the edges that need to be created based on the variables in this constraint equation edges_between_nodes = [ @@ -145,9 +145,9 @@ def generate_model_graph( ] # Update constraint_variable_map - constraint_variable_map[ - numbered_objective - ] = numbered_variables_in_objective + constraint_variable_map[numbered_objective] = ( + numbered_variables_in_objective + ) # Create a list of all the edges that need to be created based on the variables in the objective function edges_between_nodes = [ diff --git a/pyomo/contrib/community_detection/detection.py b/pyomo/contrib/community_detection/detection.py index 5751f54e9c1..c5366394530 100644 --- a/pyomo/contrib/community_detection/detection.py +++ b/pyomo/contrib/community_detection/detection.py @@ -7,6 +7,7 @@ Original implementation developed by Rahul Joglekar in the Grossmann research group. """ + from logging import getLogger from pyomo.common.dependencies import attempt_import @@ -453,16 +454,14 @@ def visualize_model_graph( if type_of_graph != self.type_of_community_map: # Use the generate_model_graph function to create a NetworkX graph of the given model (along with # number_component_map and constraint_variable_map, which will be used to help with drawing the graph) - ( - model_graph, - number_component_map, - constraint_variable_map, - ) = generate_model_graph( - self.model, - type_of_graph=type_of_graph, - with_objective=self.with_objective, - weighted_graph=self.weighted_graph, - use_only_active_components=self.use_only_active_components, + (model_graph, number_component_map, constraint_variable_map) = ( + generate_model_graph( + self.model, + type_of_graph=type_of_graph, + with_objective=self.with_objective, + weighted_graph=self.weighted_graph, + use_only_active_components=self.use_only_active_components, + ) ) else: # This is the case where, as mentioned above, we can use the networkX graph that was made to create @@ -726,13 +725,10 @@ def generate_structured_model(self): variable_in_new_model = structured_model.find_component( new_variable ) - blocked_variable_map[ - variable_in_stored_constraint - ] = blocked_variable_map.get( - variable_in_stored_constraint, [] - ) + [ - variable_in_new_model - ] + blocked_variable_map[variable_in_stored_constraint] = ( + blocked_variable_map.get(variable_in_stored_constraint, []) + + [variable_in_new_model] + ) # Update replace_variables_in_expression_map accordingly replace_variables_in_expression_map[ @@ -802,11 +798,10 @@ def generate_structured_model(self): variable_in_new_model = structured_model.find_component( new_variable ) - blocked_variable_map[ - variable_in_objective - ] = blocked_variable_map.get(variable_in_objective, []) + [ - variable_in_new_model - ] + blocked_variable_map[variable_in_objective] = ( + blocked_variable_map.get(variable_in_objective, []) + + [variable_in_new_model] + ) # Update the dictionary that we will use to replace the variables replace_variables_in_expression_map[ diff --git a/pyomo/contrib/cp/interval_var.py b/pyomo/contrib/cp/interval_var.py index 911d9ba50ba..4e22c2b2d3d 100644 --- a/pyomo/contrib/cp/interval_var.py +++ b/pyomo/contrib/cp/interval_var.py @@ -161,8 +161,7 @@ def __init__( optional=False, name=None, doc=None - ): - ... + ): ... def __init__(self, *args, **kwargs): _start_arg = kwargs.pop('start', None) diff --git a/pyomo/contrib/fbbt/fbbt.py b/pyomo/contrib/fbbt/fbbt.py index db33c27dd96..bf42cbe7f33 100644 --- a/pyomo/contrib/fbbt/fbbt.py +++ b/pyomo/contrib/fbbt/fbbt.py @@ -919,38 +919,38 @@ def _prop_bnds_root_to_leaf_GeneralExpression(node, bnds_dict, feasibility_tol): _prop_bnds_root_to_leaf_map = dict() -_prop_bnds_root_to_leaf_map[ - numeric_expr.ProductExpression -] = _prop_bnds_root_to_leaf_ProductExpression -_prop_bnds_root_to_leaf_map[ - numeric_expr.DivisionExpression -] = _prop_bnds_root_to_leaf_DivisionExpression -_prop_bnds_root_to_leaf_map[ - numeric_expr.PowExpression -] = _prop_bnds_root_to_leaf_PowExpression -_prop_bnds_root_to_leaf_map[ - numeric_expr.SumExpression -] = _prop_bnds_root_to_leaf_SumExpression -_prop_bnds_root_to_leaf_map[ - numeric_expr.MonomialTermExpression -] = _prop_bnds_root_to_leaf_ProductExpression -_prop_bnds_root_to_leaf_map[ - numeric_expr.NegationExpression -] = _prop_bnds_root_to_leaf_NegationExpression -_prop_bnds_root_to_leaf_map[ - numeric_expr.UnaryFunctionExpression -] = _prop_bnds_root_to_leaf_UnaryFunctionExpression -_prop_bnds_root_to_leaf_map[ - numeric_expr.LinearExpression -] = _prop_bnds_root_to_leaf_SumExpression +_prop_bnds_root_to_leaf_map[numeric_expr.ProductExpression] = ( + _prop_bnds_root_to_leaf_ProductExpression +) +_prop_bnds_root_to_leaf_map[numeric_expr.DivisionExpression] = ( + _prop_bnds_root_to_leaf_DivisionExpression +) +_prop_bnds_root_to_leaf_map[numeric_expr.PowExpression] = ( + _prop_bnds_root_to_leaf_PowExpression +) +_prop_bnds_root_to_leaf_map[numeric_expr.SumExpression] = ( + _prop_bnds_root_to_leaf_SumExpression +) +_prop_bnds_root_to_leaf_map[numeric_expr.MonomialTermExpression] = ( + _prop_bnds_root_to_leaf_ProductExpression +) +_prop_bnds_root_to_leaf_map[numeric_expr.NegationExpression] = ( + _prop_bnds_root_to_leaf_NegationExpression +) +_prop_bnds_root_to_leaf_map[numeric_expr.UnaryFunctionExpression] = ( + _prop_bnds_root_to_leaf_UnaryFunctionExpression +) +_prop_bnds_root_to_leaf_map[numeric_expr.LinearExpression] = ( + _prop_bnds_root_to_leaf_SumExpression +) _prop_bnds_root_to_leaf_map[numeric_expr.AbsExpression] = _prop_bnds_root_to_leaf_abs -_prop_bnds_root_to_leaf_map[ - _GeneralExpressionData -] = _prop_bnds_root_to_leaf_GeneralExpression -_prop_bnds_root_to_leaf_map[ - ScalarExpression -] = _prop_bnds_root_to_leaf_GeneralExpression +_prop_bnds_root_to_leaf_map[_GeneralExpressionData] = ( + _prop_bnds_root_to_leaf_GeneralExpression +) +_prop_bnds_root_to_leaf_map[ScalarExpression] = ( + _prop_bnds_root_to_leaf_GeneralExpression +) def _check_and_reset_bounds(var, lb, ub): @@ -1033,9 +1033,9 @@ def _register_new_before_child_handler(visitor, child): _before_child_handlers = defaultdict(lambda: _register_new_before_child_handler) -_before_child_handlers[ - numeric_expr.ExternalFunctionExpression -] = _before_external_function +_before_child_handlers[numeric_expr.ExternalFunctionExpression] = ( + _before_external_function +) for _type in nonpyomo_leaf_types: _before_child_handlers[_type] = _before_constant diff --git a/pyomo/contrib/gdp_bounds/info.py b/pyomo/contrib/gdp_bounds/info.py index bad76e0f2f7..3ee87041d25 100644 --- a/pyomo/contrib/gdp_bounds/info.py +++ b/pyomo/contrib/gdp_bounds/info.py @@ -1,4 +1,5 @@ """Provides functions for retrieving disjunctive variable bound information stored on a model.""" + from pyomo.common.collections import ComponentMap from pyomo.core import value diff --git a/pyomo/contrib/gdp_bounds/tests/test_gdp_bounds.py b/pyomo/contrib/gdp_bounds/tests/test_gdp_bounds.py index a5f7780f043..e856ae247f3 100644 --- a/pyomo/contrib/gdp_bounds/tests/test_gdp_bounds.py +++ b/pyomo/contrib/gdp_bounds/tests/test_gdp_bounds.py @@ -1,4 +1,5 @@ """Tests explicit bound to variable bound transformation module.""" + import pyomo.common.unittest as unittest from pyomo.contrib.gdp_bounds.info import disjunctive_lb, disjunctive_ub from pyomo.environ import ( diff --git a/pyomo/contrib/gdpopt/branch_and_bound.py b/pyomo/contrib/gdpopt/branch_and_bound.py index f69a92efe16..26dc2b5f2eb 100644 --- a/pyomo/contrib/gdpopt/branch_and_bound.py +++ b/pyomo/contrib/gdpopt/branch_and_bound.py @@ -179,13 +179,13 @@ def _solve_gdp(self, model, config): # TODO might be worthwhile to log number of nonlinear # constraints in each disjunction for later branching # purposes - root_util_blk.disjunct_to_nonlinear_constraints[ - disjunct - ] = nonlinear_constraints_in_disjunct + root_util_blk.disjunct_to_nonlinear_constraints[disjunct] = ( + nonlinear_constraints_in_disjunct + ) - root_util_blk.disjunction_to_unfixed_disjuncts[ - disjunction - ] = unfixed_disjuncts + root_util_blk.disjunction_to_unfixed_disjuncts[disjunction] = ( + unfixed_disjuncts + ) pass # Add the BigM suffix if it does not already exist. Used later during diff --git a/pyomo/contrib/gdpopt/gloa.py b/pyomo/contrib/gdpopt/gloa.py index ba8ed2fe234..68bd692f967 100644 --- a/pyomo/contrib/gdpopt/gloa.py +++ b/pyomo/contrib/gdpopt/gloa.py @@ -89,10 +89,9 @@ def _solve_gdp(self, original_model, config): # constraints will be added by the transformation to a MIP, so these are # all we'll ever need. add_global_constraint_list(self.original_util_block) - ( - discrete_problem_util_block, - subproblem_util_block, - ) = _get_discrete_problem_and_subproblem(self, config) + (discrete_problem_util_block, subproblem_util_block) = ( + _get_discrete_problem_and_subproblem(self, config) + ) discrete = discrete_problem_util_block.parent_block() subproblem = subproblem_util_block.parent_block() discrete_obj = next( diff --git a/pyomo/contrib/gdpopt/loa.py b/pyomo/contrib/gdpopt/loa.py index 6a9889065bf..44c1f8609e8 100644 --- a/pyomo/contrib/gdpopt/loa.py +++ b/pyomo/contrib/gdpopt/loa.py @@ -99,10 +99,9 @@ def _solve_gdp(self, original_model, config): # We'll need these to get dual info after solving subproblems add_constraint_list(self.original_util_block) - ( - discrete_problem_util_block, - subproblem_util_block, - ) = _get_discrete_problem_and_subproblem(self, config) + (discrete_problem_util_block, subproblem_util_block) = ( + _get_discrete_problem_and_subproblem(self, config) + ) discrete = discrete_problem_util_block.parent_block() subproblem = subproblem_util_block.parent_block() diff --git a/pyomo/contrib/gdpopt/ric.py b/pyomo/contrib/gdpopt/ric.py index f3eb83b79a9..586a27362a1 100644 --- a/pyomo/contrib/gdpopt/ric.py +++ b/pyomo/contrib/gdpopt/ric.py @@ -62,10 +62,9 @@ def solve(self, model, **kwds): def _solve_gdp(self, original_model, config): logger = config.logger - ( - discrete_problem_util_block, - subproblem_util_block, - ) = _get_discrete_problem_and_subproblem(self, config) + (discrete_problem_util_block, subproblem_util_block) = ( + _get_discrete_problem_and_subproblem(self, config) + ) discrete_problem = discrete_problem_util_block.parent_block() subproblem = subproblem_util_block.parent_block() discrete_problem_obj = next( diff --git a/pyomo/contrib/incidence_analysis/dulmage_mendelsohn.py b/pyomo/contrib/incidence_analysis/dulmage_mendelsohn.py index 5a1b125c0ae..eb24b0559fc 100644 --- a/pyomo/contrib/incidence_analysis/dulmage_mendelsohn.py +++ b/pyomo/contrib/incidence_analysis/dulmage_mendelsohn.py @@ -160,7 +160,7 @@ def dulmage_mendelsohn(matrix_or_graph, top_nodes=None, matching=None): partition = ( row_partition, - tuple([n - M for n in subset] for subset in col_partition) + tuple([n - M for n in subset] for subset in col_partition), # Column nodes have values in [M, M+N-1]. Apply the offset # to get values corresponding to indices in user's matrix. ) diff --git a/pyomo/contrib/interior_point/interface.py b/pyomo/contrib/interior_point/interface.py index 38d91be5566..7d04f578238 100644 --- a/pyomo/contrib/interior_point/interface.py +++ b/pyomo/contrib/interior_point/interface.py @@ -258,10 +258,9 @@ def __init__(self, pyomo_model): # set the init_duals_primals_lb/ub from ipopt_zL_out, ipopt_zU_out if available # need to compress them as well and initialize the duals_primals_lb/ub - ( - self._init_duals_primals_lb, - self._init_duals_primals_ub, - ) = self._get_full_duals_primals_bounds() + (self._init_duals_primals_lb, self._init_duals_primals_ub) = ( + self._get_full_duals_primals_bounds() + ) self._init_duals_primals_lb[np.isneginf(self._nlp.primals_lb())] = 0 self._init_duals_primals_ub[np.isinf(self._nlp.primals_ub())] = 0 self._duals_primals_lb = self._init_duals_primals_lb.copy() diff --git a/pyomo/contrib/latex_printer/latex_printer.py b/pyomo/contrib/latex_printer/latex_printer.py index 63c8caddcd2..b84f9a420fc 100644 --- a/pyomo/contrib/latex_printer/latex_printer.py +++ b/pyomo/contrib/latex_printer/latex_printer.py @@ -1087,12 +1087,12 @@ def latex_printer( for ky, vl in setInfo.items(): ix = int(ky[3:]) - 1 setInfo[ky]['setObject'] = setMap_inverse[ky] # setList[ix] - setInfo[ky][ - 'setRegEx' - ] = r'__S_PLACEHOLDER_8675309_GROUP_([0-9*])_%s__' % (ky) - setInfo[ky][ - 'sumSetRegEx' - ] = r'sum_{__S_PLACEHOLDER_8675309_GROUP_([0-9*])_%s__}' % (ky) + setInfo[ky]['setRegEx'] = ( + r'__S_PLACEHOLDER_8675309_GROUP_([0-9*])_%s__' % (ky) + ) + setInfo[ky]['sumSetRegEx'] = ( + r'sum_{__S_PLACEHOLDER_8675309_GROUP_([0-9*])_%s__}' % (ky) + ) # setInfo[ky]['idxRegEx'] = r'__I_PLACEHOLDER_8675309_GROUP_[0-9*]_%s__'%(ky) if explicit_set_summation: diff --git a/pyomo/contrib/mcpp/pyomo_mcpp.py b/pyomo/contrib/mcpp/pyomo_mcpp.py index bfd4b80edc3..817b18bff7c 100644 --- a/pyomo/contrib/mcpp/pyomo_mcpp.py +++ b/pyomo/contrib/mcpp/pyomo_mcpp.py @@ -383,7 +383,6 @@ def finalizeResult(self, node_result): class McCormick(object): - """ This class takes the constructed expression from MCPP_Visitor and allows for MC methods to be performed on pyomo expressions. diff --git a/pyomo/contrib/mindtpy/algorithm_base_class.py b/pyomo/contrib/mindtpy/algorithm_base_class.py index c9169ab8c62..570e7c0a27d 100644 --- a/pyomo/contrib/mindtpy/algorithm_base_class.py +++ b/pyomo/contrib/mindtpy/algorithm_base_class.py @@ -2607,9 +2607,9 @@ def initialize_subsolvers(self): if config.mip_regularization_solver == 'gams': self.regularization_mip_opt.options['add_options'] = [] if config.regularization_mip_threads > 0: - self.regularization_mip_opt.options[ - 'threads' - ] = config.regularization_mip_threads + self.regularization_mip_opt.options['threads'] = ( + config.regularization_mip_threads + ) else: self.regularization_mip_opt.options['threads'] = config.threads @@ -2619,9 +2619,9 @@ def initialize_subsolvers(self): 'cplex_persistent', }: if config.solution_limit is not None: - self.regularization_mip_opt.options[ - 'mip_limits_solutions' - ] = config.solution_limit + self.regularization_mip_opt.options['mip_limits_solutions'] = ( + config.solution_limit + ) # We don't need to solve the regularization problem to optimality. # We will choose to perform aggressive node probing during presolve. self.regularization_mip_opt.options['mip_strategy_presolvenode'] = 3 @@ -2634,9 +2634,9 @@ def initialize_subsolvers(self): self.regularization_mip_opt.options['optimalitytarget'] = 3 elif config.mip_regularization_solver == 'gurobi': if config.solution_limit is not None: - self.regularization_mip_opt.options[ - 'SolutionLimit' - ] = config.solution_limit + self.regularization_mip_opt.options['SolutionLimit'] = ( + config.solution_limit + ) # Same reason as mip_strategy_presolvenode. self.regularization_mip_opt.options['Presolve'] = 2 @@ -2983,10 +2983,9 @@ def add_regularization(self): # The main problem might be unbounded, regularization is activated only when a valid bound is provided. if self.dual_bound != self.dual_bound_progress[0]: with time_code(self.timing, 'regularization main'): - ( - regularization_main_mip, - regularization_main_mip_results, - ) = self.solve_regularization_main() + (regularization_main_mip, regularization_main_mip_results) = ( + self.solve_regularization_main() + ) self.handle_regularization_main_tc( regularization_main_mip, regularization_main_mip_results ) diff --git a/pyomo/contrib/mindtpy/single_tree.py b/pyomo/contrib/mindtpy/single_tree.py index 9776920f434..5383624b6aa 100644 --- a/pyomo/contrib/mindtpy/single_tree.py +++ b/pyomo/contrib/mindtpy/single_tree.py @@ -629,10 +629,9 @@ def handle_lazy_subproblem_infeasible(self, fixed_nlp, mindtpy_solver, config, o dual_values = None config.logger.info('Solving feasibility problem') - ( - feas_subproblem, - feas_subproblem_results, - ) = mindtpy_solver.solve_feasibility_subproblem() + (feas_subproblem, feas_subproblem_results) = ( + mindtpy_solver.solve_feasibility_subproblem() + ) # In OA algorithm, OA cuts are generated based on the solution of the subproblem # We need to first copy the value of variables from the subproblem and then add cuts copy_var_list_values( diff --git a/pyomo/contrib/mindtpy/tests/nonconvex3.py b/pyomo/contrib/mindtpy/tests/nonconvex3.py index dbb88bb1fad..b08deb67b63 100644 --- a/pyomo/contrib/mindtpy/tests/nonconvex3.py +++ b/pyomo/contrib/mindtpy/tests/nonconvex3.py @@ -40,9 +40,7 @@ def __init__(self, *args, **kwargs): m.objective = Objective(expr=7 * m.x1 + 10 * m.x2, sense=minimize) - m.c1 = Constraint( - expr=(m.x1**1.2) * (m.x2**1.7) - 7 * m.x1 - 9 * m.x2 <= -24 - ) + m.c1 = Constraint(expr=(m.x1**1.2) * (m.x2**1.7) - 7 * m.x1 - 9 * m.x2 <= -24) m.c2 = Constraint(expr=-m.x1 - 2 * m.x2 <= 5) m.c3 = Constraint(expr=-3 * m.x1 + m.x2 <= 1) m.c4 = Constraint(expr=4 * m.x1 - 3 * m.x2 <= 11) diff --git a/pyomo/contrib/mindtpy/tests/test_mindtpy_solution_pool.py b/pyomo/contrib/mindtpy/tests/test_mindtpy_solution_pool.py index e8ad85ad9bc..7a9898d3c7b 100644 --- a/pyomo/contrib/mindtpy/tests/test_mindtpy_solution_pool.py +++ b/pyomo/contrib/mindtpy/tests/test_mindtpy_solution_pool.py @@ -1,4 +1,5 @@ """Tests for solution pool in the MindtPy solver.""" + from pyomo.core.expr.calculus.diff_with_sympy import differentiate_available import pyomo.common.unittest as unittest from pyomo.contrib.mindtpy.tests.eight_process_problem import EightProcessFlowsheet diff --git a/pyomo/contrib/mindtpy/util.py b/pyomo/contrib/mindtpy/util.py index e336715cc8f..cd2b31e5954 100644 --- a/pyomo/contrib/mindtpy/util.py +++ b/pyomo/contrib/mindtpy/util.py @@ -344,9 +344,11 @@ def generate_lag_objective_function( with time_code(timing, 'PyomoNLP'): nlp = pyomo_nlp.PyomoNLP(temp_model) lam = [ - -temp_model.dual[constr] - if abs(temp_model.dual[constr]) > config.zero_tolerance - else 0 + ( + -temp_model.dual[constr] + if abs(temp_model.dual[constr]) > config.zero_tolerance + else 0 + ) for constr in nlp.get_pyomo_constraints() ] nlp.set_duals(lam) diff --git a/pyomo/contrib/multistart/high_conf_stop.py b/pyomo/contrib/multistart/high_conf_stop.py index e18467c1741..f85daf633de 100644 --- a/pyomo/contrib/multistart/high_conf_stop.py +++ b/pyomo/contrib/multistart/high_conf_stop.py @@ -5,6 +5,7 @@ range, given some confidence. """ + from __future__ import division from collections import Counter diff --git a/pyomo/contrib/multistart/reinit.py b/pyomo/contrib/multistart/reinit.py index 214192df648..3904a7e343f 100644 --- a/pyomo/contrib/multistart/reinit.py +++ b/pyomo/contrib/multistart/reinit.py @@ -1,4 +1,5 @@ """Helper functions for variable reinitialization.""" + from __future__ import division import logging diff --git a/pyomo/contrib/parmest/parmest.py b/pyomo/contrib/parmest/parmest.py index cbdc9179f35..82bf893dd06 100644 --- a/pyomo/contrib/parmest/parmest.py +++ b/pyomo/contrib/parmest/parmest.py @@ -548,14 +548,13 @@ def _Q_opt( for ndname, Var, solval in ef_nonants(ef): ind_vars.append(Var) # calculate the reduced hessian - ( - solve_result, - inv_red_hes, - ) = inverse_reduced_hessian.inv_reduced_hessian_barrier( - self.ef_instance, - independent_variables=ind_vars, - solver_options=self.solver_options, - tee=self.tee, + (solve_result, inv_red_hes) = ( + inverse_reduced_hessian.inv_reduced_hessian_barrier( + self.ef_instance, + independent_variables=ind_vars, + solver_options=self.solver_options, + tee=self.tee, + ) ) if self.diagnostic_mode: @@ -745,14 +744,10 @@ def _Q_at_theta(self, thetavals, initialize_parmest_model=False): if self.diagnostic_mode: print(' Experiment = ', snum) print(' First solve with special diagnostics wrapper') - ( - status_obj, - solved, - iters, - time, - regu, - ) = utils.ipopt_solve_with_stats( - instance, optimizer, max_iter=500, max_cpu_time=120 + (status_obj, solved, iters, time, regu) = ( + utils.ipopt_solve_with_stats( + instance, optimizer, max_iter=500, max_cpu_time=120 + ) ) print( " status_obj, solved, iters, time, regularization_stat = ", diff --git a/pyomo/contrib/parmest/tests/test_parmest.py b/pyomo/contrib/parmest/tests/test_parmest.py index 2cc8ad36b0a..b5c1fe1bfac 100644 --- a/pyomo/contrib/parmest/tests/test_parmest.py +++ b/pyomo/contrib/parmest/tests/test_parmest.py @@ -411,9 +411,7 @@ def rooney_biegler_indexed_vars(data): model.theta = pyo.Var( model.var_names, initialize={"asymptote": 15, "rate_constant": 0.5} ) - model.theta[ - "asymptote" - ].fixed = ( + model.theta["asymptote"].fixed = ( True # parmest will unfix theta variables, even when they are indexed ) model.theta["rate_constant"].fixed = True diff --git a/pyomo/contrib/preprocessing/plugins/int_to_binary.py b/pyomo/contrib/preprocessing/plugins/int_to_binary.py index 8b264868ba5..55b9d26948f 100644 --- a/pyomo/contrib/preprocessing/plugins/int_to_binary.py +++ b/pyomo/contrib/preprocessing/plugins/int_to_binary.py @@ -1,4 +1,5 @@ """Transformation to reformulate integer variables into binary.""" + from __future__ import division from math import floor, log diff --git a/pyomo/contrib/preprocessing/tests/test_bounds_to_vars_xfrm.py b/pyomo/contrib/preprocessing/tests/test_bounds_to_vars_xfrm.py index 5770b23eb11..c2b8acd3e49 100644 --- a/pyomo/contrib/preprocessing/tests/test_bounds_to_vars_xfrm.py +++ b/pyomo/contrib/preprocessing/tests/test_bounds_to_vars_xfrm.py @@ -1,4 +1,5 @@ """Tests explicit bound to variable bound transformation module.""" + import pyomo.common.unittest as unittest from pyomo.environ import ( ConcreteModel, diff --git a/pyomo/contrib/preprocessing/tests/test_constraint_tightener.py b/pyomo/contrib/preprocessing/tests/test_constraint_tightener.py index aa7fa52d272..8f36bee15a1 100644 --- a/pyomo/contrib/preprocessing/tests/test_constraint_tightener.py +++ b/pyomo/contrib/preprocessing/tests/test_constraint_tightener.py @@ -1,4 +1,5 @@ """Tests the Bounds Tightening module.""" + import pyomo.common.unittest as unittest from pyomo.environ import ConcreteModel, Constraint, TransformationFactory, Var, value diff --git a/pyomo/contrib/preprocessing/tests/test_detect_fixed_vars.py b/pyomo/contrib/preprocessing/tests/test_detect_fixed_vars.py index d40206d621b..b3c72531f77 100644 --- a/pyomo/contrib/preprocessing/tests/test_detect_fixed_vars.py +++ b/pyomo/contrib/preprocessing/tests/test_detect_fixed_vars.py @@ -1,4 +1,5 @@ """Tests detection of fixed variables.""" + import pyomo.common.unittest as unittest from pyomo.environ import ConcreteModel, TransformationFactory, Var, value diff --git a/pyomo/contrib/preprocessing/tests/test_equality_propagate.py b/pyomo/contrib/preprocessing/tests/test_equality_propagate.py index 40e1d7eecb9..b77f5c5f3f5 100644 --- a/pyomo/contrib/preprocessing/tests/test_equality_propagate.py +++ b/pyomo/contrib/preprocessing/tests/test_equality_propagate.py @@ -1,4 +1,5 @@ """Tests the equality set propagation module.""" + import pyomo.common.unittest as unittest from pyomo.common.errors import InfeasibleConstraintException diff --git a/pyomo/contrib/preprocessing/tests/test_init_vars.py b/pyomo/contrib/preprocessing/tests/test_init_vars.py index f65773f7dbb..e52c9fd5cc8 100644 --- a/pyomo/contrib/preprocessing/tests/test_init_vars.py +++ b/pyomo/contrib/preprocessing/tests/test_init_vars.py @@ -1,4 +1,5 @@ """Tests initialization of uninitialized variables.""" + import pyomo.common.unittest as unittest from pyomo.environ import ConcreteModel, TransformationFactory, value, Var diff --git a/pyomo/contrib/preprocessing/tests/test_strip_bounds.py b/pyomo/contrib/preprocessing/tests/test_strip_bounds.py index deb1b6c8b37..a8526c613c4 100644 --- a/pyomo/contrib/preprocessing/tests/test_strip_bounds.py +++ b/pyomo/contrib/preprocessing/tests/test_strip_bounds.py @@ -1,4 +1,5 @@ """Tests stripping of variable bounds.""" + import pyomo.common.unittest as unittest from pyomo.environ import ( diff --git a/pyomo/contrib/preprocessing/tests/test_var_aggregator.py b/pyomo/contrib/preprocessing/tests/test_var_aggregator.py index d44f8abdeb2..1f2c06dd0d1 100644 --- a/pyomo/contrib/preprocessing/tests/test_var_aggregator.py +++ b/pyomo/contrib/preprocessing/tests/test_var_aggregator.py @@ -1,4 +1,5 @@ """Tests the variable aggregation module.""" + import pyomo.common.unittest as unittest from pyomo.common.collections import ComponentSet from pyomo.contrib.preprocessing.plugins.var_aggregator import ( diff --git a/pyomo/contrib/preprocessing/tests/test_zero_sum_propagate.py b/pyomo/contrib/preprocessing/tests/test_zero_sum_propagate.py index e5dc132628b..bec889c7635 100644 --- a/pyomo/contrib/preprocessing/tests/test_zero_sum_propagate.py +++ b/pyomo/contrib/preprocessing/tests/test_zero_sum_propagate.py @@ -1,4 +1,5 @@ """Tests the zero sum propagation module.""" + import pyomo.common.unittest as unittest from pyomo.environ import ( ConcreteModel, diff --git a/pyomo/contrib/preprocessing/tests/test_zero_term_removal.py b/pyomo/contrib/preprocessing/tests/test_zero_term_removal.py index 7ff40b6ae32..d1b74822747 100644 --- a/pyomo/contrib/preprocessing/tests/test_zero_term_removal.py +++ b/pyomo/contrib/preprocessing/tests/test_zero_term_removal.py @@ -1,4 +1,5 @@ """Tests detection of zero terms.""" + import pyomo.common.unittest as unittest from pyomo.environ import ConcreteModel, Constraint, TransformationFactory, Var import pyomo.core.expr as EXPR diff --git a/pyomo/contrib/pynumero/interfaces/tests/external_grey_box_models.py b/pyomo/contrib/pynumero/interfaces/tests/external_grey_box_models.py index 1f2a5169857..e65e9a7eb5c 100644 --- a/pyomo/contrib/pynumero/interfaces/tests/external_grey_box_models.py +++ b/pyomo/contrib/pynumero/interfaces/tests/external_grey_box_models.py @@ -298,8 +298,7 @@ def evaluate_equality_constraints(self): P2 = self._input_values[3] Pout = self._input_values[4] return np.asarray( - [P2 - (Pin - 2 * c * F**2), Pout - (P2 - 2 * c * F**2)], - dtype=np.float64, + [P2 - (Pin - 2 * c * F**2), Pout - (P2 - 2 * c * F**2)], dtype=np.float64 ) def evaluate_jacobian_equality_constraints(self): diff --git a/pyomo/contrib/pynumero/interfaces/tests/test_external_pyomo_block.py b/pyomo/contrib/pynumero/interfaces/tests/test_external_pyomo_block.py index 2d758e2e1a9..7e250b9194e 100644 --- a/pyomo/contrib/pynumero/interfaces/tests/test_external_pyomo_block.py +++ b/pyomo/contrib/pynumero/interfaces/tests/test_external_pyomo_block.py @@ -68,12 +68,8 @@ def _make_external_model(): m.y_out = pyo.Var() m.c_out_1 = pyo.Constraint(expr=m.x_out - m.x == 0) m.c_out_2 = pyo.Constraint(expr=m.y_out - m.y == 0) - m.c_ex_1 = pyo.Constraint( - expr=m.x**3 - 2 * m.y == m.a**2 + m.b**3 - m.r**3 - 2 - ) - m.c_ex_2 = pyo.Constraint( - expr=m.x + m.y**3 == m.a**3 + 2 * m.b**2 + m.r**2 + 1 - ) + m.c_ex_1 = pyo.Constraint(expr=m.x**3 - 2 * m.y == m.a**2 + m.b**3 - m.r**3 - 2) + m.c_ex_2 = pyo.Constraint(expr=m.x + m.y**3 == m.a**3 + 2 * m.b**2 + m.r**2 + 1) return m diff --git a/pyomo/contrib/pynumero/interfaces/tests/test_external_pyomo_model.py b/pyomo/contrib/pynumero/interfaces/tests/test_external_pyomo_model.py index f808decf26c..390d0b6fe63 100644 --- a/pyomo/contrib/pynumero/interfaces/tests/test_external_pyomo_model.py +++ b/pyomo/contrib/pynumero/interfaces/tests/test_external_pyomo_model.py @@ -901,11 +901,9 @@ def test_full_space_lagrangian_hessians(self): # multipliers won't necessarily correspond). external_model.set_external_constraint_multipliers(lam) hlxx, hlxy, hlyy = external_model.get_full_space_lagrangian_hessians() - ( - pred_hlxx, - pred_hlxy, - pred_hlyy, - ) = model.calculate_full_space_lagrangian_hessians(lam, x) + (pred_hlxx, pred_hlxy, pred_hlyy) = ( + model.calculate_full_space_lagrangian_hessians(lam, x) + ) # TODO: Is comparing the array representation sufficient here? # Should I make sure I get the sparse representation I expect? diff --git a/pyomo/contrib/pynumero/sparse/mpi_block_vector.py b/pyomo/contrib/pynumero/sparse/mpi_block_vector.py index 5d89bbf5522..0f57f0eb41e 100644 --- a/pyomo/contrib/pynumero/sparse/mpi_block_vector.py +++ b/pyomo/contrib/pynumero/sparse/mpi_block_vector.py @@ -1112,9 +1112,9 @@ def make_local_copy(self): if ndx in block_indices: blk = self.get_block(ndx) if isinstance(blk, BlockVector): - local_data[ - offset : offset + self.get_block_size(ndx) - ] = blk.flatten() + local_data[offset : offset + self.get_block_size(ndx)] = ( + blk.flatten() + ) elif isinstance(blk, np.ndarray): local_data[offset : offset + self.get_block_size(ndx)] = blk else: diff --git a/pyomo/contrib/pyros/master_problem_methods.py b/pyomo/contrib/pyros/master_problem_methods.py index 5477fcc5048..e2ce74a493e 100644 --- a/pyomo/contrib/pyros/master_problem_methods.py +++ b/pyomo/contrib/pyros/master_problem_methods.py @@ -1,6 +1,7 @@ """ Functions for handling the construction and solving of the GRCS master problem via ROSolver """ + from pyomo.core.base import ( ConcreteModel, Block, @@ -758,12 +759,9 @@ def solver_call_master(model_data, config, solver, solve_data): solver_term_cond_dict[str(opt)] = str(results.solver.termination_condition) master_soln.termination_condition = results.solver.termination_condition master_soln.pyros_termination_condition = None - ( - try_backup, - _, - ) = ( - master_soln.master_subsolver_results - ) = process_termination_condition_master_problem(config=config, results=results) + (try_backup, _) = master_soln.master_subsolver_results = ( + process_termination_condition_master_problem(config=config, results=results) + ) master_soln.nominal_block = nlp_model.scenarios[0, 0] master_soln.results = results diff --git a/pyomo/contrib/pyros/pyros_algorithm_methods.py b/pyomo/contrib/pyros/pyros_algorithm_methods.py index 38f675e64a5..4ae033b9498 100644 --- a/pyomo/contrib/pyros/pyros_algorithm_methods.py +++ b/pyomo/contrib/pyros/pyros_algorithm_methods.py @@ -642,11 +642,10 @@ def ROSolver_iterative_solve(model_data, config): vals.append(dvar.value) dr_var_lists_original.append(vals) - ( - polishing_results, - polishing_successful, - ) = master_problem_methods.minimize_dr_vars( - model_data=master_data, config=config + (polishing_results, polishing_successful) = ( + master_problem_methods.minimize_dr_vars( + model_data=master_data, config=config + ) ) timing_data.total_dr_polish_time += get_time_from_solver(polishing_results) diff --git a/pyomo/contrib/pyros/separation_problem_methods.py b/pyomo/contrib/pyros/separation_problem_methods.py index 240291f5375..b9659f044f4 100644 --- a/pyomo/contrib/pyros/separation_problem_methods.py +++ b/pyomo/contrib/pyros/separation_problem_methods.py @@ -1,6 +1,7 @@ """ Functions for the construction and solving of the GRCS separation problem via ROsolver """ + from pyomo.core.base.constraint import Constraint, ConstraintList from pyomo.core.base.objective import Objective, maximize, value from pyomo.core.base import Var, Param diff --git a/pyomo/contrib/pyros/tests/test_grcs.py b/pyomo/contrib/pyros/tests/test_grcs.py index 1690ba72f6f..8de1c2666b9 100644 --- a/pyomo/contrib/pyros/tests/test_grcs.py +++ b/pyomo/contrib/pyros/tests/test_grcs.py @@ -3766,9 +3766,9 @@ def test_solve_master(self): master_data.master_model.scenarios[0, 0].second_stage_objective = Expression( expr=master_data.master_model.scenarios[0, 0].x ) - master_data.master_model.scenarios[ - 0, 0 - ].util.dr_var_to_exponent_map = ComponentMap() + master_data.master_model.scenarios[0, 0].util.dr_var_to_exponent_map = ( + ComponentMap() + ) master_data.iteration = 0 master_data.timing = TimingData() diff --git a/pyomo/contrib/pyros/uncertainty_sets.py b/pyomo/contrib/pyros/uncertainty_sets.py index 54a268f204e..1b51e41fcaf 100644 --- a/pyomo/contrib/pyros/uncertainty_sets.py +++ b/pyomo/contrib/pyros/uncertainty_sets.py @@ -44,7 +44,6 @@ ``UncertaintySet`` object. """ - import abc import math import functools diff --git a/pyomo/contrib/pyros/util.py b/pyomo/contrib/pyros/util.py index 6aa6ed61936..e2986ae18c7 100644 --- a/pyomo/contrib/pyros/util.py +++ b/pyomo/contrib/pyros/util.py @@ -1,6 +1,7 @@ ''' Utility functions for the PyROS solver ''' + import copy from enum import Enum, auto from pyomo.common.collections import ComponentSet, ComponentMap diff --git a/pyomo/contrib/viewer/residual_table.py b/pyomo/contrib/viewer/residual_table.py index 46a86adbce6..73cf73847e5 100644 --- a/pyomo/contrib/viewer/residual_table.py +++ b/pyomo/contrib/viewer/residual_table.py @@ -102,10 +102,12 @@ def _inactive_to_back(c): self._items.sort( key=lambda o: ( o is None, - get_residual(self.ui_data, o) - if get_residual(self.ui_data, o) is not None - and not isinstance(get_residual(self.ui_data, o), str) - else _inactive_to_back(o), + ( + get_residual(self.ui_data, o) + if get_residual(self.ui_data, o) is not None + and not isinstance(get_residual(self.ui_data, o), str) + else _inactive_to_back(o) + ), ), reverse=True, ) diff --git a/pyomo/core/base/block.py b/pyomo/core/base/block.py index d3950575435..89e872ebbe5 100644 --- a/pyomo/core/base/block.py +++ b/pyomo/core/base/block.py @@ -2056,8 +2056,7 @@ def __new__(cls, *args, **kwds): @overload def __init__( self, *indexes, rule=None, concrete=False, dense=True, name=None, doc=None - ): - ... + ): ... def __init__(self, *args, **kwargs): """Constructor""" diff --git a/pyomo/core/base/boolean_var.py b/pyomo/core/base/boolean_var.py index e2aebb4e466..1945045abdd 100644 --- a/pyomo/core/base/boolean_var.py +++ b/pyomo/core/base/boolean_var.py @@ -283,9 +283,11 @@ def associate_binary_var(self, binary_var): "with '%s') with '%s' is not allowed" % ( self.name, - self._associated_binary().name - if self._associated_binary is not None - else None, + ( + self._associated_binary().name + if self._associated_binary is not None + else None + ), binary_var.name if binary_var is not None else None, ) ) @@ -496,7 +498,6 @@ def _pprint(self): class ScalarBooleanVar(_GeneralBooleanVarData, BooleanVar): - """A single variable.""" def __init__(self, *args, **kwd): diff --git a/pyomo/core/base/componentuid.py b/pyomo/core/base/componentuid.py index 0ab57d1c253..89f7e5f8320 100644 --- a/pyomo/core/base/componentuid.py +++ b/pyomo/core/base/componentuid.py @@ -144,9 +144,11 @@ def __hash__(self): ( name, tuple( - (slice, x.start, x.stop, x.step) - if x.__class__ is slice - else x + ( + (slice, x.start, x.stop, x.step) + if x.__class__ is slice + else x + ) for x in idx ), ) diff --git a/pyomo/core/base/constraint.py b/pyomo/core/base/constraint.py index 53afa35c70c..e391b4a5605 100644 --- a/pyomo/core/base/constraint.py +++ b/pyomo/core/base/constraint.py @@ -746,8 +746,7 @@ def __new__(cls, *args, **kwds): return super(Constraint, cls).__new__(IndexedConstraint) @overload - def __init__(self, *indexes, expr=None, rule=None, name=None, doc=None): - ... + def __init__(self, *indexes, expr=None, rule=None, name=None, doc=None): ... def __init__(self, *args, **kwargs): _init = self._pop_from_kwargs('Constraint', kwargs, ('rule', 'expr'), None) diff --git a/pyomo/core/base/enums.py b/pyomo/core/base/enums.py index 972d6b09117..ddcc66fdc4e 100644 --- a/pyomo/core/base/enums.py +++ b/pyomo/core/base/enums.py @@ -33,7 +33,6 @@ class TraversalStrategy(enum.Enum, **strictEnum): class SortComponents(enum.Flag, **strictEnum): - """ This class is a convenient wrapper for specifying various sort ordering. We pass these objects to the "sort" argument to various diff --git a/pyomo/core/base/expression.py b/pyomo/core/base/expression.py index df9abf0a5a5..780bc17c8a3 100644 --- a/pyomo/core/base/expression.py +++ b/pyomo/core/base/expression.py @@ -293,8 +293,7 @@ def __new__(cls, *args, **kwds): @overload def __init__( self, *indexes, rule=None, expr=None, initialize=None, name=None, doc=None - ): - ... + ): ... def __init__(self, *args, **kwds): _init = self._pop_from_kwargs( diff --git a/pyomo/core/base/external.py b/pyomo/core/base/external.py index 8157ca4badb..93fb69e8cf7 100644 --- a/pyomo/core/base/external.py +++ b/pyomo/core/base/external.py @@ -81,12 +81,10 @@ def __new__(cls, *args, **kwargs): return super().__new__(AMPLExternalFunction) @overload - def __init__(self, function=None, gradient=None, hessian=None, *, fgh=None): - ... + def __init__(self, function=None, gradient=None, hessian=None, *, fgh=None): ... @overload - def __init__(self, *, library: str, function: str): - ... + def __init__(self, *, library: str, function: str): ... def __init__(self, *args, **kwargs): """Construct a reference to an external function. @@ -457,9 +455,11 @@ def _pprint(self): ('units', str(self._units)), ( 'arg_units', - [str(u) for u in self._arg_units] - if self._arg_units is not None - else None, + ( + [str(u) for u in self._arg_units] + if self._arg_units is not None + else None + ), ), ], (), @@ -609,9 +609,11 @@ def _pprint(self): ('units', str(self._units)), ( 'arg_units', - [str(u) for u in self._arg_units[:-1]] - if self._arg_units is not None - else None, + ( + [str(u) for u in self._arg_units[:-1]] + if self._arg_units is not None + else None + ), ), ], (), diff --git a/pyomo/core/base/objective.py b/pyomo/core/base/objective.py index 3c625d81c2d..7fb495f3e5b 100644 --- a/pyomo/core/base/objective.py +++ b/pyomo/core/base/objective.py @@ -266,8 +266,7 @@ def __new__(cls, *args, **kwds): @overload def __init__( self, *indexes, expr=None, rule=None, sense=minimize, name=None, doc=None - ): - ... + ): ... def __init__(self, *args, **kwargs): _sense = kwargs.pop('sense', minimize) diff --git a/pyomo/core/base/param.py b/pyomo/core/base/param.py index a6b893ec2c9..ea4290d880d 100644 --- a/pyomo/core/base/param.py +++ b/pyomo/core/base/param.py @@ -301,8 +301,7 @@ def __init__( units=None, name=None, doc=None, - ): - ... + ): ... def __init__(self, *args, **kwd): _init = self._pop_from_kwargs('Param', kwd, ('rule', 'initialize'), NOTSET) diff --git a/pyomo/core/base/piecewise.py b/pyomo/core/base/piecewise.py index 8ab6ce38ca5..0c949f87993 100644 --- a/pyomo/core/base/piecewise.py +++ b/pyomo/core/base/piecewise.py @@ -178,9 +178,11 @@ def _characterize_function(name, tol, f_rule, model, points, *index): # we have a step function step = True slopes = [ - (None) - if (points[i] == points[i - 1]) - else ((values[i] - values[i - 1]) / (points[i] - points[i - 1])) + ( + (None) + if (points[i] == points[i - 1]) + else ((values[i] - values[i - 1]) / (points[i] - points[i - 1])) + ) for i in range(1, len(points)) ] @@ -193,9 +195,9 @@ def _characterize_function(name, tol, f_rule, model, points, *index): # to send this warning through Pyomo if not all( itertools.starmap( - lambda x1, x2: (True) - if ((x1 is None) or (x2 is None)) - else (abs(x1 - x2) > tol), + lambda x1, x2: ( + (True) if ((x1 is None) or (x2 is None)) else (abs(x1 - x2) > tol) + ), zip(slopes, itertools.islice(slopes, 1, None)), ) ): diff --git a/pyomo/core/base/set.py b/pyomo/core/base/set.py index 6dfc3f07427..ba7fdd52446 100644 --- a/pyomo/core/base/set.py +++ b/pyomo/core/base/set.py @@ -2022,8 +2022,7 @@ def __init__( validate=None, name=None, doc=None, - ): - ... + ): ... def __init__(self, *args, **kwds): kwds.setdefault('ctype', Set) @@ -2238,9 +2237,11 @@ def _getitem_when_not_present(self, index): % ( self.name, ("[%s]" % (index,) if self.is_indexed() else ""), - _values - if _values.__class__ is type - else type(_values).__name__, + ( + _values + if _values.__class__ is type + else type(_values).__name__ + ), ) ) raise @@ -2860,8 +2861,7 @@ def __init__( validate=None, name=None, doc=None, - ): - ... + ): ... @overload def __init__( @@ -2877,8 +2877,7 @@ def __init__( validate=None, name=None, doc=None, - ): - ... + ): ... @overload def __init__( @@ -2891,8 +2890,7 @@ def __init__( validate=None, name=None, doc=None, - ): - ... + ): ... def __init__(self, *args, **kwds): # Finite was processed by __new__ diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index 46a87523001..19ffed0e6fd 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -181,8 +181,7 @@ def __init__( rule=None, name=None, doc=None - ): - ... + ): ... def __init__(self, **kwds): # Suffix type information diff --git a/pyomo/core/base/var.py b/pyomo/core/base/var.py index e7e9e4f8f2f..f54cea98a9e 100644 --- a/pyomo/core/base/var.py +++ b/pyomo/core/base/var.py @@ -688,8 +688,7 @@ def __init__( units=None, name=None, doc=None - ): - ... + ): ... def __init__(self, *args, **kwargs): # diff --git a/pyomo/core/expr/numeric_expr.py b/pyomo/core/expr/numeric_expr.py index a638903236f..d0609395f64 100644 --- a/pyomo/core/expr/numeric_expr.py +++ b/pyomo/core/expr/numeric_expr.py @@ -2355,9 +2355,9 @@ def _register_new_iadd_mutablenpvsum_handler(a, b): # Retrieve the appropriate handler, record it in the main # _iadd_mutablenpvsum_dispatcher dict (so this method is not called a second time for # these types) - _iadd_mutablenpvsum_dispatcher[ - b.__class__ - ] = handler = _iadd_mutablenpvsum_type_handler_mapping[types[0]] + _iadd_mutablenpvsum_dispatcher[b.__class__] = handler = ( + _iadd_mutablenpvsum_type_handler_mapping[types[0]] + ) # Call the appropriate handler return handler(a, b) @@ -2454,9 +2454,9 @@ def _register_new_iadd_mutablelinear_handler(a, b): # Retrieve the appropriate handler, record it in the main # _iadd_mutablelinear_dispatcher dict (so this method is not called a second time for # these types) - _iadd_mutablelinear_dispatcher[ - b.__class__ - ] = handler = _iadd_mutablelinear_type_handler_mapping[types[0]] + _iadd_mutablelinear_dispatcher[b.__class__] = handler = ( + _iadd_mutablelinear_type_handler_mapping[types[0]] + ) # Call the appropriate handler return handler(a, b) @@ -2555,9 +2555,9 @@ def _register_new_iadd_mutablesum_handler(a, b): # Retrieve the appropriate handler, record it in the main # _iadd_mutablesum_dispatcher dict (so this method is not called a # second time for these types) - _iadd_mutablesum_dispatcher[ - b.__class__ - ] = handler = _iadd_mutablesum_type_handler_mapping[types[0]] + _iadd_mutablesum_dispatcher[b.__class__] = handler = ( + _iadd_mutablesum_type_handler_mapping[types[0]] + ) # Call the appropriate handler return handler(a, b) diff --git a/pyomo/core/expr/template_expr.py b/pyomo/core/expr/template_expr.py index 21e26038771..fd6294f2289 100644 --- a/pyomo/core/expr/template_expr.py +++ b/pyomo/core/expr/template_expr.py @@ -120,9 +120,11 @@ def _resolve_template(self, args): def _apply_operation(self, result): args = tuple( - arg - if arg.__class__ in native_types or not arg.is_numeric_type() - else value(arg) + ( + arg + if arg.__class__ in native_types or not arg.is_numeric_type() + else value(arg) + ) for arg in result[1:] ) return result[0].__getitem__(tuple(result[1:])) diff --git a/pyomo/core/expr/visitor.py b/pyomo/core/expr/visitor.py index ca3a1c9e745..1d02146b1e5 100644 --- a/pyomo/core/expr/visitor.py +++ b/pyomo/core/expr/visitor.py @@ -681,7 +681,6 @@ def _nonrecursive_walker_loop(self, ptr): class SimpleExpressionVisitor(object): - """ Note: This class is a customization of the PyUtilib :class:`SimpleVisitor diff --git a/pyomo/core/plugins/transform/expand_connectors.py b/pyomo/core/plugins/transform/expand_connectors.py index bf1b517c1b0..8fe14318669 100644 --- a/pyomo/core/plugins/transform/expand_connectors.py +++ b/pyomo/core/plugins/transform/expand_connectors.py @@ -180,9 +180,11 @@ def _validate_and_expand_connector_set(self, connectors): # -3 if v is None else -2 if k in c.aggregators - else -1 - if not hasattr(v, 'is_indexed') or not v.is_indexed() - else len(v) + else ( + -1 + if not hasattr(v, 'is_indexed') or not v.is_indexed() + else len(v) + ) ) ref[k] = (v, _len, c) @@ -220,11 +222,15 @@ def _validate_and_expand_connector_set(self, connectors): _len = ( -3 if _v is None - else -2 - if k in c.aggregators - else -1 - if not hasattr(_v, 'is_indexed') or not _v.is_indexed() - else len(_v) + else ( + -2 + if k in c.aggregators + else ( + -1 + if not hasattr(_v, 'is_indexed') or not _v.is_indexed() + else len(_v) + ) + ) ) if (_len >= 0) ^ (v[1] >= 0): raise ValueError( diff --git a/pyomo/core/plugins/transform/logical_to_linear.py b/pyomo/core/plugins/transform/logical_to_linear.py index e6554e0ed38..f4107b8a32c 100644 --- a/pyomo/core/plugins/transform/logical_to_linear.py +++ b/pyomo/core/plugins/transform/logical_to_linear.py @@ -1,5 +1,6 @@ """Transformation from BooleanVar and LogicalConstraint to Binary and Constraints.""" + from pyomo.common.collections import ComponentMap from pyomo.common.errors import MouseTrap, DeveloperError from pyomo.common.modeling import unique_component_name diff --git a/pyomo/core/plugins/transform/radix_linearization.py b/pyomo/core/plugins/transform/radix_linearization.py index 0d77a342147..b7ff3375a76 100644 --- a/pyomo/core/plugins/transform/radix_linearization.py +++ b/pyomo/core/plugins/transform/radix_linearization.py @@ -237,10 +237,7 @@ def _discretize_bilinear(self, b, v, v_idx, u, u_idx): K = max(b.DISCRETIZATION) _dw = Var( - bounds=( - min(0, _lb * 2**-K, _ub * 2**-K), - max(0, _lb * 2**-K, _ub * 2**-K), - ) + bounds=(min(0, _lb * 2**-K, _ub * 2**-K), max(0, _lb * 2**-K, _ub * 2**-K)) ) b.add_component("dw%s_v%s" % (u_idx, v_idx), _dw) diff --git a/pyomo/core/tests/unit/kernel/test_block.py b/pyomo/core/tests/unit/kernel/test_block.py index 5d1ecc33f06..a22ed4fb4b5 100644 --- a/pyomo/core/tests/unit/kernel/test_block.py +++ b/pyomo/core/tests/unit/kernel/test_block.py @@ -1646,13 +1646,15 @@ def test_components_no_descend_active_True(self): ctype=IBlock, active=True, descend_into=False ) ), - sorted( - str(_b) - for _b in self._components_no_descend[obj][IBlock] - if _b.active - ) - if getattr(obj, 'active', True) - else [], + ( + sorted( + str(_b) + for _b in self._components_no_descend[obj][IBlock] + if _b.active + ) + if getattr(obj, 'active', True) + else [] + ), ) self.assertEqual( set( @@ -1661,13 +1663,15 @@ def test_components_no_descend_active_True(self): ctype=IBlock, active=True, descend_into=False ) ), - set( - id(_b) - for _b in self._components_no_descend[obj][IBlock] - if _b.active - ) - if getattr(obj, 'active', True) - else set(), + ( + set( + id(_b) + for _b in self._components_no_descend[obj][IBlock] + if _b.active + ) + if getattr(obj, 'active', True) + else set() + ), ) # test ctype=IVariable self.assertEqual( @@ -1677,9 +1681,13 @@ def test_components_no_descend_active_True(self): ctype=IVariable, active=True, descend_into=False ) ), - sorted(str(_v) for _v in self._components_no_descend[obj][IVariable]) - if getattr(obj, 'active', True) - else [], + ( + sorted( + str(_v) for _v in self._components_no_descend[obj][IVariable] + ) + if getattr(obj, 'active', True) + else [] + ), ) self.assertEqual( set( @@ -1688,34 +1696,40 @@ def test_components_no_descend_active_True(self): ctype=IVariable, active=True, descend_into=False ) ), - set(id(_v) for _v in self._components_no_descend[obj][IVariable]) - if getattr(obj, 'active', True) - else set(), + ( + set(id(_v) for _v in self._components_no_descend[obj][IVariable]) + if getattr(obj, 'active', True) + else set() + ), ) # test no ctype self.assertEqual( sorted( str(_c) for _c in obj.components(active=True, descend_into=False) ), - sorted( - str(_c) - for ctype in self._components_no_descend[obj] - for _c in self._components_no_descend[obj][ctype] - if getattr(_c, "active", True) - ) - if getattr(obj, 'active', True) - else [], + ( + sorted( + str(_c) + for ctype in self._components_no_descend[obj] + for _c in self._components_no_descend[obj][ctype] + if getattr(_c, "active", True) + ) + if getattr(obj, 'active', True) + else [] + ), ) self.assertEqual( set(id(_c) for _c in obj.components(active=True, descend_into=False)), - set( - id(_c) - for ctype in self._components_no_descend[obj] - for _c in self._components_no_descend[obj][ctype] - if getattr(_c, "active", True) - ) - if getattr(obj, 'active', True) - else set(), + ( + set( + id(_c) + for ctype in self._components_no_descend[obj] + for _c in self._components_no_descend[obj][ctype] + if getattr(_c, "active", True) + ) + if getattr(obj, 'active', True) + else set() + ), ) def test_components_active_None(self): @@ -1794,9 +1808,11 @@ def test_components_active_True(self): ctype=IBlock, active=True, descend_into=True ) ), - sorted(str(_b) for _b in self._components[obj][IBlock] if _b.active) - if getattr(obj, 'active', True) - else [], + ( + sorted(str(_b) for _b in self._components[obj][IBlock] if _b.active) + if getattr(obj, 'active', True) + else [] + ), ) self.assertEqual( set( @@ -1805,9 +1821,11 @@ def test_components_active_True(self): ctype=IBlock, active=True, descend_into=True ) ), - set(id(_b) for _b in self._components[obj][IBlock] if _b.active) - if getattr(obj, 'active', True) - else set(), + ( + set(id(_b) for _b in self._components[obj][IBlock] if _b.active) + if getattr(obj, 'active', True) + else set() + ), ) # test ctype=IVariable self.assertEqual( @@ -1817,13 +1835,15 @@ def test_components_active_True(self): ctype=IVariable, active=True, descend_into=True ) ), - sorted( - str(_v) - for _v in self._components[obj][IVariable] - if _active_path_to_object_exists(obj, _v) - ) - if getattr(obj, 'active', True) - else [], + ( + sorted( + str(_v) + for _v in self._components[obj][IVariable] + if _active_path_to_object_exists(obj, _v) + ) + if getattr(obj, 'active', True) + else [] + ), ) self.assertEqual( set( @@ -1832,38 +1852,44 @@ def test_components_active_True(self): ctype=IVariable, active=True, descend_into=True ) ), - set( - id(_v) - for _v in self._components[obj][IVariable] - if _active_path_to_object_exists(obj, _v) - ) - if getattr(obj, 'active', True) - else set(), + ( + set( + id(_v) + for _v in self._components[obj][IVariable] + if _active_path_to_object_exists(obj, _v) + ) + if getattr(obj, 'active', True) + else set() + ), ) # test no ctype self.assertEqual( sorted( str(_c) for _c in obj.components(active=True, descend_into=True) ), - sorted( - str(_c) - for ctype in self._components[obj] - for _c in self._components[obj][ctype] - if _active_path_to_object_exists(obj, _c) - ) - if getattr(obj, 'active', True) - else [], + ( + sorted( + str(_c) + for ctype in self._components[obj] + for _c in self._components[obj][ctype] + if _active_path_to_object_exists(obj, _c) + ) + if getattr(obj, 'active', True) + else [] + ), ) self.assertEqual( set(id(_c) for _c in obj.components(active=True, descend_into=True)), - set( - id(_c) - for ctype in self._components[obj] - for _c in self._components[obj][ctype] - if _active_path_to_object_exists(obj, _c) - ) - if getattr(obj, 'active', True) - else set(), + ( + set( + id(_c) + for ctype in self._components[obj] + for _c in self._components[obj][ctype] + if _active_path_to_object_exists(obj, _c) + ) + if getattr(obj, 'active', True) + else set() + ), ) diff --git a/pyomo/core/tests/unit/kernel/test_conic.py b/pyomo/core/tests/unit/kernel/test_conic.py index e7416210b8a..352976a2410 100644 --- a/pyomo/core/tests/unit/kernel/test_conic.py +++ b/pyomo/core/tests/unit/kernel/test_conic.py @@ -700,8 +700,7 @@ def test_expression(self): c.x[0].value = 1.2 c.x[1].value = -5.3 val = round( - (1.2**2 + (-5.3) ** 2) ** 0.5 - - ((2.7 / 0.4) ** 0.4) * ((3.7 / 0.6) ** 0.6), + (1.2**2 + (-5.3) ** 2) ** 0.5 - ((2.7 / 0.4) ** 0.4) * ((3.7 / 0.6) ** 0.6), 9, ) self.assertEqual(round(c(), 9), val) diff --git a/pyomo/core/tests/unit/test_units.py b/pyomo/core/tests/unit/test_units.py index 8ec83fe1a73..809db733cde 100644 --- a/pyomo/core/tests/unit/test_units.py +++ b/pyomo/core/tests/unit/test_units.py @@ -920,8 +920,7 @@ def test_module_example(self): model = ConcreteModel() model.acc = Var() model.obj = Objective( - expr=(model.acc * units.m / units.s**2 - 9.81 * units.m / units.s**2) - ** 2 + expr=(model.acc * units.m / units.s**2 - 9.81 * units.m / units.s**2) ** 2 ) self.assertEqual('m**2/s**4', str(units.get_units(model.obj.expr))) diff --git a/pyomo/dae/set_utils.py b/pyomo/dae/set_utils.py index 96a7489261e..981954189b3 100644 --- a/pyomo/dae/set_utils.py +++ b/pyomo/dae/set_utils.py @@ -164,8 +164,8 @@ def get_indices_of_projection(index_set, *sets): info['set_except'] = [None] # index_getter returns an index corresponding to the values passed to # it, re-ordered according to order of indexing sets in component. - info['index_getter'] = ( - lambda incomplete_index, *newvals: newvals[0] + info['index_getter'] = lambda incomplete_index, *newvals: ( + newvals[0] if len(newvals) <= 1 else tuple([newvals[location[i]] for i in location]) ) diff --git a/pyomo/dataportal/tests/test_dataportal.py b/pyomo/dataportal/tests/test_dataportal.py index db9423abff6..3171a118118 100644 --- a/pyomo/dataportal/tests/test_dataportal.py +++ b/pyomo/dataportal/tests/test_dataportal.py @@ -772,22 +772,22 @@ def test_data_namespace(self): self.assertEqual( sorted( md.values(), - key=lambda x: tuple(sorted(x) + [0]) - if type(x) is list - else tuple(sorted(x.values())) - if not type(x) is int - else (x,), + key=lambda x: ( + tuple(sorted(x) + [0]) + if type(x) is list + else tuple(sorted(x.values())) if not type(x) is int else (x,) + ), ), [-4, -3, -2, -1, [1, 3, 5], {1: 10, 3: 30, 5: 50}], ) self.assertEqual( sorted( md.values('ns1'), - key=lambda x: tuple(sorted(x) + [0]) - if type(x) is list - else tuple(sorted(x.values())) - if not type(x) is int - else (x,), + key=lambda x: ( + tuple(sorted(x) + [0]) + if type(x) is list + else tuple(sorted(x.values())) if not type(x) is int else (x,) + ), ), [1, [7, 9, 11], {7: 70, 9: 90, 11: 110}], ) diff --git a/pyomo/duality/plugins.py b/pyomo/duality/plugins.py index 9a8e10b4cfc..c8c84153975 100644 --- a/pyomo/duality/plugins.py +++ b/pyomo/duality/plugins.py @@ -87,16 +87,9 @@ def _dualize(self, block, unfixed=[]): # # Collect linear terms from the block # - ( - A, - b_coef, - c_rhs, - c_sense, - d_sense, - vnames, - cnames, - v_domain, - ) = collect_linear_terms(block, unfixed) + (A, b_coef, c_rhs, c_sense, d_sense, vnames, cnames, v_domain) = ( + collect_linear_terms(block, unfixed) + ) ##print(A) ##print(vnames) ##print(cnames) diff --git a/pyomo/gdp/plugins/bigm.py b/pyomo/gdp/plugins/bigm.py index b960b5087ea..e554d5593ab 100644 --- a/pyomo/gdp/plugins/bigm.py +++ b/pyomo/gdp/plugins/bigm.py @@ -409,10 +409,9 @@ def _update_M_from_suffixes(self, constraint, suffix_list, lower, upper): ) def get_m_value_src(self, constraint): transBlock = _get_constraint_transBlock(constraint) - ( - (lower_val, lower_source, lower_key), - (upper_val, upper_source, upper_key), - ) = transBlock.bigm_src[constraint] + ((lower_val, lower_source, lower_key), (upper_val, upper_source, upper_key)) = ( + transBlock.bigm_src[constraint] + ) if ( constraint.lower is not None diff --git a/pyomo/gdp/plugins/bound_pretransformation.py b/pyomo/gdp/plugins/bound_pretransformation.py index 0d6b14a4b80..56a39115f34 100644 --- a/pyomo/gdp/plugins/bound_pretransformation.py +++ b/pyomo/gdp/plugins/bound_pretransformation.py @@ -244,9 +244,9 @@ def _create_transformation_constraints( disjunction, transformation_blocks ) if self.transformation_name not in disjunction._transformation_map: - disjunction._transformation_map[ - self.transformation_name - ] = ComponentMap() + disjunction._transformation_map[self.transformation_name] = ( + ComponentMap() + ) trans_map = disjunction._transformation_map[self.transformation_name] for disj in disjunction.disjuncts: diff --git a/pyomo/gdp/plugins/cuttingplane.py b/pyomo/gdp/plugins/cuttingplane.py index 49d984a0712..fcb0e8886f1 100644 --- a/pyomo/gdp/plugins/cuttingplane.py +++ b/pyomo/gdp/plugins/cuttingplane.py @@ -808,13 +808,9 @@ def _apply_to(self, instance, bigM=None, **kwds): else: self.verbose = False - ( - instance_rBigM, - cuts_obj, - instance_rHull, - var_info, - transBlockName, - ) = self._setup_subproblems(instance, bigM, self._config.tighten_relaxation) + (instance_rBigM, cuts_obj, instance_rHull, var_info, transBlockName) = ( + self._setup_subproblems(instance, bigM, self._config.tighten_relaxation) + ) self._generate_cuttingplanes( instance_rBigM, cuts_obj, instance_rHull, var_info, transBlockName diff --git a/pyomo/gdp/plugins/hull.py b/pyomo/gdp/plugins/hull.py index b8e2b3e3699..a600ef76bc7 100644 --- a/pyomo/gdp/plugins/hull.py +++ b/pyomo/gdp/plugins/hull.py @@ -470,9 +470,9 @@ def _transform_disjunctionData( and disj not in disjunctsVarAppearsIn[var] ): relaxationBlock = disj._transformation_block().parent_block() - relaxationBlock._bigMConstraintMap[ - disaggregated_var - ] = Reference(disaggregated_var_bounds[idx, :]) + relaxationBlock._bigMConstraintMap[disaggregated_var] = ( + Reference(disaggregated_var_bounds[idx, :]) + ) relaxationBlock._disaggregatedVarMap['srcVar'][ disaggregated_var ] = var diff --git a/pyomo/gdp/plugins/multiple_bigm.py b/pyomo/gdp/plugins/multiple_bigm.py index 18f159c7ca2..85fb1e4aa6b 100644 --- a/pyomo/gdp/plugins/multiple_bigm.py +++ b/pyomo/gdp/plugins/multiple_bigm.py @@ -310,10 +310,10 @@ def _transform_disjunctionData(self, obj, index, parent_disjunct, root_disjunct) Ms = arg_Ms if not self._config.only_mbigm_bound_constraints: - Ms = ( - transBlock.calculated_missing_m_values - ) = self._calculate_missing_M_values( - active_disjuncts, arg_Ms, transBlock, transformed_constraints + Ms = transBlock.calculated_missing_m_values = ( + self._calculate_missing_M_values( + active_disjuncts, arg_Ms, transBlock, transformed_constraints + ) ) # Now we can deactivate the constraints we deferred, so that we don't diff --git a/pyomo/gdp/tests/test_partition_disjuncts.py b/pyomo/gdp/tests/test_partition_disjuncts.py index 56faaa9b8f5..b050bc5e653 100644 --- a/pyomo/gdp/tests/test_partition_disjuncts.py +++ b/pyomo/gdp/tests/test_partition_disjuncts.py @@ -227,14 +227,18 @@ def check_transformation_block( aux22ub, partitions, ): - ( - b, - disj1, - disj2, - aux_vars1, - aux_vars2, - ) = self.check_transformation_block_structure( - m, aux11lb, aux11ub, aux12lb, aux12ub, aux21lb, aux21ub, aux22lb, aux22ub + (b, disj1, disj2, aux_vars1, aux_vars2) = ( + self.check_transformation_block_structure( + m, + aux11lb, + aux11ub, + aux12lb, + aux12ub, + aux21lb, + aux21ub, + aux22lb, + aux22ub, + ) ) self.check_disjunct_constraints(disj1, disj2, aux_vars1, aux_vars2) @@ -351,14 +355,12 @@ def check_transformation_block_nested_disjunction( else: block_prefix = disjunction_block + "." disjunction_parent = m.component(disjunction_block) - ( - inner_b, - inner_disj1, - inner_disj2, - ) = self.check_transformation_block_disjuncts_and_constraints( - disj2, - disjunction_parent.disj2.disjunction, - "%sdisj2.disjunction" % block_prefix, + (inner_b, inner_disj1, inner_disj2) = ( + self.check_transformation_block_disjuncts_and_constraints( + disj2, + disjunction_parent.disj2.disjunction, + "%sdisj2.disjunction" % block_prefix, + ) ) # Has it's own indicator var, the aux vars, and the Reference to the @@ -753,13 +755,9 @@ def test_assume_fixed_vars_permanent(self): # This actually changes the structure of the model because fixed vars # move to the constants. I think this is fair, and we should allow it # because it will allow for a tighter relaxation. - ( - b, - disj1, - disj2, - aux_vars1, - aux_vars2, - ) = self.check_transformation_block_structure(m, 0, 36, 0, 72, -9, 16, -18, 32) + (b, disj1, disj2, aux_vars1, aux_vars2) = ( + self.check_transformation_block_structure(m, 0, 36, 0, 72, -9, 16, -18, 32) + ) # check disjunct constraints self.check_disjunct_constraints(disj1, disj2, aux_vars1, aux_vars2) @@ -1702,9 +1700,7 @@ def test_transformation_block_fbbt_bounds(self): compute_bounds_method=compute_fbbt_bounds, ) - self.check_transformation_block( - m, 0, (2 * 6**4) ** 0.25, 0, (2 * 5**4) ** 0.25 - ) + self.check_transformation_block(m, 0, (2 * 6**4) ** 0.25, 0, (2 * 5**4) ** 0.25) def test_invalid_partition_error(self): m = models.makeNonQuadraticNonlinearGDP() diff --git a/pyomo/neos/plugins/kestrel_plugin.py b/pyomo/neos/plugins/kestrel_plugin.py index 72d73d15ace..49fb3809622 100644 --- a/pyomo/neos/plugins/kestrel_plugin.py +++ b/pyomo/neos/plugins/kestrel_plugin.py @@ -193,13 +193,9 @@ def _perform_wait_any(self): del self._ah[jobNumber] ah.status = ActionStatus.done - ( - opt, - smap_id, - load_solutions, - select_index, - default_variable_value, - ) = self._opt_data[jobNumber] + (opt, smap_id, load_solutions, select_index, default_variable_value) = ( + self._opt_data[jobNumber] + ) del self._opt_data[jobNumber] args = self._args[jobNumber] @@ -262,11 +258,10 @@ def _perform_wait_any(self): # minutes. If NEOS doesn't produce intermediate results # by then we will need to catch (and eat) the exception try: - ( - message_fragment, - new_offset, - ) = self.kestrel.neos.getIntermediateResults( - jobNumber, self._ah[jobNumber].password, current_offset + (message_fragment, new_offset) = ( + self.kestrel.neos.getIntermediateResults( + jobNumber, self._ah[jobNumber].password, current_offset + ) ) logger.info(message_fragment) self._neos_log[jobNumber] = ( diff --git a/pyomo/opt/base/solvers.py b/pyomo/opt/base/solvers.py index 0de60902af2..b11e6393b02 100644 --- a/pyomo/opt/base/solvers.py +++ b/pyomo/opt/base/solvers.py @@ -641,9 +641,9 @@ def solve(self, *args, **kwds): result.solution(0).symbol_map = getattr( _model, "._symbol_maps" )[result._smap_id] - result.solution( - 0 - ).default_variable_value = self._default_variable_value + result.solution(0).default_variable_value = ( + self._default_variable_value + ) if self._load_solutions: _model.load_solution(result.solution(0)) else: @@ -699,12 +699,10 @@ def _presolve(self, *args, **kwds): if self._problem_format: write_start_time = time.time() - ( - self._problem_files, - self._problem_format, - self._smap_id, - ) = self._convert_problem( - args, self._problem_format, self._valid_problem_formats, **kwds + (self._problem_files, self._problem_format, self._smap_id) = ( + self._convert_problem( + args, self._problem_format, self._valid_problem_formats, **kwds + ) ) total_time = time.time() - write_start_time if self._report_timing: diff --git a/pyomo/opt/plugins/sol.py b/pyomo/opt/plugins/sol.py index 6e1ca666633..297b1c87d06 100644 --- a/pyomo/opt/plugins/sol.py +++ b/pyomo/opt/plugins/sol.py @@ -241,9 +241,9 @@ def _load(self, fin, res, soln, suffixes): translated_suffix_name = ( suffix_name[0].upper() + suffix_name[1:] ) - soln_constraint[key][ - translated_suffix_name - ] = convert_function(suf_line[1]) + soln_constraint[key][translated_suffix_name] = ( + convert_function(suf_line[1]) + ) elif kind == 2: # Obj for cnt in range(nvalues): suf_line = fin.readline().split() diff --git a/pyomo/repn/plugins/ampl/ampl_.py b/pyomo/repn/plugins/ampl/ampl_.py index a2bd55cb73a..d1a11bf2f38 100644 --- a/pyomo/repn/plugins/ampl/ampl_.py +++ b/pyomo/repn/plugins/ampl/ampl_.py @@ -1964,9 +1964,9 @@ def _print_model_NL( for obj_ID, (obj, wrapped_repn) in Objectives_dict.items(): grad_entries = {} for idx, obj_var in enumerate(wrapped_repn.linear_vars): - grad_entries[ - self_ampl_var_id[obj_var] - ] = wrapped_repn.repn.linear_coefs[idx] + grad_entries[self_ampl_var_id[obj_var]] = ( + wrapped_repn.repn.linear_coefs[idx] + ) for obj_var in wrapped_repn.nonlinear_vars: if obj_var not in wrapped_repn.linear_vars: grad_entries[self_ampl_var_id[obj_var]] = 0 diff --git a/pyomo/repn/plugins/baron_writer.py b/pyomo/repn/plugins/baron_writer.py index 4242ae7431c..0d684fcd1d2 100644 --- a/pyomo/repn/plugins/baron_writer.py +++ b/pyomo/repn/plugins/baron_writer.py @@ -176,12 +176,14 @@ def _monomial_to_string(self, node): def _linear_to_string(self, node): values = [ - self._monomial_to_string(arg) - if ( - arg.__class__ is EXPR.MonomialTermExpression - and not arg.arg(1).is_fixed() + ( + self._monomial_to_string(arg) + if ( + arg.__class__ is EXPR.MonomialTermExpression + and not arg.arg(1).is_fixed() + ) + else ftoa(value(arg)) ) - else ftoa(value(arg)) for arg in node.args ] return node._to_string(values, False, self.smap) @@ -644,19 +646,18 @@ def _write_bar_file(self, model, output_file, solver_capability, io_options): # variables. # equation_section_stream = StringIO() - ( - referenced_variable_ids, - branching_priorities_suffixes, - ) = self._write_equations_section( - model, - equation_section_stream, - all_blocks_list, - active_components_data_var, - symbol_map, - c_labeler, - output_fixed_variable_bounds, - skip_trivial_constraints, - sorter, + (referenced_variable_ids, branching_priorities_suffixes) = ( + self._write_equations_section( + model, + equation_section_stream, + all_blocks_list, + active_components_data_var, + symbol_map, + c_labeler, + output_fixed_variable_bounds, + skip_trivial_constraints, + sorter, + ) ) # diff --git a/pyomo/repn/plugins/gams_writer.py b/pyomo/repn/plugins/gams_writer.py index de0e4684fc4..719839fc8dd 100644 --- a/pyomo/repn/plugins/gams_writer.py +++ b/pyomo/repn/plugins/gams_writer.py @@ -180,9 +180,11 @@ def _monomial_to_string(self, node): def _linear_to_string(self, node): values = [ - self._monomial_to_string(arg) - if arg.__class__ is EXPR.MonomialTermExpression - else ftoa(arg, True) + ( + self._monomial_to_string(arg) + if arg.__class__ is EXPR.MonomialTermExpression + else ftoa(arg, True) + ) for arg in node.args ] return node._to_string(values, False, self.smap) diff --git a/pyomo/repn/plugins/nl_writer.py b/pyomo/repn/plugins/nl_writer.py index 1081b69acff..2d5eae151b0 100644 --- a/pyomo/repn/plugins/nl_writer.py +++ b/pyomo/repn/plugins/nl_writer.py @@ -1451,9 +1451,11 @@ def write(self, model): ostream.write( 'r%s\n' % ( - "\t#%d ranges (rhs's)" % len(constraints) - if symbolic_solver_labels - else '', + ( + "\t#%d ranges (rhs's)" % len(constraints) + if symbolic_solver_labels + else '' + ), ) ) ostream.write("\n".join(r_lines)) @@ -1466,9 +1468,11 @@ def write(self, model): ostream.write( 'b%s\n' % ( - "\t#%d bounds (on variables)" % len(variables) - if symbolic_solver_labels - else '', + ( + "\t#%d bounds (on variables)" % len(variables) + if symbolic_solver_labels + else '' + ), ) ) for var_idx, _id in enumerate(variables): @@ -1492,9 +1496,11 @@ def write(self, model): 'k%d%s\n' % ( len(variables) - 1, - "\t#intermediate Jacobian column lengths" - if symbolic_solver_labels - else '', + ( + "\t#intermediate Jacobian column lengths" + if symbolic_solver_labels + else '' + ), ) ) ktot = 0 diff --git a/pyomo/repn/tests/ampl/test_nlv2.py b/pyomo/repn/tests/ampl/test_nlv2.py index 2e366039185..6422a2b0020 100644 --- a/pyomo/repn/tests/ampl/test_nlv2.py +++ b/pyomo/repn/tests/ampl/test_nlv2.py @@ -1123,9 +1123,7 @@ def test_linear_constraint_npv_const(self): m.x = Var([1, 2]) m.p = Param(initialize=5, mutable=True) m.o = Objective(expr=1) - m.c = Constraint( - expr=LinearExpression([m.p**2, 5 * m.x[1], 10 * m.x[2]]) <= 0 - ) + m.c = Constraint(expr=LinearExpression([m.p**2, 5 * m.x[1], 10 * m.x[2]]) <= 0) OUT = io.StringIO() nl_writer.NLWriter().write(m, OUT) diff --git a/pyomo/solvers/plugins/solvers/GAMS.py b/pyomo/solvers/plugins/solvers/GAMS.py index 16a126b9af4..d0365d49078 100644 --- a/pyomo/solvers/plugins/solvers/GAMS.py +++ b/pyomo/solvers/plugins/solvers/GAMS.py @@ -605,9 +605,9 @@ def solve(self, *args, **kwds): results.solution(0).symbol_map = getattr(model, "._symbol_maps")[ results._smap_id ] - results.solution( - 0 - ).default_variable_value = self._default_variable_value + results.solution(0).default_variable_value = ( + self._default_variable_value + ) if load_solutions: model.load_solution(results.solution(0)) else: @@ -1187,9 +1187,9 @@ def solve(self, *args, **kwds): results.solution(0).symbol_map = getattr(model, "._symbol_maps")[ results._smap_id ] - results.solution( - 0 - ).default_variable_value = self._default_variable_value + results.solution(0).default_variable_value = ( + self._default_variable_value + ) if load_solutions: model.load_solution(results.solution(0)) else: diff --git a/pyomo/solvers/plugins/solvers/GUROBI.py b/pyomo/solvers/plugins/solvers/GUROBI.py index 3c7c227b9e7..e0eddf008af 100644 --- a/pyomo/solvers/plugins/solvers/GUROBI.py +++ b/pyomo/solvers/plugins/solvers/GUROBI.py @@ -480,9 +480,9 @@ def process_soln_file(self, results): name = tokens[1] if name != "c_e_ONE_VAR_CONSTANT": if name.startswith('c_'): - soln_constraints.setdefault(tokens[1], {})[ - "Dual" - ] = float(tokens[2]) + soln_constraints.setdefault(tokens[1], {})["Dual"] = ( + float(tokens[2]) + ) elif name.startswith('r_l_'): range_duals.setdefault(name[4:], [0, 0])[0] = float( tokens[2] @@ -495,9 +495,9 @@ def process_soln_file(self, results): name = tokens[1] if name != "c_e_ONE_VAR_CONSTANT": if name.startswith('c_'): - soln_constraints.setdefault(tokens[1], {})[ - "Slack" - ] = float(tokens[2]) + soln_constraints.setdefault(tokens[1], {})["Slack"] = ( + float(tokens[2]) + ) elif name.startswith('r_l_'): range_slacks.setdefault(name[4:], [0, 0])[0] = float( tokens[2] diff --git a/pyomo/solvers/plugins/solvers/direct_solver.py b/pyomo/solvers/plugins/solvers/direct_solver.py index a99eec79fd9..4f90a753fe6 100644 --- a/pyomo/solvers/plugins/solvers/direct_solver.py +++ b/pyomo/solvers/plugins/solvers/direct_solver.py @@ -178,9 +178,9 @@ def solve(self, *args, **kwds): result.solution(0).symbol_map = getattr( _model, "._symbol_maps" )[result._smap_id] - result.solution( - 0 - ).default_variable_value = self._default_variable_value + result.solution(0).default_variable_value = ( + self._default_variable_value + ) if self._load_solutions: _model.load_solution(result.solution(0)) else: diff --git a/pyomo/solvers/plugins/solvers/mosek_direct.py b/pyomo/solvers/plugins/solvers/mosek_direct.py index 6a21e0fcb9b..4c0718bfe74 100644 --- a/pyomo/solvers/plugins/solvers/mosek_direct.py +++ b/pyomo/solvers/plugins/solvers/mosek_direct.py @@ -305,10 +305,12 @@ def _get_expr_from_pyomo_repn(self, repn, max_degree=2): referenced_vars.update(q_vars) qsubi, qsubj = zip( *[ - (i, j) - if self._pyomo_var_to_solver_var_map[i] - >= self._pyomo_var_to_solver_var_map[j] - else (j, i) + ( + (i, j) + if self._pyomo_var_to_solver_var_map[i] + >= self._pyomo_var_to_solver_var_map[j] + else (j, i) + ) for i, j in repn.quadratic_vars ] ) @@ -465,15 +467,19 @@ def _add_constraints(self, con_seq): q_is, q_js, q_vals = zip(*qexp) l_ids, l_coefs, constants = zip(*arow) lbs = tuple( - -inf - if value(lq_all[i].lower) is None - else value(lq_all[i].lower) - constants[i] + ( + -inf + if value(lq_all[i].lower) is None + else value(lq_all[i].lower) - constants[i] + ) for i in range(num_lq) ) ubs = tuple( - inf - if value(lq_all[i].upper) is None - else value(lq_all[i].upper) - constants[i] + ( + inf + if value(lq_all[i].upper) is None + else value(lq_all[i].upper) - constants[i] + ) for i in range(num_lq) ) fxs = tuple(c.equality for c in lq_all) diff --git a/pyomo/solvers/plugins/solvers/mosek_persistent.py b/pyomo/solvers/plugins/solvers/mosek_persistent.py index 4e2aa97b379..6eaad564781 100644 --- a/pyomo/solvers/plugins/solvers/mosek_persistent.py +++ b/pyomo/solvers/plugins/solvers/mosek_persistent.py @@ -213,19 +213,19 @@ def update_vars(self, *solver_vars): var_ids.append(self._pyomo_var_to_solver_var_map[v]) vtypes = tuple(map(self._mosek_vartype_from_var, solver_vars)) lbs = tuple( - value(v) - if v.fixed - else -float('inf') - if value(v.lb) is None - else value(v.lb) + ( + value(v) + if v.fixed + else -float('inf') if value(v.lb) is None else value(v.lb) + ) for v in solver_vars ) ubs = tuple( - value(v) - if v.fixed - else float('inf') - if value(v.ub) is None - else value(v.ub) + ( + value(v) + if v.fixed + else float('inf') if value(v.ub) is None else value(v.ub) + ) for v in solver_vars ) fxs = tuple(v.is_fixed() for v in solver_vars) diff --git a/pyomo/solvers/plugins/solvers/persistent_solver.py b/pyomo/solvers/plugins/solvers/persistent_solver.py index 34df4e4b454..141621d0a31 100644 --- a/pyomo/solvers/plugins/solvers/persistent_solver.py +++ b/pyomo/solvers/plugins/solvers/persistent_solver.py @@ -547,9 +547,9 @@ def solve(self, *args, **kwds): result.solution(0).symbol_map = getattr( _model, "._symbol_maps" )[result._smap_id] - result.solution( - 0 - ).default_variable_value = self._default_variable_value + result.solution(0).default_variable_value = ( + self._default_variable_value + ) if self._load_solutions: _model.load_solution(result.solution(0)) else: diff --git a/pyomo/solvers/tests/checks/test_GAMS.py b/pyomo/solvers/tests/checks/test_GAMS.py index c4913672694..7aa952a6c69 100644 --- a/pyomo/solvers/tests/checks/test_GAMS.py +++ b/pyomo/solvers/tests/checks/test_GAMS.py @@ -212,12 +212,12 @@ def test_fixed_var_sign_gms(self): def test_long_var_py(self): with SolverFactory("gams", solver_io="python") as opt: m = ConcreteModel() - x = ( - m.a23456789012345678901234567890123456789012345678901234567890123 - ) = Var() - y = ( - m.b234567890123456789012345678901234567890123456789012345678901234 - ) = Var() + x = m.a23456789012345678901234567890123456789012345678901234567890123 = ( + Var() + ) + y = m.b234567890123456789012345678901234567890123456789012345678901234 = ( + Var() + ) z = ( m.c23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 ) = Var() @@ -236,12 +236,12 @@ def test_long_var_py(self): def test_long_var_gms(self): with SolverFactory("gams", solver_io="gms") as opt: m = ConcreteModel() - x = ( - m.a23456789012345678901234567890123456789012345678901234567890123 - ) = Var() - y = ( - m.b234567890123456789012345678901234567890123456789012345678901234 - ) = Var() + x = m.a23456789012345678901234567890123456789012345678901234567890123 = ( + Var() + ) + y = m.b234567890123456789012345678901234567890123456789012345678901234 = ( + Var() + ) z = ( m.c23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 ) = Var() diff --git a/pyomo/solvers/tests/checks/test_cplex.py b/pyomo/solvers/tests/checks/test_cplex.py index 4f1d7aca99b..44b82d2ad77 100644 --- a/pyomo/solvers/tests/checks/test_cplex.py +++ b/pyomo/solvers/tests/checks/test_cplex.py @@ -129,16 +129,14 @@ def get_mock_model(self): def get_mock_cplex_shell(self, mock_model): solver = MockCPLEX() - ( - solver._problem_files, - solver._problem_format, - solver._smap_id, - ) = convert_problem( - (mock_model,), - ProblemFormat.cpxlp, - [ProblemFormat.cpxlp], - has_capability=lambda x: True, - symbolic_solver_labels=True, + (solver._problem_files, solver._problem_format, solver._smap_id) = ( + convert_problem( + (mock_model,), + ProblemFormat.cpxlp, + [ProblemFormat.cpxlp], + has_capability=lambda x: True, + symbolic_solver_labels=True, + ) ) return solver From 916881c12f0145de0df2e613195eb14918e2a478 Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Fri, 26 Jan 2024 09:29:43 -0700 Subject: [PATCH 36/43] Update ignore-revs --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 6d3e6401c5b..8863634b6a2 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -43,4 +43,5 @@ ed13c8c65d6c3f56973887744be1c62a5d4756de 0d93f98aa608f892df404ad8015885d26e09bb55 63a3c602a00a2b747fc308c0571bbe33e55a3731 363a16a609f519b3edfdfcf40c66d6de7ac135af +d024718991455519e09149ede53a38df1c067abe From 017e21ee50d98d8b2f2083e6880f030025ed5378 Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Fri, 26 Jan 2024 09:36:28 -0700 Subject: [PATCH 37/43] Apply new black to docs/examples --- doc/OnlineDocs/src/scripting/spy4Constraints.py | 1 + doc/OnlineDocs/src/scripting/spy4Expressions.py | 1 + doc/OnlineDocs/src/scripting/spy4PyomoCommand.py | 1 + doc/OnlineDocs/src/scripting/spy4Variables.py | 1 + examples/gdp/constrained_layout/cons_layout_model.py | 1 + examples/gdp/eight_process/eight_proc_logical.py | 1 + examples/gdp/eight_process/eight_proc_model.py | 1 + examples/gdp/eight_process/eight_proc_verbose_model.py | 1 + examples/gdp/nine_process/small_process.py | 1 + examples/gdp/small_lit/basic_step.py | 1 + examples/gdp/small_lit/ex_633_trespalacios.py | 1 + examples/gdp/small_lit/nonconvex_HEN.py | 1 - examples/gdp/strip_packing/strip_packing_concrete.py | 1 + examples/gdp/two_rxn_lee/two_rxn_model.py | 1 + examples/kernel/mosek/power1.py | 4 +--- examples/pyomobook/nonlinear-ch/react_design/ReactorDesign.py | 4 +--- 16 files changed, 15 insertions(+), 7 deletions(-) diff --git a/doc/OnlineDocs/src/scripting/spy4Constraints.py b/doc/OnlineDocs/src/scripting/spy4Constraints.py index ac42b4d38b3..f0033bbc33e 100644 --- a/doc/OnlineDocs/src/scripting/spy4Constraints.py +++ b/doc/OnlineDocs/src/scripting/spy4Constraints.py @@ -2,6 +2,7 @@ David L. Woodruff and Mingye Yang, Spring 2018 Code snippets for Constraints.rst in testable form """ + from pyomo.environ import * model = ConcreteModel() diff --git a/doc/OnlineDocs/src/scripting/spy4Expressions.py b/doc/OnlineDocs/src/scripting/spy4Expressions.py index d4a5cad321a..0e8a50c78b3 100644 --- a/doc/OnlineDocs/src/scripting/spy4Expressions.py +++ b/doc/OnlineDocs/src/scripting/spy4Expressions.py @@ -2,6 +2,7 @@ David L. Woodruff and Mingye Yang, Spring 2018 Code snippets for Expressions.rst in testable form """ + from pyomo.environ import * model = ConcreteModel() diff --git a/doc/OnlineDocs/src/scripting/spy4PyomoCommand.py b/doc/OnlineDocs/src/scripting/spy4PyomoCommand.py index c03ee1e5039..f655b812076 100644 --- a/doc/OnlineDocs/src/scripting/spy4PyomoCommand.py +++ b/doc/OnlineDocs/src/scripting/spy4PyomoCommand.py @@ -2,6 +2,7 @@ David L. Woodruff and Mingye Yang, Spring 2018 Code snippets for PyomoCommand.rst in testable form """ + from pyomo.environ import * model = ConcreteModel() diff --git a/doc/OnlineDocs/src/scripting/spy4Variables.py b/doc/OnlineDocs/src/scripting/spy4Variables.py index 802226247c5..c4e2ff612f1 100644 --- a/doc/OnlineDocs/src/scripting/spy4Variables.py +++ b/doc/OnlineDocs/src/scripting/spy4Variables.py @@ -2,6 +2,7 @@ David L. Woodruff and Mingye Yang, Spring 2018 Code snippets for Variables.rst in testable form """ + from pyomo.environ import * model = ConcreteModel() diff --git a/examples/gdp/constrained_layout/cons_layout_model.py b/examples/gdp/constrained_layout/cons_layout_model.py index 10595db4c22..c10c6f6be81 100644 --- a/examples/gdp/constrained_layout/cons_layout_model.py +++ b/examples/gdp/constrained_layout/cons_layout_model.py @@ -9,6 +9,7 @@ with each other. """ + from __future__ import division from pyomo.environ import ConcreteModel, Objective, Param, RangeSet, Set, Var, value diff --git a/examples/gdp/eight_process/eight_proc_logical.py b/examples/gdp/eight_process/eight_proc_logical.py index 7e183dfc397..aaa71f0b2c9 100644 --- a/examples/gdp/eight_process/eight_proc_logical.py +++ b/examples/gdp/eight_process/eight_proc_logical.py @@ -22,6 +22,7 @@ http://dx.doi.org/10.1016/0098-1354(95)00219-7 """ + from __future__ import division from pyomo.core.expr.logical_expr import land, lor diff --git a/examples/gdp/eight_process/eight_proc_model.py b/examples/gdp/eight_process/eight_proc_model.py index d4bd4dbd102..4ab4eb780db 100644 --- a/examples/gdp/eight_process/eight_proc_model.py +++ b/examples/gdp/eight_process/eight_proc_model.py @@ -22,6 +22,7 @@ http://dx.doi.org/10.1016/0098-1354(95)00219-7 """ + from __future__ import division from pyomo.environ import ( diff --git a/examples/gdp/eight_process/eight_proc_verbose_model.py b/examples/gdp/eight_process/eight_proc_verbose_model.py index 78da347e564..4c4886afe10 100644 --- a/examples/gdp/eight_process/eight_proc_verbose_model.py +++ b/examples/gdp/eight_process/eight_proc_verbose_model.py @@ -4,6 +4,7 @@ eight_proc_model.py. """ + from __future__ import division from pyomo.environ import ( diff --git a/examples/gdp/nine_process/small_process.py b/examples/gdp/nine_process/small_process.py index 7f96f32c65c..2758069f316 100644 --- a/examples/gdp/nine_process/small_process.py +++ b/examples/gdp/nine_process/small_process.py @@ -1,6 +1,7 @@ """Small process synthesis-inspired toy GDP example. """ + from pyomo.core import ConcreteModel, RangeSet, Var, Constraint, Objective from pyomo.core.expr.current import exp, log, sqrt from pyomo.gdp import Disjunction diff --git a/examples/gdp/small_lit/basic_step.py b/examples/gdp/small_lit/basic_step.py index 16d134500e7..48ef52d9ba0 100644 --- a/examples/gdp/small_lit/basic_step.py +++ b/examples/gdp/small_lit/basic_step.py @@ -9,6 +9,7 @@ Pyomo model implementation by @RomeoV """ + from pyomo.environ import * from pyomo.gdp import * from pyomo.gdp.basic_step import apply_basic_step diff --git a/examples/gdp/small_lit/ex_633_trespalacios.py b/examples/gdp/small_lit/ex_633_trespalacios.py index b281e009d1f..ce9ae55a85c 100644 --- a/examples/gdp/small_lit/ex_633_trespalacios.py +++ b/examples/gdp/small_lit/ex_633_trespalacios.py @@ -14,6 +14,7 @@ Pyomo model implementation by @bernalde and @qtothec. """ + from __future__ import division from pyomo.environ import * diff --git a/examples/gdp/small_lit/nonconvex_HEN.py b/examples/gdp/small_lit/nonconvex_HEN.py index 61c24c3187a..99e2c4f15e2 100644 --- a/examples/gdp/small_lit/nonconvex_HEN.py +++ b/examples/gdp/small_lit/nonconvex_HEN.py @@ -7,7 +7,6 @@ Pyomo model implementation by @RomeoV """ - from pyomo.environ import ( ConcreteModel, Constraint, diff --git a/examples/gdp/strip_packing/strip_packing_concrete.py b/examples/gdp/strip_packing/strip_packing_concrete.py index 4fa6172a8d1..9e11b702366 100644 --- a/examples/gdp/strip_packing/strip_packing_concrete.py +++ b/examples/gdp/strip_packing/strip_packing_concrete.py @@ -9,6 +9,7 @@ cutting fabric. """ + from __future__ import division from pyomo.environ import ConcreteModel, NonNegativeReals, Objective, Param, Set, Var diff --git a/examples/gdp/two_rxn_lee/two_rxn_model.py b/examples/gdp/two_rxn_lee/two_rxn_model.py index 9057ef8c006..7e43dc4e744 100644 --- a/examples/gdp/two_rxn_lee/two_rxn_model.py +++ b/examples/gdp/two_rxn_lee/two_rxn_model.py @@ -1,4 +1,5 @@ """Two reactor model from literature. See README.md.""" + from __future__ import division from pyomo.core import ConcreteModel, Constraint, Objective, Param, Var, maximize diff --git a/examples/kernel/mosek/power1.py b/examples/kernel/mosek/power1.py index 7274b587dae..d7a12c1ce54 100644 --- a/examples/kernel/mosek/power1.py +++ b/examples/kernel/mosek/power1.py @@ -12,9 +12,7 @@ def solve_nonlinear(): m.c = pmo.constraint(body=m.x + m.y + 0.5 * m.z, rhs=2) - m.o = pmo.objective( - (m.x**0.2) * (m.y**0.8) + (m.z**0.4) - m.x, sense=pmo.maximize - ) + m.o = pmo.objective((m.x**0.2) * (m.y**0.8) + (m.z**0.4) - m.x, sense=pmo.maximize) m.x.value, m.y.value, m.z.value = (1, 1, 1) ipopt = pmo.SolverFactory("ipopt") diff --git a/examples/pyomobook/nonlinear-ch/react_design/ReactorDesign.py b/examples/pyomobook/nonlinear-ch/react_design/ReactorDesign.py index 814b4a5938e..90822c153a5 100644 --- a/examples/pyomobook/nonlinear-ch/react_design/ReactorDesign.py +++ b/examples/pyomobook/nonlinear-ch/react_design/ReactorDesign.py @@ -33,9 +33,7 @@ def create_model(k1, k2, k3, caf): model.cc_bal = pyo.Constraint(expr=(0 == -model.sv * model.cc + k2 * model.cb)) - model.cd_bal = pyo.Constraint( - expr=(0 == -model.sv * model.cd + k3 * model.ca**2.0) - ) + model.cd_bal = pyo.Constraint(expr=(0 == -model.sv * model.cd + k3 * model.ca**2.0)) return model From aa401ac19a9d47c3762895e305bf31bb3e297070 Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Fri, 26 Jan 2024 09:37:10 -0700 Subject: [PATCH 38/43] Ignore black rev --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 8863634b6a2..36b9898397f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -44,4 +44,5 @@ ed13c8c65d6c3f56973887744be1c62a5d4756de 63a3c602a00a2b747fc308c0571bbe33e55a3731 363a16a609f519b3edfdfcf40c66d6de7ac135af d024718991455519e09149ede53a38df1c067abe +017e21ee50d98d8b2f2083e6880f030025ed5378 From 6ec5561c71120677dbc90b69aed8c88875a25bfa Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Fri, 26 Jan 2024 10:18:40 -0700 Subject: [PATCH 39/43] Remove __future__ imports for Python 2->3 --- doc/OnlineDocs/src/scripting/abstract2.py | 2 +- doc/OnlineDocs/src/scripting/concrete1.py | 1 - doc/OnlineDocs/src/scripting/driveabs2.py | 2 +- doc/OnlineDocs/src/scripting/driveconc1.py | 2 +- examples/dae/stochpdegas_automatic.py | 2 +- .../gdp/constrained_layout/cons_layout_model.py | 2 -- examples/gdp/eight_process/eight_proc_logical.py | 2 -- examples/gdp/eight_process/eight_proc_model.py | 2 -- .../eight_process/eight_proc_verbose_model.py | 2 -- examples/gdp/small_lit/ex_633_trespalacios.py | 2 -- .../gdp/strip_packing/strip_packing_8rect.py | 2 -- .../gdp/strip_packing/strip_packing_concrete.py | 2 -- examples/gdp/two_rxn_lee/two_rxn_model.py | 2 -- .../performance/dae/stochpdegas1_automatic.py | 2 +- .../pyomo-components-ch/var_declaration.py | 2 +- .../community_detection/tests/test_detection.py | 1 - pyomo/contrib/mcpp/pyomo_mcpp.py | 2 +- pyomo/contrib/mcpp/test_mcpp.py | 2 +- pyomo/contrib/multistart/high_conf_stop.py | 2 -- pyomo/contrib/multistart/multi.py | 2 -- pyomo/contrib/multistart/reinit.py | 2 -- .../preprocessing/plugins/bounds_to_vars.py | 1 - .../preprocessing/plugins/induced_linearity.py | 1 - pyomo/contrib/preprocessing/plugins/init_vars.py | 2 +- .../preprocessing/plugins/int_to_binary.py | 2 -- .../preprocessing/plugins/remove_zero_terms.py | 2 +- .../preprocessing/plugins/var_aggregator.py | 1 - .../react_example/maximize_cb_outputs.py | 2 +- .../react_example/reactor_model_outputs.py | 1 - .../react_example/reactor_model_residuals.py | 1 - .../pynumero/sparse/tests/test_block_vector.py | 2 +- .../examples/HIV_Transmission.py | 2 +- .../sensitivity_toolbox/examples/parameter.py | 2 +- pyomo/core/base/piecewise.py | 14 +------------- pyomo/core/expr/logical_expr.py | 1 - pyomo/core/expr/numeric_expr.py | 8 +------- pyomo/core/expr/numvalue.py | 16 ---------------- pyomo/core/expr/visitor.py | 1 - .../tests/unit/test_logical_expr_expanded.py | 2 +- pyomo/dae/tests/test_colloc.py | 2 +- pyomo/dae/tests/test_finite_diff.py | 2 +- pyomo/dae/tests/test_simulator.py | 1 - pyomo/gdp/plugins/cuttingplane.py | 2 +- pyomo/gdp/plugins/partition_disjuncts.py | 2 +- pyomo/repn/standard_aux.py | 1 - pyomo/repn/standard_repn.py | 1 - 46 files changed, 21 insertions(+), 91 deletions(-) diff --git a/doc/OnlineDocs/src/scripting/abstract2.py b/doc/OnlineDocs/src/scripting/abstract2.py index 7eb444914db..1e14d1d1898 100644 --- a/doc/OnlineDocs/src/scripting/abstract2.py +++ b/doc/OnlineDocs/src/scripting/abstract2.py @@ -1,6 +1,6 @@ # abstract2.py -from __future__ import division + from pyomo.environ import * model = AbstractModel() diff --git a/doc/OnlineDocs/src/scripting/concrete1.py b/doc/OnlineDocs/src/scripting/concrete1.py index 1c1f1517e17..2cd1a1f722c 100644 --- a/doc/OnlineDocs/src/scripting/concrete1.py +++ b/doc/OnlineDocs/src/scripting/concrete1.py @@ -1,4 +1,3 @@ -from __future__ import division from pyomo.environ import * model = ConcreteModel() diff --git a/doc/OnlineDocs/src/scripting/driveabs2.py b/doc/OnlineDocs/src/scripting/driveabs2.py index 67ab7468864..45862195a57 100644 --- a/doc/OnlineDocs/src/scripting/driveabs2.py +++ b/doc/OnlineDocs/src/scripting/driveabs2.py @@ -1,5 +1,5 @@ # driveabs2.py -from __future__ import division + import pyomo.environ as pyo from pyomo.opt import SolverFactory diff --git a/doc/OnlineDocs/src/scripting/driveconc1.py b/doc/OnlineDocs/src/scripting/driveconc1.py index ca5d6fc1593..95b0f42806d 100644 --- a/doc/OnlineDocs/src/scripting/driveconc1.py +++ b/doc/OnlineDocs/src/scripting/driveconc1.py @@ -1,5 +1,5 @@ # driveconc1.py -from __future__ import division + import pyomo.environ as pyo from pyomo.opt import SolverFactory diff --git a/examples/dae/stochpdegas_automatic.py b/examples/dae/stochpdegas_automatic.py index 3cd5c34f011..fdde099a396 100644 --- a/examples/dae/stochpdegas_automatic.py +++ b/examples/dae/stochpdegas_automatic.py @@ -1,7 +1,7 @@ # stochastic pde model for natural gas network # victor m. zavala / 2013 -# from __future__ import division +# from pyomo.environ import * from pyomo.dae import * diff --git a/examples/gdp/constrained_layout/cons_layout_model.py b/examples/gdp/constrained_layout/cons_layout_model.py index c10c6f6be81..245aa2df58e 100644 --- a/examples/gdp/constrained_layout/cons_layout_model.py +++ b/examples/gdp/constrained_layout/cons_layout_model.py @@ -10,8 +10,6 @@ """ -from __future__ import division - from pyomo.environ import ConcreteModel, Objective, Param, RangeSet, Set, Var, value # Constrained layout model examples. These are from Nicolas Sawaya (2006). diff --git a/examples/gdp/eight_process/eight_proc_logical.py b/examples/gdp/eight_process/eight_proc_logical.py index aaa71f0b2c9..60f7acee876 100644 --- a/examples/gdp/eight_process/eight_proc_logical.py +++ b/examples/gdp/eight_process/eight_proc_logical.py @@ -23,8 +23,6 @@ """ -from __future__ import division - from pyomo.core.expr.logical_expr import land, lor from pyomo.core.plugins.transform.logical_to_linear import ( update_boolean_vars_from_binary, diff --git a/examples/gdp/eight_process/eight_proc_model.py b/examples/gdp/eight_process/eight_proc_model.py index 4ab4eb780db..840b6911d83 100644 --- a/examples/gdp/eight_process/eight_proc_model.py +++ b/examples/gdp/eight_process/eight_proc_model.py @@ -23,8 +23,6 @@ """ -from __future__ import division - from pyomo.environ import ( ConcreteModel, Constraint, diff --git a/examples/gdp/eight_process/eight_proc_verbose_model.py b/examples/gdp/eight_process/eight_proc_verbose_model.py index 4c4886afe10..cae584d4127 100644 --- a/examples/gdp/eight_process/eight_proc_verbose_model.py +++ b/examples/gdp/eight_process/eight_proc_verbose_model.py @@ -5,8 +5,6 @@ """ -from __future__ import division - from pyomo.environ import ( ConcreteModel, Constraint, diff --git a/examples/gdp/small_lit/ex_633_trespalacios.py b/examples/gdp/small_lit/ex_633_trespalacios.py index ce9ae55a85c..61b7294e3ba 100644 --- a/examples/gdp/small_lit/ex_633_trespalacios.py +++ b/examples/gdp/small_lit/ex_633_trespalacios.py @@ -15,8 +15,6 @@ """ -from __future__ import division - from pyomo.environ import * from pyomo.gdp import * diff --git a/examples/gdp/strip_packing/strip_packing_8rect.py b/examples/gdp/strip_packing/strip_packing_8rect.py index eba3c82dc05..e1350dbc39e 100644 --- a/examples/gdp/strip_packing/strip_packing_8rect.py +++ b/examples/gdp/strip_packing/strip_packing_8rect.py @@ -11,8 +11,6 @@ """ -from __future__ import division - from pyomo.environ import ( ConcreteModel, NonNegativeReals, diff --git a/examples/gdp/strip_packing/strip_packing_concrete.py b/examples/gdp/strip_packing/strip_packing_concrete.py index 9e11b702366..1313d75561c 100644 --- a/examples/gdp/strip_packing/strip_packing_concrete.py +++ b/examples/gdp/strip_packing/strip_packing_concrete.py @@ -10,8 +10,6 @@ """ -from __future__ import division - from pyomo.environ import ConcreteModel, NonNegativeReals, Objective, Param, Set, Var diff --git a/examples/gdp/two_rxn_lee/two_rxn_model.py b/examples/gdp/two_rxn_lee/two_rxn_model.py index 7e43dc4e744..2e5f1734130 100644 --- a/examples/gdp/two_rxn_lee/two_rxn_model.py +++ b/examples/gdp/two_rxn_lee/two_rxn_model.py @@ -1,7 +1,5 @@ """Two reactor model from literature. See README.md.""" -from __future__ import division - from pyomo.core import ConcreteModel, Constraint, Objective, Param, Var, maximize # from pyomo.environ import * # NOQA diff --git a/examples/performance/dae/stochpdegas1_automatic.py b/examples/performance/dae/stochpdegas1_automatic.py index cd0153eee61..905ec9a5330 100644 --- a/examples/performance/dae/stochpdegas1_automatic.py +++ b/examples/performance/dae/stochpdegas1_automatic.py @@ -1,7 +1,7 @@ # stochastic pde model for natural gas network # victor m. zavala / 2013 -# from __future__ import division +# from pyomo.environ import * from pyomo.dae import * diff --git a/examples/pyomobook/pyomo-components-ch/var_declaration.py b/examples/pyomobook/pyomo-components-ch/var_declaration.py index 538cbea1842..a122decd2ae 100644 --- a/examples/pyomobook/pyomo-components-ch/var_declaration.py +++ b/examples/pyomobook/pyomo-components-ch/var_declaration.py @@ -1,4 +1,4 @@ -from __future__ import print_function + import pyomo.environ as pyo model = pyo.ConcreteModel() diff --git a/pyomo/contrib/community_detection/tests/test_detection.py b/pyomo/contrib/community_detection/tests/test_detection.py index 724388f9ab6..acfd441005f 100644 --- a/pyomo/contrib/community_detection/tests/test_detection.py +++ b/pyomo/contrib/community_detection/tests/test_detection.py @@ -12,7 +12,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import division import logging diff --git a/pyomo/contrib/mcpp/pyomo_mcpp.py b/pyomo/contrib/mcpp/pyomo_mcpp.py index 817b18bff7c..25a4237ff16 100644 --- a/pyomo/contrib/mcpp/pyomo_mcpp.py +++ b/pyomo/contrib/mcpp/pyomo_mcpp.py @@ -11,7 +11,7 @@ # Note: the self.mcpp.* functions are all C-style functions implemented # in the compiled MC++ wrapper library # Note: argument to pow must be an integer -from __future__ import division + import ctypes import logging diff --git a/pyomo/contrib/mcpp/test_mcpp.py b/pyomo/contrib/mcpp/test_mcpp.py index 9d8c670d470..23b963e11bf 100644 --- a/pyomo/contrib/mcpp/test_mcpp.py +++ b/pyomo/contrib/mcpp/test_mcpp.py @@ -8,7 +8,7 @@ # rights in this software. # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import division + import logging from math import pi diff --git a/pyomo/contrib/multistart/high_conf_stop.py b/pyomo/contrib/multistart/high_conf_stop.py index f85daf633de..96b350557ae 100644 --- a/pyomo/contrib/multistart/high_conf_stop.py +++ b/pyomo/contrib/multistart/high_conf_stop.py @@ -6,8 +6,6 @@ """ -from __future__ import division - from collections import Counter from math import log, sqrt diff --git a/pyomo/contrib/multistart/multi.py b/pyomo/contrib/multistart/multi.py index a0e424d2c95..867d47d4951 100644 --- a/pyomo/contrib/multistart/multi.py +++ b/pyomo/contrib/multistart/multi.py @@ -10,8 +10,6 @@ # ___________________________________________________________________________ -from __future__ import division - import logging from pyomo.common.config import ( diff --git a/pyomo/contrib/multistart/reinit.py b/pyomo/contrib/multistart/reinit.py index 3904a7e343f..de10fe3ba8b 100644 --- a/pyomo/contrib/multistart/reinit.py +++ b/pyomo/contrib/multistart/reinit.py @@ -1,7 +1,5 @@ """Helper functions for variable reinitialization.""" -from __future__ import division - import logging import random diff --git a/pyomo/contrib/preprocessing/plugins/bounds_to_vars.py b/pyomo/contrib/preprocessing/plugins/bounds_to_vars.py index ece2376774c..33eaa731816 100644 --- a/pyomo/contrib/preprocessing/plugins/bounds_to_vars.py +++ b/pyomo/contrib/preprocessing/plugins/bounds_to_vars.py @@ -11,7 +11,6 @@ """Transformation to convert explicit bounds to variable bounds.""" -from __future__ import division from math import fabs import math diff --git a/pyomo/contrib/preprocessing/plugins/induced_linearity.py b/pyomo/contrib/preprocessing/plugins/induced_linearity.py index 88c062fdee2..6378c94e44e 100644 --- a/pyomo/contrib/preprocessing/plugins/induced_linearity.py +++ b/pyomo/contrib/preprocessing/plugins/induced_linearity.py @@ -17,7 +17,6 @@ """ -from __future__ import division import logging import textwrap diff --git a/pyomo/contrib/preprocessing/plugins/init_vars.py b/pyomo/contrib/preprocessing/plugins/init_vars.py index 2b37e13e4cd..7469722cf23 100644 --- a/pyomo/contrib/preprocessing/plugins/init_vars.py +++ b/pyomo/contrib/preprocessing/plugins/init_vars.py @@ -10,7 +10,7 @@ # ___________________________________________________________________________ """Automatically initialize variables.""" -from __future__ import division + from pyomo.core.base.var import Var from pyomo.core.base.transformation import TransformationFactory diff --git a/pyomo/contrib/preprocessing/plugins/int_to_binary.py b/pyomo/contrib/preprocessing/plugins/int_to_binary.py index 55b9d26948f..6ed6c3a9cfa 100644 --- a/pyomo/contrib/preprocessing/plugins/int_to_binary.py +++ b/pyomo/contrib/preprocessing/plugins/int_to_binary.py @@ -1,7 +1,5 @@ """Transformation to reformulate integer variables into binary.""" -from __future__ import division - from math import floor, log import logging diff --git a/pyomo/contrib/preprocessing/plugins/remove_zero_terms.py b/pyomo/contrib/preprocessing/plugins/remove_zero_terms.py index 7cce719f98d..256c94d4b7a 100644 --- a/pyomo/contrib/preprocessing/plugins/remove_zero_terms.py +++ b/pyomo/contrib/preprocessing/plugins/remove_zero_terms.py @@ -11,7 +11,7 @@ # -*- coding: UTF-8 -*- """Transformation to remove zero terms from constraints.""" -from __future__ import division + from pyomo.core import quicksum from pyomo.core.base.constraint import Constraint diff --git a/pyomo/contrib/preprocessing/plugins/var_aggregator.py b/pyomo/contrib/preprocessing/plugins/var_aggregator.py index 0a429cb5a67..651c0ecf7e0 100644 --- a/pyomo/contrib/preprocessing/plugins/var_aggregator.py +++ b/pyomo/contrib/preprocessing/plugins/var_aggregator.py @@ -11,7 +11,6 @@ """Transformation to aggregate equal variables.""" -from __future__ import division from pyomo.common.collections import ComponentMap, ComponentSet from pyomo.core.base import Block, Constraint, VarList, Objective, TransformationFactory diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/react_example/maximize_cb_outputs.py b/pyomo/contrib/pynumero/examples/external_grey_box/react_example/maximize_cb_outputs.py index eff4f34cabc..9f683b146fe 100644 --- a/pyomo/contrib/pynumero/examples/external_grey_box/react_example/maximize_cb_outputs.py +++ b/pyomo/contrib/pynumero/examples/external_grey_box/react_example/maximize_cb_outputs.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import division + import pyomo.environ as pyo from pyomo.contrib.pynumero.interfaces.external_grey_box import ExternalGreyBoxBlock from pyomo.contrib.pynumero.examples.external_grey_box.react_example.reactor_model_outputs import ( diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/react_example/reactor_model_outputs.py b/pyomo/contrib/pynumero/examples/external_grey_box/react_example/reactor_model_outputs.py index 7570a20b066..6e6c997880b 100644 --- a/pyomo/contrib/pynumero/examples/external_grey_box/react_example/reactor_model_outputs.py +++ b/pyomo/contrib/pynumero/examples/external_grey_box/react_example/reactor_model_outputs.py @@ -21,7 +21,6 @@ box model interface. """ -from __future__ import division import numpy as np from scipy.optimize import fsolve diff --git a/pyomo/contrib/pynumero/examples/external_grey_box/react_example/reactor_model_residuals.py b/pyomo/contrib/pynumero/examples/external_grey_box/react_example/reactor_model_residuals.py index 6a6ae9bb652..69a79425750 100644 --- a/pyomo/contrib/pynumero/examples/external_grey_box/react_example/reactor_model_residuals.py +++ b/pyomo/contrib/pynumero/examples/external_grey_box/react_example/reactor_model_residuals.py @@ -19,7 +19,6 @@ box model interface. """ -from __future__ import division import pyomo.environ as pyo import numpy as np diff --git a/pyomo/contrib/pynumero/sparse/tests/test_block_vector.py b/pyomo/contrib/pynumero/sparse/tests/test_block_vector.py index 2d1bc7b640d..780a8bc2609 100644 --- a/pyomo/contrib/pynumero/sparse/tests/test_block_vector.py +++ b/pyomo/contrib/pynumero/sparse/tests/test_block_vector.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import division + import pyomo.common.unittest as unittest from pyomo.contrib.pynumero.dependencies import ( diff --git a/pyomo/contrib/sensitivity_toolbox/examples/HIV_Transmission.py b/pyomo/contrib/sensitivity_toolbox/examples/HIV_Transmission.py index 146baedd8aa..2c8996c95ca 100755 --- a/pyomo/contrib/sensitivity_toolbox/examples/HIV_Transmission.py +++ b/pyomo/contrib/sensitivity_toolbox/examples/HIV_Transmission.py @@ -14,7 +14,7 @@ # and D.K. Owen 1998, Interfaces # -from __future__ import division + from pyomo.environ import ( ConcreteModel, Param, diff --git a/pyomo/contrib/sensitivity_toolbox/examples/parameter.py b/pyomo/contrib/sensitivity_toolbox/examples/parameter.py index 93c6124701b..3ed1628f2c2 100644 --- a/pyomo/contrib/sensitivity_toolbox/examples/parameter.py +++ b/pyomo/contrib/sensitivity_toolbox/examples/parameter.py @@ -13,7 +13,7 @@ # # Original implementation by Hans Pirany is in pyomo/examples/pyomo/suffixes # -from __future__ import print_function + from pyomo.environ import ( ConcreteModel, Param, diff --git a/pyomo/core/base/piecewise.py b/pyomo/core/base/piecewise.py index 0c949f87993..ef2fb9eefae 100644 --- a/pyomo/core/base/piecewise.py +++ b/pyomo/core/base/piecewise.py @@ -32,10 +32,6 @@ *) piecewise for functions of the form y = f(x1,x2,...) """ -# ****** NOTE: Nothing in this file relies on integer division ******* -# I predict this will save numerous headaches as -# well as gratuitous calls to float() in this code -from __future__ import division __all__ = ['Piecewise'] @@ -151,8 +147,6 @@ def _characterize_function(name, tol, f_rule, model, points, *index): # expression generation errors in the checks below points = [value(_p) for _p in points] - # we use future division to protect against the case where - # the user supplies integer type points for return values if isinstance(f_rule, types.FunctionType): values = [f_rule(model, *flatten_tuple((index, x))) for x in points] elif f_rule.__class__ is dict: @@ -272,7 +266,6 @@ def __call__(self, x): yU = self._range_pts[i + 1] if xL == xU: # a step function return yU - # using future division return yL + ((yU - yL) / (xU - xL)) * (x - xL) raise ValueError( "The point %s is outside the list of domain " @@ -299,7 +292,6 @@ def construct(self, pblock, x_var, y_var): # create a single linear constraint LHS = y_var F_AT_XO = y_pts[0] - # using future division dF_AT_XO = (y_pts[1] - y_pts[0]) / (x_pts[1] - x_pts[0]) X_MINUS_XO = x_var - x_pts[0] if bound_type == Bound.Upper: @@ -739,7 +731,7 @@ def construct(self, pblock, x_var, y_var): # create indexers polytopes = range(1, len_x_pts) - # create constants (using future division) + # create constants SLOPE = { p: (y_pts[p] - y_pts[p - 1]) / (x_pts[p] - x_pts[p - 1]) for p in polytopes } @@ -908,7 +900,6 @@ def con1_rule(model, i): rhs *= 0.0 else: rhs *= OPT_M['UB'][i] * (1 - bigm_y[i]) - # using future division return ( y_var - y_pts[i - 1] @@ -922,7 +913,6 @@ def con1_rule(model, i): rhs *= 0.0 else: rhs *= OPT_M['LB'][i] * (1 - bigm_y[i]) - # using future division return ( y_var - y_pts[i - 1] @@ -944,7 +934,6 @@ def conAFF_rule(model, i): rhs *= 0.0 else: rhs *= OPT_M['LB'][i] * (1 - bigm_y[i]) - # using future division return ( y_var - y_pts[i - 1] @@ -974,7 +963,6 @@ def conAFF_rule(model, i): pblock.bigm_domain_constraint_upper = Constraint(expr=x_var <= x_pts[-1]) def _M_func(self, a, Fa, b, Fb, c, Fc): - # using future division return Fa - Fb - ((a - b) * ((Fc - Fb) / (c - b))) def _find_M(self, x_pts, y_pts, bound_type): diff --git a/pyomo/core/expr/logical_expr.py b/pyomo/core/expr/logical_expr.py index e5a2f411a6e..f2d3e110166 100644 --- a/pyomo/core/expr/logical_expr.py +++ b/pyomo/core/expr/logical_expr.py @@ -10,7 +10,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import division import types from itertools import islice diff --git a/pyomo/core/expr/numeric_expr.py b/pyomo/core/expr/numeric_expr.py index d0609395f64..0a300474790 100644 --- a/pyomo/core/expr/numeric_expr.py +++ b/pyomo/core/expr/numeric_expr.py @@ -17,14 +17,8 @@ logger = logging.getLogger('pyomo.core') -from math import isclose - from pyomo.common.dependencies import attempt_import -from pyomo.common.deprecation import ( - deprecated, - deprecation_warning, - relocated_module_attribute, -) +from pyomo.common.deprecation import deprecated, relocated_module_attribute from pyomo.common.errors import PyomoException, DeveloperError from pyomo.common.formatting import tostr from pyomo.common.numeric_types import ( diff --git a/pyomo/core/expr/numvalue.py b/pyomo/core/expr/numvalue.py index 305391daffa..d54b9621e70 100644 --- a/pyomo/core/expr/numvalue.py +++ b/pyomo/core/expr/numvalue.py @@ -264,15 +264,6 @@ def polynomial_degree(obj): # constants get repeated many times. KnownConstants lets us re-use / # share constants we have seen before. # -# Note: -# For now, all constants are coerced to floats. This avoids integer -# division in Python 2.x. (At least some of the time.) -# -# When we eliminate support for Python 2.x, we will not need this -# coercion. The main difference in the following code is that we will -# need to index KnownConstants by both the class type and value, since -# INT, FLOAT and LONG values sometimes hash the same. -# _KnownConstants = {} @@ -299,13 +290,6 @@ def as_numeric(obj): if val is not None: return val # - # Coerce the value to a float, if possible - # - try: - obj = float(obj) - except: - pass - # # Create the numeric constant. This really # should be the only place in the code # where these objects are constructed. diff --git a/pyomo/core/expr/visitor.py b/pyomo/core/expr/visitor.py index 1d02146b1e5..f1cd3b7bde6 100644 --- a/pyomo/core/expr/visitor.py +++ b/pyomo/core/expr/visitor.py @@ -9,7 +9,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import division import inspect import logging diff --git a/pyomo/core/tests/unit/test_logical_expr_expanded.py b/pyomo/core/tests/unit/test_logical_expr_expanded.py index f5b86d59cbd..95ae0494a48 100644 --- a/pyomo/core/tests/unit/test_logical_expr_expanded.py +++ b/pyomo/core/tests/unit/test_logical_expr_expanded.py @@ -13,7 +13,7 @@ """ Testing for the logical expression system """ -from __future__ import division + import operator from itertools import product diff --git a/pyomo/dae/tests/test_colloc.py b/pyomo/dae/tests/test_colloc.py index dda928110ae..0786903f12e 100644 --- a/pyomo/dae/tests/test_colloc.py +++ b/pyomo/dae/tests/test_colloc.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import print_function + import pyomo.common.unittest as unittest from pyomo.environ import Var, Set, ConcreteModel, TransformationFactory, pyomo diff --git a/pyomo/dae/tests/test_finite_diff.py b/pyomo/dae/tests/test_finite_diff.py index 9ae7ecdea91..adca8bf6a15 100644 --- a/pyomo/dae/tests/test_finite_diff.py +++ b/pyomo/dae/tests/test_finite_diff.py @@ -9,7 +9,7 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import print_function + import pyomo.common.unittest as unittest from pyomo.environ import Var, Set, ConcreteModel, TransformationFactory diff --git a/pyomo/dae/tests/test_simulator.py b/pyomo/dae/tests/test_simulator.py index b3003bb5a0d..e79bc7b23b6 100644 --- a/pyomo/dae/tests/test_simulator.py +++ b/pyomo/dae/tests/test_simulator.py @@ -9,7 +9,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import print_function import json import pyomo.common.unittest as unittest diff --git a/pyomo/gdp/plugins/cuttingplane.py b/pyomo/gdp/plugins/cuttingplane.py index fcb0e8886f1..7a6a927a316 100644 --- a/pyomo/gdp/plugins/cuttingplane.py +++ b/pyomo/gdp/plugins/cuttingplane.py @@ -15,7 +15,7 @@ Implements a general cutting plane-based reformulation for linear and convex GDPs. """ -from __future__ import division + from pyomo.common.config import ( ConfigBlock, diff --git a/pyomo/gdp/plugins/partition_disjuncts.py b/pyomo/gdp/plugins/partition_disjuncts.py index 57cfe1852c3..fbe25ed3ae1 100644 --- a/pyomo/gdp/plugins/partition_disjuncts.py +++ b/pyomo/gdp/plugins/partition_disjuncts.py @@ -15,7 +15,7 @@ J. Kronqvist, R. Misener, and C. Tsay, "Between Steps: Intermediate Relaxations between big-M and Convex Hull Reformulations," 2021. """ -from __future__ import division + from pyomo.common.config import ( ConfigBlock, diff --git a/pyomo/repn/standard_aux.py b/pyomo/repn/standard_aux.py index 7995949fc05..8704253eca3 100644 --- a/pyomo/repn/standard_aux.py +++ b/pyomo/repn/standard_aux.py @@ -9,7 +9,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import division __all__ = ['compute_standard_repn'] diff --git a/pyomo/repn/standard_repn.py b/pyomo/repn/standard_repn.py index 95fa824b14a..53618d3eb50 100644 --- a/pyomo/repn/standard_repn.py +++ b/pyomo/repn/standard_repn.py @@ -9,7 +9,6 @@ # This software is distributed under the 3-clause BSD License. # ___________________________________________________________________________ -from __future__ import division __all__ = ['StandardRepn', 'generate_standard_repn'] From da8e3b6e92dc78ab7687bcedb6e20efe0794435d Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Fri, 26 Jan 2024 10:21:22 -0700 Subject: [PATCH 40/43] Black whoops --- examples/pyomobook/pyomo-components-ch/var_declaration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/pyomobook/pyomo-components-ch/var_declaration.py b/examples/pyomobook/pyomo-components-ch/var_declaration.py index a122decd2ae..60d3b00756a 100644 --- a/examples/pyomobook/pyomo-components-ch/var_declaration.py +++ b/examples/pyomobook/pyomo-components-ch/var_declaration.py @@ -1,4 +1,3 @@ - import pyomo.environ as pyo model = pyo.ConcreteModel() From 2e98405297b8eec41be40c53b71cfa54f1fe549d Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Fri, 26 Jan 2024 10:38:33 -0700 Subject: [PATCH 41/43] Add back value coercion --- pyomo/core/expr/numvalue.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pyomo/core/expr/numvalue.py b/pyomo/core/expr/numvalue.py index d54b9621e70..1ef0f7f3044 100644 --- a/pyomo/core/expr/numvalue.py +++ b/pyomo/core/expr/numvalue.py @@ -290,6 +290,13 @@ def as_numeric(obj): if val is not None: return val # + # Coerce the value to a float, if possible + # + try: + obj = float(obj) + except: + pass + # # Create the numeric constant. This really # should be the only place in the code # where these objects are constructed. From 3260e1394906bdc23c7e08aba25c747d967e7f12 Mon Sep 17 00:00:00 2001 From: Miranda Mundt Date: Fri, 26 Jan 2024 13:02:09 -0700 Subject: [PATCH 42/43] Readding note - but should be addressed soon because it's not accurate --- pyomo/core/expr/numvalue.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyomo/core/expr/numvalue.py b/pyomo/core/expr/numvalue.py index 1ef0f7f3044..6c605b080a3 100644 --- a/pyomo/core/expr/numvalue.py +++ b/pyomo/core/expr/numvalue.py @@ -259,6 +259,14 @@ def polynomial_degree(obj): ) +# Note: +# For now, all constants are coerced to floats. This avoids integer +# division in Python 2.x. (At least some of the time.) +# +# When we eliminate support for Python 2.x, we will not need this +# coercion. The main difference in the following code is that we will +# need to index KnownConstants by both the class type and value, since +# INT, FLOAT and LONG values sometimes hash the same. # # It is very common to have only a few constants in a model, but those # constants get repeated many times. KnownConstants lets us re-use / From 0ce77fb64b870a7637c144646f49450296a63cfe Mon Sep 17 00:00:00 2001 From: Bethany Nicholson Date: Fri, 26 Jan 2024 15:12:50 -0700 Subject: [PATCH 43/43] Fixing kernel online doc test and black formatting --- doc/OnlineDocs/src/kernel/examples.txt | 2 +- pyomo/core/base/suffix.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/OnlineDocs/src/kernel/examples.txt b/doc/OnlineDocs/src/kernel/examples.txt index 306eff0e929..e85c64efd86 100644 --- a/doc/OnlineDocs/src/kernel/examples.txt +++ b/doc/OnlineDocs/src/kernel/examples.txt @@ -154,7 +154,7 @@ 5 Declarations: SOS2_y_index SOS2_y SOS2_constraint_index SOS2_constraint SOS2_sosconstraint 1 Suffix Declarations - dual : Direction=Suffix.IMPORT, Datatype=Suffix.FLOAT + dual : Direction=IMPORT, Datatype=FLOAT Key : Value 27 Declarations: b s q p pd v vd vl_index vl c cd_index cd cl_index cl e ed o od ol_index ol sos1 sos2 sd_index sd dual f pw diff --git a/pyomo/core/base/suffix.py b/pyomo/core/base/suffix.py index d8a5feb6009..160ae20f116 100644 --- a/pyomo/core/base/suffix.py +++ b/pyomo/core/base/suffix.py @@ -189,7 +189,7 @@ def __init__( initialize=None, rule=None, name=None, - doc=None + doc=None, ): ... def __init__(self, **kwargs):