Skip to content

Commit

Permalink
Merge branch 'main' into python37-support
Browse files Browse the repository at this point in the history
  • Loading branch information
jsiirola authored Oct 4, 2023
2 parents 4a8c179 + eb2628e commit 5af3427
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 30 deletions.
5 changes: 4 additions & 1 deletion pyomo/contrib/cp/tests/test_logical_to_disjunctive.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@
TransformationFactory,
)

gurobi_available = SolverFactory('gurobi').available(exception_flag=False)
gurobi_available = (
SolverFactory('gurobi').available(exception_flag=False)
and SolverFactory('gurobi').license_is_valid()
)


class TestLogicalToDisjunctiveVisitor(unittest.TestCase):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ def test_integer_arithmetic_non1_coefficients(self):
def test_numerical_instability_almost_canceling(self):
# It's possible that we get almost-but-not-quite zero on the variable
# being eliminated when we are doing this with floating point
# arithmetic. This can get ugly later becuase it might get muliplied by
# arithmetic. This can get ugly later because it might get muliplied by
# a large number later and start to "reappear"
m = ConcreteModel()
m.x = Var()
Expand Down
28 changes: 15 additions & 13 deletions pyomo/contrib/gdpopt/tests/test_enumerate.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@


@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi not available')
@unittest.skipUnless(SolverFactory('gurobi').license_is_valid(), 'Gurobi not licensed')
class TestGDPoptEnumerate(unittest.TestCase):
def test_solve_two_term_disjunction(self):
m = models.makeTwoTermDisj()
Expand Down Expand Up @@ -144,18 +145,6 @@ def test_stop_at_iteration_limit(self):
results.solver.termination_condition, TerminationCondition.maxIterations
)

@unittest.skipUnless(SolverFactory('ipopt').available(), 'Ipopt not available')
def test_infeasible_GDP(self):
m = models.make_infeasible_gdp_model()

results = SolverFactory('gdpopt.enumerate').solve(m)

self.assertEqual(results.solver.iterations, 2)
self.assertEqual(
results.solver.termination_condition, TerminationCondition.infeasible
)
self.assertEqual(results.problem.lower_bound, float('inf'))

def test_unbounded_GDP(self):
m = ConcreteModel()
m.x = Var(bounds=(-1, 10))
Expand All @@ -173,7 +162,20 @@ def test_unbounded_GDP(self):
self.assertEqual(results.problem.lower_bound, -float('inf'))
self.assertEqual(results.problem.upper_bound, -float('inf'))

@unittest.skipUnless(SolverFactory('ipopt').available(), 'Ipopt not available')

@unittest.skipUnless(SolverFactory('ipopt').available(), 'Ipopt not available')
class TestGDPoptEnumerate_ipopt_tests(unittest.TestCase):
def test_infeasible_GDP(self):
m = models.make_infeasible_gdp_model()

results = SolverFactory('gdpopt.enumerate').solve(m)

self.assertEqual(results.solver.iterations, 2)
self.assertEqual(
results.solver.termination_condition, TerminationCondition.infeasible
)
self.assertEqual(results.problem.lower_bound, float('inf'))

def test_algorithm_specified_to_solve(self):
m = models.twoDisj_twoCircles_easy()

Expand Down
19 changes: 11 additions & 8 deletions pyomo/contrib/gdpopt/tests/test_gdpopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
else False
)

gurobi_available = (
SolverFactory('gurobi').available(exception_flag=False)
and SolverFactory('gurobi').license_is_valid()
)


class TestGDPoptUnit(unittest.TestCase):
"""Real unit tests for GDPopt"""
Expand Down Expand Up @@ -129,7 +134,7 @@ def test_solve_lp(self):
self.assertAlmostEqual(results.problem.lower_bound, 1)
self.assertAlmostEqual(results.problem.upper_bound, 1)

