Skip to content

Commit

Permalink
Merge branch 'main' into set-init-performance
Browse files Browse the repository at this point in the history
  • Loading branch information
jsiirola authored Jul 29, 2024
2 parents 0fc78ad + 1ada528 commit 3bc6a52
Show file tree
Hide file tree
Showing 28 changed files with 1,525 additions and 234 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test_branches.yml
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ jobs:
$BARON_DIR = "${env:TPL_DIR}/baron"
echo "$BARON_DIR" | `
Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
$URL = "https://www.minlp.com/downloads/xecs/baron/current/"
$URL = "https://minlp.com/downloads/xecs/baron/current/"
if ( "${{matrix.TARGET}}" -eq "win" ) {
$INSTALLER = "${env:DOWNLOAD_DIR}/baron_install.exe"
$URL += "baron-win64.exe"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_pr_and_main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ jobs:
$BARON_DIR = "${env:TPL_DIR}/baron"
echo "$BARON_DIR" | `
Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
$URL = "https://www.minlp.com/downloads/xecs/baron/current/"
$URL = "https://minlp.com/downloads/xecs/baron/current/"
if ( "${{matrix.TARGET}}" -eq "win" ) {
$INSTALLER = "${env:DOWNLOAD_DIR}/baron_install.exe"
$URL += "baron-win64.exe"
Expand Down
3 changes: 3 additions & 0 deletions examples/pyomo/tutorials/set.dat
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ set S[5] := 2 3;

set T[2] := 1 3;
set T[5] := 2 3;

set X[2] := 1;
set X[5] := 2 3;
8 changes: 6 additions & 2 deletions examples/pyomo/tutorials/set.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
23 Set Declarations
24 Set Declarations
A : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 3 : {1, 2, 3}
Expand Down Expand Up @@ -89,5 +89,9 @@
2 : 1 : Any : 5 : {1, 3, 5, 7, 9}
3 : 1 : Any : 5 : {1, 4, 7, 10, 13}
4 : 1 : Any : 5 : {1, 5, 9, 13, 17}
X : Size=2, Index=B, Ordered=Insertion
Key : Dimen : Domain : Size : Members
2 : 1 : S[2] : 1 : {1,}
5 : 1 : S[5] : 2 : {2, 3}

23 Declarations: A B C D E F G H Hsub I J K K_2 L M N O P R S T U V
24 Declarations: A B C D E F G H Hsub I J K K_2 L M N O P R S X T U V
7 changes: 7 additions & 0 deletions examples/pyomo/tutorials/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ def P_init(model, i, j):
#
model.S = Set(model.B, within=model.A)

#
# Validation of a set array can also be linked to another set array. If so, the
# elements under each index must also be found under the corresponding index in
# the validation set array:
#
model.X = Set(model.B, within=model.S)


#
# Validation of set arrays can also be performed with the _validate_ option.
Expand Down
40 changes: 11 additions & 29 deletions pyomo/contrib/appsi/solvers/maingo.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,33 +57,13 @@
from pyomo.repn.util import valid_expr_ctypes_minlp


def _import_SolverModel():
try:
from . import maingo_solvermodel
except ImportError:
raise
return maingo_solvermodel


maingo_solvermodel, solvermodel_available = attempt_import(
"maingo_solvermodel", importer=_import_SolverModel
)

MaingoVar = namedtuple("MaingoVar", "type name lb ub init")

logger = logging.getLogger(__name__)


def _import_maingopy():
try:
import maingopy
except ImportError:
MAiNGO._available = MAiNGO.Availability.NotFound
raise
return maingopy


maingopy, maingopy_available = attempt_import("maingopy", importer=_import_maingopy)
MaingoVar = namedtuple("MaingoVar", "type name lb ub init")
maingopy, maingopy_available = attempt_import("maingopy")
# Note that importing maingo_solvermodel will trigger the import of
# maingopy, so we defer that import using attempt_import (which will
# always succeed, even if maingopy is not available)
maingo_solvermodel = attempt_import("pyomo.contrib.appsi.solvers.maingo_solvermodel")[0]


class MAiNGOConfig(MIPSolverConfig):
Expand Down Expand Up @@ -185,9 +165,11 @@ def __init__(self, only_child_vars=False):
self._last_results_object: Optional[MAiNGOResults] = None

def available(self):
if not maingopy_available:
return self.Availability.NotFound
self._available = True
if self._available is None:
if maingopy_available:
MAiNGO._available = True
else:
MAiNGO._available = MAiNGO.Availability.NotFound
return self._available

def version(self):
Expand Down
12 changes: 2 additions & 10 deletions pyomo/contrib/appsi/solvers/maingo_solvermodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,7 @@
from pyomo.repn.util import valid_expr_ctypes_minlp


def _import_maingopy():
try:
import maingopy
except ImportError:
raise
return maingopy


maingopy, maingopy_available = attempt_import("maingopy", importer=_import_maingopy)
maingopy, maingopy_available = attempt_import("maingopy")

_plusMinusOne = {1, -1}

Expand Down Expand Up @@ -219,7 +211,7 @@ def _linear_to_maingo(self, node):
return sum(values)


class SolverModel(maingopy.MAiNGOmodel):
class SolverModel(maingopy.MAiNGOmodel if maingopy_available else object):
def __init__(self, var_list, objective, con_list, idmap, logger):
maingopy.MAiNGOmodel.__init__(self)
self._var_list = var_list
Expand Down
11 changes: 10 additions & 1 deletion pyomo/contrib/incidence_analysis/scc_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,14 @@ def generate_strongly_connected_components(
)
)

