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()