Skip to content

Commit 4489372

Browse files
authored
Merge branch 'main' into feature/3381_quadratic_obj_highs
2 parents 3fb1476 + 874352c commit 4489372

File tree

17 files changed

+165
-86
lines changed

17 files changed

+165
-86
lines changed

.github/workflows/test_branches.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,9 +534,8 @@ jobs:
534534
echo "DYLD_LIBRARY_PATH=${env:DYLD_LIBRARY_PATH}:$GAMS_DIR" `
535535
Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
536536
$INSTALLER = "${env:DOWNLOAD_DIR}/gams_install.exe"
537-
# We are pinning to 29.1.0 because a license is required for
538-
# versions after this in order to run in demo mode.
539-
$URL = "https://d37drm4t2jghv5.cloudfront.net/distributions/29.1.0"
537+
# Demo licenses are included for 5mo from the newest release
538+
$URL = "https://d37drm4t2jghv5.cloudfront.net/distributions/50.1.0"
540539
if ( "${{matrix.TARGET}}" -eq "win" ) {
541540
$URL = "$URL/windows/windows_x64_64.exe"
542541
} elseif ( "${{matrix.TARGET}}" -eq "osx" ) {

.github/workflows/test_pr_and_main.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -586,9 +586,8 @@ jobs:
586586
echo "DYLD_LIBRARY_PATH=${env:DYLD_LIBRARY_PATH}:$GAMS_DIR" `
587587
Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
588588
$INSTALLER = "${env:DOWNLOAD_DIR}/gams_install.exe"
589-
# We are pinning to 29.1.0 because a license is required for
590-
# versions after this in order to run in demo mode.
591-
$URL = "https://d37drm4t2jghv5.cloudfront.net/distributions/29.1.0"
589+
# Demo licenses are included for 5mo from the newest release
590+
$URL = "https://d37drm4t2jghv5.cloudfront.net/distributions/50.1.0"
592591
if ( "${{matrix.TARGET}}" -eq "win" ) {
593592
$URL = "$URL/windows/windows_x64_64.exe"
594593
} elseif ( "${{matrix.TARGET}}" -eq "osx" ) {

pyomo/common/dependencies.py

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -443,25 +443,9 @@ def check_min_version(module, min_version):
443443
module = indicator._module
444444
else:
445445
return False
446-
if check_min_version._parser is None:
447-
try:
448-
from packaging import version as _version
449-
450-
_parser = _version.parse
451-
except ImportError:
452-
# pkg_resources is an order of magnitude slower to import than
453-
# packaging. Only use it if the preferred (but optional)
454-
# packaging library is not present
455-
from pkg_resources import parse_version as _parser
456-
check_min_version._parser = _parser
457-
else:
458-
_parser = check_min_version._parser
459446

460447
version = getattr(module, '__version__', '0.0.0')
461-
return _parser(min_version) <= _parser(version)
462-
463-
464-
check_min_version._parser = None
448+
return packaging.version.parse(min_version) <= packaging.version.parse(version)
465449

466450

467451
#
@@ -993,6 +977,12 @@ def _finalize_pympler(module, available):
993977
import pympler.muppy
994978

995979

980+
def _finalize_packaging(module, available):
981+
if available:
982+
# Import key subpackages that we will want to assume are present
983+
import packaging.version
984+
985+
996986
def _finalize_matplotlib(module, available):
997987
if not available:
998988
return
@@ -1094,6 +1084,10 @@ def _pyutilib_importer():
10941084
)
10951085
random, _ = attempt_import('random')
10961086

1087+
# Necessary for minimum version checking for other optional dependencies
1088+
packaging, packaging_available = attempt_import(
1089+
'packaging', deferred_submodules=['version'], callback=_finalize_packaging
1090+
)
10971091
# Commonly-used optional dependencies
10981092
dill, dill_available = attempt_import('dill')
10991093
mpi4py, mpi4py_available = attempt_import(

pyomo/common/tests/test_dependencies.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
dill,
3232
dill_available,
3333
mpi4py_available,
34+
packaging_available,
3435
)
3536

3637
import pyomo.common.tests.dep_mod as dep_mod
@@ -125,6 +126,9 @@ def test_imported_deferred_import(self):
125126
self.assertIs(dep_mod.bogus_nonexisting_module_available, False)
126127
self.assertIs(type(dep_mod.bogus_nonexisting_module), ModuleUnavailable)
127128

129+
@unittest.skipUnless(
130+
packaging_available, "min_version tests require packaging module"
131+
)
128132
def test_min_version(self):
129133
mod, avail = attempt_import(
130134
'pyomo.common.tests.dep_mod', minimum_version='1.0', defer_import=False
@@ -177,6 +181,9 @@ def test_min_version(self):
177181
mod, avail = attempt_import('pyomo.common.tests.bogus', minimum_version='1.0')
178182
self.assertFalse(check_min_version(mod, '1.0'))
179183

184+
@unittest.skipUnless(
185+
packaging_available, "min_version tests require packaging module"
186+
)
180187
def test_and_or(self):
181188
mod0, avail0 = attempt_import('ply', defer_import=True)
182189
mod1, avail1 = attempt_import('pyomo.common.tests.dep_mod', defer_import=True)
@@ -231,6 +238,9 @@ def test_and_or(self):
231238
self.assertIsInstance(_ror, _DeferredOr)
232239
self.assertTrue(_ror)
233240

241+
@unittest.skipUnless(
242+
packaging_available, "min_version tests require packaging module"
243+
)
234244
def test_callbacks(self):
235245
ans = []
236246

pyomo/contrib/appsi/solvers/maingo.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,18 @@ def available(self):
173173
return self._available
174174

175175
def version(self):
176-
import pkg_resources
176+
import importlib.metadata
177177

178-
version = pkg_resources.get_distribution('maingopy').version
179-
180-
return tuple(int(k) for k in version.split('.'))
178+
try:
179+
version = importlib.metadata.version('maingopy').split('.')
180+
except ImportError:
181+
return None
182+
for i, n in enumerate(version):
183+
try:
184+
version[i] = int(version[i])
185+
except:
186+
pass
187+
return tuple(version)
181188

182189
@property
183190
def config(self) -> MAiNGOConfig:

pyomo/contrib/fme/fourier_motzkin_elimination.py

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

pyomo/contrib/parmest/utils/model_utils.py

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

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

pyomo/core/base/block.py

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

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

pyomo/core/base/component.py

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

pyomo/core/base/expression.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@
3030
import pyomo.core.expr.numeric_expr as numeric_expr
3131
from pyomo.core.base.component import ComponentData, ModelComponentFactory
3232
from pyomo.core.base.global_set import UnindexedComponent_index
33-
from pyomo.core.base.indexed_component import IndexedComponent, UnindexedComponent_set
33+
from pyomo.core.base.indexed_component import (
34+
IndexedComponent,
35+
UnindexedComponent_set,
36+
IndexedComponent_NDArrayMixin,
37+
)
3438
from pyomo.core.expr.numvalue import as_numeric
3539
from pyomo.core.base.initializer import Initializer
3640

@@ -235,7 +239,7 @@ class _GeneralExpressionData(metaclass=RenamedClass):
235239
@ModelComponentFactory.register(
236240
"Named expressions that can be used in other expressions."
237241
)
238-
class Expression(IndexedComponent):
242+
class Expression(IndexedComponent, IndexedComponent_NDArrayMixin):
239243
"""A shared expression container, which may be defined over an index.
240244
241245
Parameters

0 commit comments

Comments
 (0)