assert len(variables) == len(constraints)
if len(variables) != len(constraints):
nvar = len(variables)
ncon = len(constraints)
raise RuntimeError(
"generate_strongly_connected_components only supports systems with the"
f" same numbers of variables and equality constraints. Got {nvar}"
f" variables and {ncon} constraints."
)
if igraph is None:
igraph = IncidenceGraphInterface()

Expand All @@ -78,6 +85,8 @@ def generate_strongly_connected_components(
subsets, include_fixed=include_fixed
):
# TODO: How does len scale for reference-to-list?
# If this assert fails, it may be due to a bug in block_triangularize
# or generate_subsystem_block.
assert len(block.vars) == len(block.cons)
yield (block, inputs)

Expand Down
17 changes: 17 additions & 0 deletions pyomo/contrib/incidence_analysis/tests/test_scc_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,5 +501,22 @@ def test_with_inequalities(self):
self.assertEqual(m.x[3].value, 1.0)


@unittest.skipUnless(scipy_available, "SciPy is not available")
@unittest.skipUnless(networkx_available, "NetworkX is not available")
class TestExceptions(unittest.TestCase):
def test_nonsquare_system(self):
m = pyo.ConcreteModel()
m.x = pyo.Var([1, 2], initialize=1)
m.eq = pyo.Constraint(expr=m.x[1] + m.x[2] == 1)

msg = "Got 2 variables and 1 constraints"
with self.assertRaisesRegex(RuntimeError, msg):
list(
generate_strongly_connected_components(
constraints=[m.eq], variables=[m.x[1], m.x[2]]
)
)


if __name__ == "__main__":
unittest.main()
5 changes: 3 additions & 2 deletions pyomo/core/base/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -1984,7 +1984,8 @@ class Set(IndexedComponent):
within : initialiser(set), optional
A set that defines the valid values that can be contained
in this set
in this set. If the latter is indexed, the former can be indexed or
non-indexed, in which case it applies to all indices.
domain : initializer(set), optional
A set that defines the valid values that can be contained
in this set
Expand Down Expand Up @@ -2274,7 +2275,7 @@ def _getitem_when_not_present(self, index):

domain = self._init_domain(_block, index, self)
if domain is not None:
domain.construct()
domain.parent_component().construct()
if _d is UnknownSetDimen and domain is not None and domain.dimen is not None:
_d = domain.dimen

