From a78f5a5fb9fddbae328841c16832e6c5f1b688e1 Mon Sep 17 00:00:00 2001 From: bammari Date: Thu, 8 Jan 2026 10:42:40 -0500 Subject: [PATCH 1/7] Change getname argument relative_to to ensure constraint and disjunct names can be resolved when instance searches for components during M calculations --- pyomo/gdp/plugins/multiple_bigm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyomo/gdp/plugins/multiple_bigm.py b/pyomo/gdp/plugins/multiple_bigm.py index 9b98d630762..0261bc5bdc4 100644 --- a/pyomo/gdp/plugins/multiple_bigm.py +++ b/pyomo/gdp/plugins/multiple_bigm.py @@ -462,8 +462,8 @@ def _transform_disjunctionDatas( if jobs: jobs_by_name = [ ( - constraint.getname(fully_qualified=True), - other_disjunct.getname(fully_qualified=True), + constraint.getname(fully_qualified=True, relative_to=instance), + other_disjunct.getname(fully_qualified=True, relative_to=instance), unsuccessful_solve_msg, is_upper, ) From 85d5fa604ae08580e38176f83b7adddd15d87497 Mon Sep 17 00:00:00 2001 From: bammari Date: Thu, 8 Jan 2026 19:32:43 -0500 Subject: [PATCH 2/7] Add test to ensure transformation works on block --- pyomo/gdp/tests/test_mbigm.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pyomo/gdp/tests/test_mbigm.py b/pyomo/gdp/tests/test_mbigm.py index a59b5f3cddb..d5d966314d0 100644 --- a/pyomo/gdp/tests/test_mbigm.py +++ b/pyomo/gdp/tests/test_mbigm.py @@ -35,6 +35,7 @@ TransformationFactory, value, Var, + Block, ) from pyomo.gdp import Disjunct, Disjunction, GDP_Error from pyomo.gdp.tests.common_tests import ( @@ -1191,3 +1192,23 @@ def test_complain_for_unrecognized_Suffix(self): TransformationFactory('gdp.mbigm').apply_to( m, reduce_bound_constraints=False ) + + def test_transform_on_block(self): + # In multiple_bigm.py, if constraint.getname and disjunct.getname do not + # set relative_to=instance, the transformation fails because of mismatched + # names in _calc_M. + # This test ensures that the transformation works on blocks as well as + # ConcreteModels. + m = ConcreteModel() + m.b = Block() + m.b.x = Var(bounds=(0, 5)) + m.b.y = Var() + m.b.dis1 = Disjunct() + m.b.dis2 = Disjunct() + + m.b.dis1.linear = Constraint(expr=m.b.x * 0.5 + 3 == m.b.y) + m.b.dis2.linear = Constraint(expr=m.b.x * 0.2 + 1 == m.b.y) + m.b.d = Disjunction(expr=[m.b.dis1, m.b.dis2]) + + TransformationFactory('gdp.mbigm').apply_to(m.b, threads=1) + assert m.b.find_component('dis1.linear') == 'b.dis1.linear' From c67ec2ccabb8795e214c0fb884625abf2e372ec9 Mon Sep 17 00:00:00 2001 From: bammari Date: Thu, 8 Jan 2026 19:36:50 -0500 Subject: [PATCH 3/7] assertEqual instead of assert --- pyomo/gdp/tests/test_mbigm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/gdp/tests/test_mbigm.py b/pyomo/gdp/tests/test_mbigm.py index d5d966314d0..c7e4e1b42c8 100644 --- a/pyomo/gdp/tests/test_mbigm.py +++ b/pyomo/gdp/tests/test_mbigm.py @@ -1211,4 +1211,4 @@ def test_transform_on_block(self): m.b.d = Disjunction(expr=[m.b.dis1, m.b.dis2]) TransformationFactory('gdp.mbigm').apply_to(m.b, threads=1) - assert m.b.find_component('dis1.linear') == 'b.dis1.linear' + self.assertEqual(m.b.find_component('dis1.linear'), 'b.dis1.linear') From c79c58b581a5eb2a0cbfb766092ae163e15187c0 Mon Sep 17 00:00:00 2001 From: bammari Date: Thu, 8 Jan 2026 19:44:14 -0500 Subject: [PATCH 4/7] black and assert names equal --- pyomo/gdp/tests/test_mbigm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/gdp/tests/test_mbigm.py b/pyomo/gdp/tests/test_mbigm.py index c7e4e1b42c8..f08cc25a9ff 100644 --- a/pyomo/gdp/tests/test_mbigm.py +++ b/pyomo/gdp/tests/test_mbigm.py @@ -1211,4 +1211,4 @@ def test_transform_on_block(self): m.b.d = Disjunction(expr=[m.b.dis1, m.b.dis2]) TransformationFactory('gdp.mbigm').apply_to(m.b, threads=1) - self.assertEqual(m.b.find_component('dis1.linear'), 'b.dis1.linear') + self.assertEqual(m.b.find_component('dis1.linear').name(), 'b.dis1.linear') From 4298328721b0ef5da12c12f715597b4cd887db45 Mon Sep 17 00:00:00 2001 From: bammari Date: Thu, 8 Jan 2026 19:45:08 -0500 Subject: [PATCH 5/7] assert names equal --- pyomo/gdp/tests/test_mbigm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/gdp/tests/test_mbigm.py b/pyomo/gdp/tests/test_mbigm.py index f08cc25a9ff..c1b7949e365 100644 --- a/pyomo/gdp/tests/test_mbigm.py +++ b/pyomo/gdp/tests/test_mbigm.py @@ -1211,4 +1211,4 @@ def test_transform_on_block(self): m.b.d = Disjunction(expr=[m.b.dis1, m.b.dis2]) TransformationFactory('gdp.mbigm').apply_to(m.b, threads=1) - self.assertEqual(m.b.find_component('dis1.linear').name(), 'b.dis1.linear') + self.assertEqual(m.b.find_component('dis1.linear').name, 'b.dis1.linear') From 7fd5d1b4431761303ff9a437d314eb140ae329c2 Mon Sep 17 00:00:00 2001 From: bammari Date: Fri, 9 Jan 2026 09:34:54 -0500 Subject: [PATCH 6/7] skip transform on block test if Gurobi not available --- pyomo/gdp/tests/test_mbigm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pyomo/gdp/tests/test_mbigm.py b/pyomo/gdp/tests/test_mbigm.py index c1b7949e365..8d2b1a583c4 100644 --- a/pyomo/gdp/tests/test_mbigm.py +++ b/pyomo/gdp/tests/test_mbigm.py @@ -1193,6 +1193,7 @@ def test_complain_for_unrecognized_Suffix(self): m, reduce_bound_constraints=False ) + @unittest.skipUnless(gurobi_available, "Gurobi is not available") def test_transform_on_block(self): # In multiple_bigm.py, if constraint.getname and disjunct.getname do not # set relative_to=instance, the transformation fails because of mismatched From e4986a0f7a5758f50286664060e9979617a43ab1 Mon Sep 17 00:00:00 2001 From: bammari Date: Fri, 9 Jan 2026 13:44:56 -0500 Subject: [PATCH 7/7] Add assertions that test whether linear constraint in disjunct 1 is transformed correctly. --- pyomo/gdp/tests/test_mbigm.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pyomo/gdp/tests/test_mbigm.py b/pyomo/gdp/tests/test_mbigm.py index 8d2b1a583c4..7d65d383c81 100644 --- a/pyomo/gdp/tests/test_mbigm.py +++ b/pyomo/gdp/tests/test_mbigm.py @@ -1211,5 +1211,16 @@ def test_transform_on_block(self): m.b.dis2.linear = Constraint(expr=m.b.x * 0.2 + 1 == m.b.y) m.b.d = Disjunction(expr=[m.b.dis1, m.b.dis2]) - TransformationFactory('gdp.mbigm').apply_to(m.b, threads=1) - self.assertEqual(m.b.find_component('dis1.linear').name, 'b.dis1.linear') + mbm = TransformationFactory('gdp.mbigm') + mbm.apply_to(m.b, threads=1) + dis1_cons = mbm.get_transformed_constraints(m.b.dis1.linear) + assertExpressionsEqual( + self, + dis1_cons[0].expr, + 2.0 * m.b.dis2.binary_indicator_var <= 0.5 * m.b.x + 3 - m.b.y, + ) + assertExpressionsEqual( + self, + dis1_cons[1].expr, + 0.5 * m.b.x + 3 - m.b.y <= 3.5 * m.b.dis2.binary_indicator_var, + )