Skip to content

Commit

Permalink
Capture output from HiGHS when initializing the model (fixes Pyomo#3003)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsiirola committed Sep 25, 2023
1 parent 8967037 commit f6357df
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 9 deletions.
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 @@ def set_instance(self, model):
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()

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
46 changes: 46 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,12 @@

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 +69,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):])

0 comments on commit f6357df

Please sign in to comment.