Expand Down
19 changes: 13 additions & 6 deletions pyomo/core/expr/compare.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,14 @@ def assertExpressionsEqual(test, a, b, include_named_exprs=True, places=None):
test.assertEqual(len(prefix_a), len(prefix_b))
for _a, _b in zip(prefix_a, prefix_b):
test.assertIs(_a.__class__, _b.__class__)
if places is None:
test.assertEqual(_a, _b)
# If _a is nan, check _b is nan
if _a != _a:
test.assertTrue(_b != _b)
else:
test.assertAlmostEqual(_a, _b, places=places)
if places is None:
test.assertEqual(_a, _b)
else:
test.assertAlmostEqual(_a, _b, places=places)
except (PyomoException, AssertionError):
test.fail(
f"Expressions not equal:\n\t"
Expand Down Expand Up @@ -292,10 +296,13 @@ def assertExpressionsStructurallyEqual(
for _a, _b in zip(prefix_a, prefix_b):
if _a.__class__ not in native_types and _b.__class__ not in native_types:
test.assertIs(_a.__class__, _b.__class__)
if places is None:
test.assertEqual(_a, _b)
if _a != _a:
test.assertTrue(_b != _b)
else:
test.assertAlmostEqual(_a, _b, places=places)
if places is None:
test.assertEqual(_a, _b)
else:
test.assertAlmostEqual(_a, _b, places=places)
except (PyomoException, AssertionError):
test.fail(
f"Expressions not structurally equal:\n\t"
Expand Down
4 changes: 2 additions & 2 deletions pyomo/core/kernel/conic.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ def as_domain(cls, r, x):
b = block()
b.r = variable_tuple([variable(lb=0) for i in range(len(r))])
b.x = variable()
b.c = _build_linking_constraints(list(r) + [x], list(b.r) + [x])
b.c = _build_linking_constraints(list(r) + [x], list(b.r) + [b.x])
b.q = cls(r=b.r, x=b.x)
return b

Expand Down Expand Up @@ -934,7 +934,7 @@ def as_domain(cls, r, x):
b = block()
b.r = variable_tuple([variable(lb=0) for i in range(len(r))])
b.x = variable()
b.c = _build_linking_constraints(list(r) + [x], list(b.r) + [x])
b.c = _build_linking_constraints(list(r) + [x], list(b.r) + [b.x])
b.q = cls(r=b.r, x=b.x)
return b

Expand Down
36 changes: 36 additions & 0 deletions pyomo/core/tests/unit/kernel/test_conic.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@
primal_power,
dual_exponential,
dual_power,
primal_geomean,
dual_geomean,
)


Expand Down Expand Up @@ -784,6 +786,40 @@ def test_as_domain(self):
x[1].value = None


# These mosek 10 constraints can't be evaluated, pprinted, checked for convexity,
# pickled, etc., so I won't use the _conic_tester_base for them
class Test_primal_geomean(unittest.TestCase):
def test_as_domain(self):
b = primal_geomean.as_domain(r=[2, 3], x=6)
self.assertIs(type(b), block)
self.assertIs(type(b.q), primal_geomean)
self.assertIs(type(b.r), variable_tuple)
self.assertIs(type(b.x), variable)
self.assertIs(type(b.c), constraint_tuple)
self.assertExpressionsEqual(b.c[0].body, b.r[0])
self.assertExpressionsEqual(b.c[0].rhs, 2)
self.assertExpressionsEqual(b.c[1].body, b.r[1])
self.assertExpressionsEqual(b.c[1].rhs, 3)
self.assertExpressionsEqual(b.c[2].body, b.x)
self.assertExpressionsEqual(b.c[2].rhs, 6)


class Test_dual_geomean(unittest.TestCase):
def test_as_domain(self):
b = dual_geomean.as_domain(r=[2, 3], x=6)
self.assertIs(type(b), block)
self.assertIs(type(b.q), dual_geomean)
self.assertIs(type(b.r), variable_tuple)
self.assertIs(type(b.x), variable)
self.assertIs(type(b.c), constraint_tuple)
self.assertExpressionsEqual(b.c[0].body, b.r[0])
self.assertExpressionsEqual(b.c[0].rhs, 2)
self.assertExpressionsEqual(b.c[1].body, b.r[1])
self.assertExpressionsEqual(b.c[1].rhs, 3)
self.assertExpressionsEqual(b.c[2].body, b.x)
self.assertExpressionsEqual(b.c[2].rhs, 6)


class TestMisc(unittest.TestCase):
def test_build_linking_constraints(self):
c = _build_linking_constraints([], [])
Expand Down
Loading

0 comments on commit 3bc6a52

Please sign in to comment.