diff --git a/.github/workflows/test_branches.yml b/.github/workflows/test_branches.yml index 8ba04eec466..03894a1cb20 100644 --- a/.github/workflows/test_branches.yml +++ b/.github/workflows/test_branches.yml @@ -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" diff --git a/.github/workflows/test_pr_and_main.yml b/.github/workflows/test_pr_and_main.yml index 0bfd12b998d..cc9760cbe5d 100644 --- a/.github/workflows/test_pr_and_main.yml +++ b/.github/workflows/test_pr_and_main.yml @@ -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" diff --git a/pyomo/contrib/incidence_analysis/scc_solver.py b/pyomo/contrib/incidence_analysis/scc_solver.py index 378647c190c..db201dccb0a 100644 --- a/pyomo/contrib/incidence_analysis/scc_solver.py +++ b/pyomo/contrib/incidence_analysis/scc_solver.py @@ -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() @@ -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) diff --git a/pyomo/contrib/incidence_analysis/tests/test_scc_solver.py b/pyomo/contrib/incidence_analysis/tests/test_scc_solver.py index b75f93e4a12..ef4853d7e9a 100644 --- a/pyomo/contrib/incidence_analysis/tests/test_scc_solver.py +++ b/pyomo/contrib/incidence_analysis/tests/test_scc_solver.py @@ -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() diff --git a/pyomo/core/kernel/conic.py b/pyomo/core/kernel/conic.py index 1bb5f1b6ce8..bd78ba310f4 100644 --- a/pyomo/core/kernel/conic.py +++ b/pyomo/core/kernel/conic.py @@ -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 @@ -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 diff --git a/pyomo/core/tests/unit/kernel/test_conic.py b/pyomo/core/tests/unit/kernel/test_conic.py index ccfbcca7e1f..bd97c13fc2e 100644 --- a/pyomo/core/tests/unit/kernel/test_conic.py +++ b/pyomo/core/tests/unit/kernel/test_conic.py @@ -35,6 +35,8 @@ primal_power, dual_exponential, dual_power, + primal_geomean, + dual_geomean, ) @@ -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([], [])