Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capture HiGHS output when initializing model #3005

Merged
merged 1 commit into from
Sep 25, 2023
Merged
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
28 changes: 19 additions & 9 deletions pyomo/contrib/appsi/solvers/highs.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,15 +345,25 @@
f'Solver {c.__module__}.{c.__qualname__} is not available '
f'({self.available()}).'
)
self._reinit()
self._model = model
if self.use_extensions and cmodel_available:
self._expr_types = cmodel.PyomoExprTypes()

self._solver_model = highspy.Highs()
self.add_block(model)
if self._objective is None:
self.set_objective(None)

ostreams = [
LogStream(
level=self.config.log_level, logger=self.config.solver_output_logger
)
]
if self.config.stream_solver:
ostreams.append(sys.stdout)
with TeeStream(*ostreams) as t:
with capture_output(output=t.STDOUT, capture_fd=True):
self._reinit()
self._model = model
if self.use_extensions and cmodel_available:
self._expr_types = cmodel.PyomoExprTypes()

Check warning on line 361 in pyomo/contrib/appsi/solvers/highs.py

View check run for this annotation

Codecov / codecov/patch

pyomo/contrib/appsi/solvers/highs.py#L361

Added line #L361 was not covered by tests

self._solver_model = highspy.Highs()
self.add_block(model)
if self._objective is None:
self.set_objective(None)

def _add_constraints(self, cons: List[_GeneralConstraintData]):
self._sol = None
Expand Down
45 changes: 45 additions & 0 deletions pyomo/contrib/appsi/solvers/tests/test_highs_persistent.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import subprocess
import sys

import pyomo.common.unittest as unittest
import pyomo.environ as pe

from pyomo.common.log import LoggingIntercept
from pyomo.common.tee import capture_output
from pyomo.contrib.appsi.solvers.highs import Highs
from pyomo.contrib.appsi.base import TerminationCondition

Expand Down Expand Up @@ -62,3 +68,42 @@ def test_mutable_params_with_remove_vars(self):
m.p2.value = 9
res = opt.solve(m)
self.assertAlmostEqual(res.best_feasible_objective, -9)

def test_capture_highs_output(self):
# tests issue #3003
#
# Note that the "Running HiGHS" message is only emitted the
# first time that a model is instantiated. We need to test this
# in a subprocess to trigger that output.
model = [
'import pyomo.environ as pe',
'm = pe.ConcreteModel()',
'm.x = pe.Var(domain=pe.NonNegativeReals)',
'm.y = pe.Var(domain=pe.NonNegativeReals)',
'm.obj = pe.Objective(expr=m.x + m.y, sense=pe.maximize)',
'm.c1 = pe.Constraint(expr=m.x <= 10)',
'm.c2 = pe.Constraint(expr=m.y <= 5)',
'from pyomo.contrib.appsi.solvers.highs import Highs',
'result = Highs().solve(m)',
'print(m.x.value, m.y.value)',
]

with LoggingIntercept() as LOG, capture_output(capture_fd=True) as OUT:
subprocess.run([sys.executable, '-c', ';'.join(model)])
self.assertEqual(LOG.getvalue(), "")
self.assertEqual(OUT.getvalue(), "10.0 5.0\n")

model[-2:-1] = [
'opt = Highs()',
'opt.config.stream_solver = True',
'result = opt.solve(m)',
]
with LoggingIntercept() as LOG, capture_output(capture_fd=True) as OUT:
subprocess.run([sys.executable, '-c', ';'.join(model)])
self.assertEqual(LOG.getvalue(), "")
# This is emitted by the model set-up
self.assertIn("Running HiGHS", OUT.getvalue())
# This is emitted by the solve()
self.assertIn("HiGHS run time", OUT.getvalue())
ref = "10.0 5.0\n"
self.assertEqual(ref, OUT.getvalue()[-len(ref) :])