Add XpressPyomoSolver

This commit is contained in:
2021-01-19 22:27:57 -06:00
parent 34e1711081
commit 4b8672870a
6 changed files with 77 additions and 15 deletions

View File

@@ -181,18 +181,27 @@ class BasePyomoSolver(InternalSolver):
if not should_repeat:
break
log = streams[0].getvalue()
return {
stats = {
"Lower bound": results["Problem"][0]["Lower bound"],
"Upper bound": results["Problem"][0]["Upper bound"],
"Wallclock time": total_wallclock_time,
"Nodes": self._extract_node_count(log),
"Sense": self._obj_sense,
"Log": log,
"Warm start value": self._extract_warm_start_value(log),
}
node_count = self._extract_node_count(log)
if node_count is not None:
stats["Nodes"] = node_count
ws_value = self._extract_warm_start_value(log)
if ws_value is not None:
stats["Warm start value"] = ws_value
return stats
@staticmethod
def __extract(log, regexp, default=None):
if regexp is None:
return default
value = default
for line in log.splitlines():
matches = re.findall(regexp, line)
@@ -208,18 +217,16 @@ class BasePyomoSolver(InternalSolver):
return value
def _extract_node_count(self, log):
return int(self.__extract(log, self._get_node_count_regexp(), default=1))
return self.__extract(log, self._get_node_count_regexp())
def get_constraint_ids(self):
return list(self._cname_to_constr.keys())
@abstractmethod
def _get_warm_start_regexp(self):
pass
return None
@abstractmethod
def _get_node_count_regexp(self):
pass
return None
def extract_constraint(self, cid):
raise Exception("Not implemented")

View File

@@ -0,0 +1,34 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
import sys
import logging
from io import StringIO
from pyomo import environ as pe
from scipy.stats import randint
from .base import BasePyomoSolver
from .. import RedirectOutput
logger = logging.getLogger(__name__)
class XpressPyomoSolver(BasePyomoSolver):
"""
An InternalSolver that uses XPRESS and the Pyomo modeling language.
Parameters
----------
params: dict
Dictionary of options to pass to the Pyomo solver. For example,
{"Threads": 4} to set the number of threads.
"""
def __init__(self, params=None):
super().__init__(
solver_factory=pe.SolverFactory("xpress_persistent"),
params={
"randomseed": randint(low=0, high=1000).rvs(),
},
)

View File

@@ -5,6 +5,7 @@
from inspect import isclass
from miplearn import BasePyomoSolver, GurobiSolver, GurobiPyomoSolver
from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance
from miplearn.solvers.pyomo.xpress import XpressPyomoSolver
def _get_instance(solver):
@@ -31,4 +32,4 @@ def _get_instance(solver):
def _get_internal_solvers():
return [GurobiPyomoSolver, GurobiSolver]
return [GurobiPyomoSolver, GurobiSolver, XpressPyomoSolver]

View File

@@ -43,7 +43,8 @@ def test_internal_solver_warm_starts():
}
)
stats = solver.solve(tee=True)
assert stats["Warm start value"] == 725.0
if "Warm start value" in stats:
assert stats["Warm start value"] == 725.0
solver.set_warm_start(
{
@@ -56,7 +57,8 @@ def test_internal_solver_warm_starts():
}
)
stats = solver.solve(tee=True)
assert stats["Warm start value"] is None
if "Warm start value" in stats:
assert stats["Warm start value"] is None
solver.fix(
{
@@ -97,7 +99,6 @@ def test_internal_solver():
assert stats["Upper bound"] == 1183.0
assert stats["Sense"] == "max"
assert isinstance(stats["Wallclock time"], float)
assert isinstance(stats["Nodes"], int)
solution = solver.get_solution()
assert solution["x"][0] == 1.0