Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion pyomo/contrib/gdpopt/loa.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,8 @@ def _setup_augmented_penalty_objective(self, discrete_problem_util_block):
# Set up augmented penalty objective
discrete_objective.deactivate()
# placeholder for OA objective
discrete_problem_util_block.oa_obj = Objective(sense=minimize)
discrete_problem_util_block.oa_obj = Objective()
discrete_problem_util_block.oa_obj.sense = discrete_objective.sense

return discrete_objective

Expand Down
43 changes: 43 additions & 0 deletions pyomo/contrib/gdpopt/tests/test_gdpopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
Integers,
LogicalConstraint,
maximize,
minimize,
Objective,
RangeSet,
TransformationFactory,
Expand Down Expand Up @@ -322,6 +323,48 @@ def test_complain_when_no_algorithm_specified(self):
):
SolverFactory('gdpopt').solve(m)

def test_loa_augmented_penalty_objective_preserves_objective_sense(self):
for sense in (minimize, maximize):
m = ConcreteModel()
m.GDPopt_utils = Block()
m.x = Var(bounds=(0, 1))
m.obj = Objective(expr=m.x, sense=sense)

solver = SolverFactory('gdpopt.loa')
original_obj = solver._setup_augmented_penalty_objective(m.GDPopt_utils)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be ideal if the test didn't rely on calling a private method. The test below covers the change, so I think you could just remove this one, keep the one below, and dodge the problem.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in a18df5d85: I removed the private-method test and its now-unused import, while keeping the end-to-end LOA regression. I also mutation-checked the remaining test: it passes with the fix and fails with the prior behavior (x = 1 instead of 9). The full GDPopt module and Black check pass.


self.assertIs(original_obj, m.obj)
self.assertFalse(m.obj.active)
self.assertEqual(m.GDPopt_utils.oa_obj.sense, sense)

@unittest.skipUnless(
SolverFactory(mip_solver).available(), "MIP solver not available"
)
def test_LOA_maximize_matches_minimize_negated_objective(self):
def build_model(maximize_objective):
m = ConcreteModel()
m.x = Var(bounds=(0, 10))
m.disjunction = Disjunction(expr=[[m.x == 1], [m.x == 9]])
if maximize_objective:
m.obj = Objective(expr=m.x, sense=maximize)
else:
m.obj = Objective(expr=-m.x)
return m

for maximize_objective in (False, True):
m = build_model(maximize_objective)
results = SolverFactory('gdpopt.loa').solve(
m,
mip_solver=mip_solver,
nlp_solver=nlp_solver,
init_algorithm='no_init',
)

self.assertEqual(
results.solver.termination_condition, TerminationCondition.optimal
)
self.assertAlmostEqual(value(m.x), 9)

@unittest.skipIf(
not LOA_solvers_available,
"Required subsolvers %s are not available" % (LOA_solvers,),
Expand Down
Loading