@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi not available')
@unittest.skipUnless(gurobi_available, 'Gurobi not available')
def test_solve_nlp(self):
m = ConcreteModel()
m.x = Var(bounds=(-5, 5))
Expand Down Expand Up @@ -222,7 +227,7 @@ def test_is_feasible_function(self):
with self.assertRaisesRegex(NotImplementedError, "Found active disjunct"):
is_feasible(m, GDP_LOA_Solver.CONFIG())

@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi not available')
@unittest.skipUnless(gurobi_available, 'Gurobi not available')
def test_infeasible_or_unbounded_mip_termination(self):
m = ConcreteModel()
m.x = Var()
Expand Down Expand Up @@ -461,9 +466,7 @@ def test_unbounded_gdp_maximization(self):
# [ESJ 5/16/22]: Using Gurobi for this test because glpk seems to get angry
# on Windows when the MIP is arbitrarily bounded with the large bounds. And
# I think I blame glpk...
@unittest.skipUnless(
SolverFactory('gurobi').available(), "Gurobi solver not available"
)
@unittest.skipUnless(gurobi_available, "Gurobi solver not available")
def test_GDP_nonlinear_objective(self):
m = ConcreteModel()
m.x = Var(bounds=(-1, 10))
Expand Down Expand Up @@ -1672,7 +1675,7 @@ def make_model(self):
return m

@unittest.skipIf(not mcpp_available(), "MC++ is not available")
@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi not available')
@unittest.skipUnless(gurobi_available, 'Gurobi not available')
def test_set_options_on_config_block(self):
m = self.make_model()

Expand Down Expand Up @@ -1711,7 +1714,7 @@ def test_set_options_on_config_block(self):
self.assertAlmostEqual(value(m.obj), -0.25)

@unittest.skipIf(not mcpp_available(), "MC++ is not available")
@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi not available')
@unittest.skipUnless(gurobi_available, 'Gurobi not available')
def test_set_options_in_init(self):
m = self.make_model()

Expand All @@ -1735,7 +1738,7 @@ def test_set_options_in_init(self):
self.assertAlmostEqual(value(m.obj), -0.25)
self.assertEqual(opt.config.mip_solver, 'gurobi')

@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi not available')
@unittest.skipUnless(gurobi_available, 'Gurobi not available')
def test_no_default_algorithm(self):
m = self.make_model()

Expand Down
1 change: 1 addition & 0 deletions pyomo/contrib/piecewise/tests/test_inner_repn_gdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ def test_descend_into_expressions_objective_target(self):
)

@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi is not available')
@unittest.skipUnless(SolverFactory('gurobi').license_is_valid(), 'No license')
def test_solve_disaggregated_convex_combo_model(self):
m = models.make_log_x_model()
TransformationFactory(
Expand Down
7 changes: 3 additions & 4 deletions pyomo/contrib/piecewise/tests/test_outer_repn_gdp.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,9 @@ def test_descend_into_expressions_objective_target(self):
self, 'contrib.piecewise.outer_repn_gdp'
)

@unittest.skipUnless(
SolverFactory('gurobi').available() and scipy_available,
'Gurobi and/or scipy is not available',
)
@unittest.skipUnless(scipy_available, "scipy is not available")
@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi is not available')
@unittest.skipUnless(SolverFactory('gurobi').license_is_valid(), 'No license')
def test_solve_multiple_choice_model(self):
m = models.make_log_x_model()
TransformationFactory('contrib.piecewise.multiple_choice').apply_to(m)
Expand Down
1 change: 1 addition & 0 deletions pyomo/contrib/piecewise/tests/test_reduced_inner_repn.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ def test_descend_into_expressions_objective_target(self):
)

@unittest.skipUnless(SolverFactory('gurobi').available(), 'Gurobi is not available')
@unittest.skipUnless(SolverFactory('gurobi').license_is_valid(), 'No license')
def test_solve_convex_combo_model(self):
m = models.make_log_x_model()
TransformationFactory('contrib.piecewise.convex_combination').apply_to(m)
Expand Down
5 changes: 4 additions & 1 deletion pyomo/gdp/tests/test_mbigm.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@
)
from pyomo.repn import generate_standard_repn

