Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion pyomo/contrib/gdpopt/branch_and_bound.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ def _solve_gdp(self, model, config):
config.logger.info(
'Final bound values: LB: {} UB: {}'.format(self.LB, self.UB)
)
return self._get_final_results_object()
return self._get_final_pyomo_results_object()

# Handle current node
if not node_data.is_screened:
Expand Down
10 changes: 9 additions & 1 deletion pyomo/contrib/gdpopt/config_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
In,
NonNegativeFloat,
NonNegativeInt,
PositiveFloat,
PositiveInt,
)
from pyomo.common.deprecation import deprecation_warning
Expand Down Expand Up @@ -50,6 +51,13 @@ def _init_strategy_deprecation(strategy):
return In(valid_init_strategies)(strategy)


def _positive_time_limit(value):
try:
return PositiveInt(value)
except (TypeError, ValueError):
return PositiveFloat(value)


def _get_algorithm_config():
CONFIG = ConfigBlock("GDPoptAlgorithm")
CONFIG.declare(
Expand Down Expand Up @@ -82,7 +90,7 @@ def _add_common_configs(CONFIG):
"time_limit",
ConfigValue(
default=None,
domain=PositiveInt,
domain=_positive_time_limit,
description="Time limit (seconds, default=600)",
doc="""
Seconds allowed until terminated. Note that the time limit can
Expand Down
34 changes: 32 additions & 2 deletions pyomo/contrib/gdpopt/tests/test_LBB.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,16 @@
from pyomo.common.log import LoggingIntercept
import pyomo.contrib.gdpopt.tests.common_tests as ct
from pyomo.contrib.satsolver.satsolver import z3_available
from pyomo.environ import SolverFactory, value, ConcreteModel, Var, Objective, maximize
from pyomo.gdp import Disjunction
from pyomo.environ import (
SolverFactory,
value,
ConcreteModel,
Constraint,
Var,
Objective,
maximize,
)
from pyomo.gdp import Disjunct, Disjunction
from pyomo.opt import TerminationCondition

currdir = dirname(abspath(__file__))
Expand All @@ -35,6 +43,28 @@
)


class TestGDPopt_LBB_TimeLimit(unittest.TestCase):
"""Tests for solver-independent LBB termination paths."""

def test_time_limit_returns_pyomo_results_object(self):
m = ConcreteModel()
m.x = Var(bounds=(0, 2))
m.d1 = Disjunct()
m.d2 = Disjunct()
m.d1.c = Constraint(expr=m.x <= 0.5)
m.d2.c = Constraint(expr=m.x >= 1.5)
m.disj = Disjunction(expr=[m.d1, m.d2])
m.obj = Objective(expr=m.x)

results = SolverFactory('gdpopt.lbb').solve(m, time_limit=1e-6, tee=False)

self.assertEqual(
results.solver.termination_condition, TerminationCondition.maxTimeLimit
)
self.assertEqual(results.problem.lower_bound, float('-inf'))
self.assertEqual(results.problem.upper_bound, float('inf'))


@unittest.skipUnless(
solver_available, "Required subsolver %s is not available" % (minlp_solver,)
)
Expand Down
22 changes: 21 additions & 1 deletion pyomo/contrib/mindtpy/tests/test_mindtpy_no_discrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
# ____________________________________________________________________________________


import logging
from unittest.mock import MagicMock, patch

from pyomo.opt import TerminationCondition as tc, SolverStatus
from pyomo.common import timing
from pyomo.contrib.mindtpy.global_outer_approximation import MindtPy_GOA_Solver
from pyomo.opt import TerminationCondition as tc, SolverStatus, SolverResults
import pyomo.common.unittest as unittest

from pyomo.environ import (
Expand Down Expand Up @@ -455,6 +458,23 @@ def test_solver_status_and_message_mirrored(self):
self.assertEqual(algo.results.solver.message, "All good")


class TestMindtPyGOATimeLimit(unittest.TestCase):
def test_goa_time_limit_sets_solver_results_condition(self):
solver = MindtPy_GOA_Solver()
solver.config = _SimpleNamespace(
logger=logging.getLogger(__name__), single_tree=False, time_limit=1
)
solver.results = SolverResults()
solver.timing = _SimpleNamespace(
main_timer_start_time=timing.default_timer() - 2
)
solver.primal_bound = float('inf')
solver.dual_bound = float('-inf')

self.assertTrue(solver.reached_time_limit())
self.assertEqual(solver.results.solver.termination_condition, tc.maxTimeLimit)


class _FakeLegacyMIPSolver:
def __init__(
self,
Expand Down
Loading