gurobi_available = SolverFactory('gurobi').available()
gurobi_available = (
SolverFactory('gurobi').available(exception_flag=False)
and SolverFactory('gurobi').license_is_valid()
)
exdir = normpath(join(PYOMO_ROOT_DIR, 'examples', 'gdp'))


Expand Down
2 changes: 1 addition & 1 deletion pyomo/gdp/tests/test_partition_disjuncts.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ def test_transformation_block_nested_disjunction_outer_disjunction_target(self):
self.check_transformation_block_nested_disjunction(m, disj2, m.disj1.x)

def test_transformation_block_nested_disjunction_badly_ordered_targets(self):
"""This tests that we preprocess targets correctly becuase we don't
"""This tests that we preprocess targets correctly because we don't
want to double transform the inner disjunct, which is what would happen
if we did things in the order given."""
m = models.makeBetweenStepsPaperExample_Nested()
Expand Down
21 changes: 20 additions & 1 deletion pyomo/solvers/plugins/solvers/GUROBI.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from pyomo.common import Executable
from pyomo.common.collections import Bunch
from pyomo.common.fileutils import this_file_dir
from pyomo.common.tee import capture_output
from pyomo.common.tempfiles import TempfileManager

from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver
Expand All @@ -32,8 +33,10 @@
)
from pyomo.opt.solver import ILMLicensedSystemCallSolver
from pyomo.core.kernel.block import IBlock
from pyomo.core import ConcreteModel, Var, Objective

from .gurobi_direct import gurobipy_available
from .ASL import ASL

logger = logging.getLogger('pyomo.solvers')

Expand Down Expand Up @@ -69,14 +72,30 @@ def __new__(cls, *args, **kwds):
if mode == 'os':
opt = SolverFactory('_ossolver', **kwds)
elif mode == 'nl':
opt = SolverFactory('asl', **kwds)
opt = SolverFactory('_gurobi_nl', **kwds)
else:
logger.error('Unknown IO type: %s' % mode)
return
opt.set_options('solver=gurobi_ampl')
return opt


@SolverFactory.register('_gurobi_nl', doc='NL interface to the Gurobi solver')
class GUROBINL(ASL):
"""NL interface to gurobi_ampl."""

def license_is_valid(self):
m = ConcreteModel()
m.x = Var(bounds=(1, 2))
m.obj = Objective(expr=m.x)
try:
with capture_output():
self.solve(m)
return abs(m.x.value - 1) <= 1e-4
except:
return False


@SolverFactory.register(
'_gurobi_shell', doc='Shell interface to the GUROBI LP/MIP solver'
)
Expand Down
5 changes: 5 additions & 0 deletions pyomo/solvers/tests/checks/test_gurobi_direct.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
except ImportError:
gurobipy_available = False

gurobi_available = GurobiDirect().available(exception_flag=False)


def clean_up_global_state():
# Best efforts to dispose any gurobipy objects from previous tests
Expand Down Expand Up @@ -79,6 +81,7 @@ def test_gurobipy_not_installed(self):


@unittest.skipIf(not gurobipy_available, "gurobipy is not available")
@unittest.skipIf(not gurobi_available, "gurobi license is not valid")
class GurobiParameterTests(GurobiBase):
# Test parameter handling at the model and environment level

Expand Down Expand Up @@ -158,6 +161,7 @@ def test_param_changes_4(self):


@unittest.skipIf(not gurobipy_available, "gurobipy is not available")
@unittest.skipIf(not gurobi_available, "gurobi license is not valid")
class GurobiEnvironmentTests(GurobiBase):
# Test handling of gurobi environments

Expand Down Expand Up @@ -318,6 +322,7 @@ def test_nonmanaged_env(self):


@unittest.skipIf(not gurobipy_available, "gurobipy is not available")
@unittest.skipIf(not gurobi_available, "gurobi license is not valid")
@unittest.skipIf(not single_use_license(), reason="test needs a single use license")
class GurobiSingleUseTests(GurobiBase):
# Integration tests for Gurobi single-use licenses (useful for checking all Gurobi
Expand Down

0 comments on commit 5af3427

Please sign in to comment.