Add types to internal solvers

master
Alinson S. Xavier 5 years ago
parent d500294ebd
commit f7ce441fa6

@ -164,12 +164,3 @@ class Instance(ABC):
data = json.dumps(self.__dict__, indent=2).encode("utf-8") data = json.dumps(self.__dict__, indent=2).encode("utf-8")
with gzip.GzipFile(filename, "w") as f: with gzip.GzipFile(filename, "w") as f:
f.write(data) f.write(data)
class PyomoInstance(Instance, ABC):
@abstractmethod
def to_model(self) -> pe.ConcreteModel:
"""
Returns the concrete Pyomo model corresponding to this instance.
"""
pass

@ -6,7 +6,7 @@ import re
import sys import sys
from io import StringIO from io import StringIO
from random import randint from random import randint
from typing import List, Any, Dict, Union, Tuple, Optional from typing import List, Any, Dict, Optional
from miplearn.instance import Instance from miplearn.instance import Instance
from miplearn.solvers import RedirectOutput from miplearn.solvers import RedirectOutput
@ -17,7 +17,7 @@ from miplearn.solvers.internal import (
LazyCallback, LazyCallback,
MIPSolveStats, MIPSolveStats,
) )
from miplearn.types import VarIndex from miplearn.types import VarIndex, SolverParams, Solution
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -25,9 +25,9 @@ logger = logging.getLogger(__name__)
class GurobiSolver(InternalSolver): class GurobiSolver(InternalSolver):
def __init__( def __init__(
self, self,
params=None, params: Optional[SolverParams] = None,
lazy_cb_frequency=1, lazy_cb_frequency: int = 1,
): ) -> None:
""" """
An InternalSolver backed by Gurobi's Python API (without Pyomo). An InternalSolver backed by Gurobi's Python API (without Pyomo).
@ -41,24 +41,28 @@ class GurobiSolver(InternalSolver):
is found. If 2, calls it also at every node, after solving the is found. If 2, calls it also at every node, after solving the
LP relaxation of that node. LP relaxation of that node.
""" """
import gurobipy
if params is None: if params is None:
params = {} params = {}
params["InfUnbdInfo"] = True params["InfUnbdInfo"] = True
import gurobipy
self.gp = gurobipy self.gp = gurobipy
self.GRB = gurobipy.GRB self.instance: Optional[Instance] = None
self.instance = None self.model: Optional["gurobipy.Model"] = None
self.model = None self.params: SolverParams = params
self.params = params
self._all_vars: Dict = {} self._all_vars: Dict = {}
self._bin_vars = None self._bin_vars: Optional[Dict[str, Dict[VarIndex, "gurobipy.Var"]]] = None
self.cb_where = None self.cb_where: Optional[int] = None
assert lazy_cb_frequency in [1, 2] assert lazy_cb_frequency in [1, 2]
if lazy_cb_frequency == 1: if lazy_cb_frequency == 1:
self.lazy_cb_where = [self.GRB.Callback.MIPSOL] self.lazy_cb_where = [self.gp.GRB.Callback.MIPSOL]
else: else:
self.lazy_cb_where = [self.GRB.Callback.MIPSOL, self.GRB.Callback.MIPNODE] self.lazy_cb_where = [
self.gp.GRB.Callback.MIPSOL,
self.gp.GRB.Callback.MIPNODE,
]
def set_instance( def set_instance(
self, self,
@ -79,9 +83,10 @@ class GurobiSolver(InternalSolver):
raise Exception("method cannot be called from a callback") raise Exception("method cannot be called from a callback")
def _update_vars(self) -> None: def _update_vars(self) -> None:
assert self.model is not None
self._all_vars = {} self._all_vars = {}
self._bin_vars = {} self._bin_vars = {}
idx: Union[Tuple, List[int], int] idx: VarIndex
for var in self.model.getVars(): for var in self.model.getVars():
m = re.search(r"([^[]*)\[(.*)]", var.varName) m = re.search(r"([^[]*)\[(.*)]", var.varName)
if m is None: if m is None:
@ -89,9 +94,8 @@ class GurobiSolver(InternalSolver):
idx = [0] idx = [0]
else: else:
name = m.group(1) name = m.group(1)
idx = tuple( parts = m.group(2).split(",")
int(k) if k.isdecimal() else k for k in m.group(2).split(",") idx = [int(k) if k.isdecimal else k for k in parts]
)
if len(idx) == 1: if len(idx) == 1:
idx = idx[0] idx = idx[0]
if name not in self._all_vars: if name not in self._all_vars:
@ -103,6 +107,7 @@ class GurobiSolver(InternalSolver):
self._bin_vars[name][idx] = var self._bin_vars[name][idx] = var
def _apply_params(self, streams: List[Any]) -> None: def _apply_params(self, streams: List[Any]) -> None:
assert self.model is not None
with RedirectOutput(streams): with RedirectOutput(streams):
for (name, value) in self.params.items(): for (name, value) in self.params.items():
self.model.setParam(name, value) self.model.setParam(name, value)
@ -118,16 +123,18 @@ class GurobiSolver(InternalSolver):
if tee: if tee:
streams += [sys.stdout] streams += [sys.stdout]
self._apply_params(streams) self._apply_params(streams)
assert self.model is not None
assert self._bin_vars is not None
for (varname, vardict) in self._bin_vars.items(): for (varname, vardict) in self._bin_vars.items():
for (idx, var) in vardict.items(): for (idx, var) in vardict.items():
var.vtype = self.GRB.CONTINUOUS var.vtype = self.gp.GRB.CONTINUOUS
var.lb = 0.0 var.lb = 0.0
var.ub = 1.0 var.ub = 1.0
with RedirectOutput(streams): with RedirectOutput(streams):
self.model.optimize() self.model.optimize()
for (varname, vardict) in self._bin_vars.items(): for (varname, vardict) in self._bin_vars.items():
for (idx, var) in vardict.items(): for (idx, var) in vardict.items():
var.vtype = self.GRB.BINARY var.vtype = self.gp.GRB.BINARY
log = streams[0].getvalue() log = streams[0].getvalue()
opt_value = None opt_value = None
if not self.is_infeasible(): if not self.is_infeasible():
@ -144,6 +151,7 @@ class GurobiSolver(InternalSolver):
lazy_cb: LazyCallback = None, lazy_cb: LazyCallback = None,
) -> MIPSolveStats: ) -> MIPSolveStats:
self._raise_if_callback() self._raise_if_callback()
assert self.model is not None
def cb_wrapper(cb_model, cb_where): def cb_wrapper(cb_model, cb_where):
try: try:
@ -199,18 +207,19 @@ class GurobiSolver(InternalSolver):
} }
return stats return stats
def get_solution(self) -> Optional[Dict]: def get_solution(self) -> Optional[Solution]:
self._raise_if_callback() self._raise_if_callback()
assert self.model is not None
if self.model.solCount == 0: if self.model.solCount == 0:
return None return None
solution: Dict = {} solution: Solution = {}
for (varname, vardict) in self._all_vars.items(): for (varname, vardict) in self._all_vars.items():
solution[varname] = {} solution[varname] = {}
for (idx, var) in vardict.items(): for (idx, var) in vardict.items():
solution[varname][idx] = var.x solution[varname][idx] = var.x
return solution return solution
def set_warm_start(self, solution: Dict) -> None: def set_warm_start(self, solution: Solution) -> None:
self._raise_if_callback() self._raise_if_callback()
self._clear_warm_start() self._clear_warm_start()
count_fixed, count_total = 0, 0 count_fixed, count_total = 0, 0
@ -225,20 +234,27 @@ class GurobiSolver(InternalSolver):
% (count_fixed, count_total) % (count_fixed, count_total)
) )
def get_sense(self): def get_sense(self) -> str:
assert self.model is not None
if self.model.modelSense == 1: if self.model.modelSense == 1:
return "min" return "min"
else: else:
return "max" return "max"
def get_value(self, var_name: str, index: VarIndex) -> Optional[float]: def get_value(
self,
var_name: str,
index: VarIndex,
) -> Optional[float]:
var = self._all_vars[var_name][index] var = self._all_vars[var_name][index]
return self._get_value(var) return self._get_value(var)
def is_infeasible(self) -> bool: def is_infeasible(self) -> bool:
return self.model.status in [self.GRB.INFEASIBLE, self.GRB.INF_OR_UNBD] assert self.model is not None
return self.model.status in [self.gp.GRB.INFEASIBLE, self.gp.GRB.INF_OR_UNBD]
def get_dual(self, cid): def get_dual(self, cid: str) -> float:
assert self.model is not None
c = self.model.getConstrByName(cid) c = self.model.getConstrByName(cid)
if self.is_infeasible(): if self.is_infeasible():
return c.farkasDual return c.farkasDual
@ -246,9 +262,10 @@ class GurobiSolver(InternalSolver):
return c.pi return c.pi
def _get_value(self, var: Any) -> Optional[float]: def _get_value(self, var: Any) -> Optional[float]:
if self.cb_where == self.GRB.Callback.MIPSOL: assert self.model is not None
if self.cb_where == self.gp.GRB.Callback.MIPSOL:
return self.model.cbGetSolution(var) return self.model.cbGetSolution(var)
elif self.cb_where == self.GRB.Callback.MIPNODE: elif self.cb_where == self.gp.GRB.Callback.MIPNODE:
return self.model.cbGetNodeRel(var) return self.model.cbGetNodeRel(var)
elif self.cb_where is None: elif self.cb_where is None:
if self.is_infeasible(): if self.is_infeasible():
@ -260,24 +277,35 @@ class GurobiSolver(InternalSolver):
"get_value cannot be called from cb_where=%s" % self.cb_where "get_value cannot be called from cb_where=%s" % self.cb_where
) )
def get_empty_solution(self) -> Dict: def get_empty_solution(self) -> Solution:
self._raise_if_callback() self._raise_if_callback()
solution: Dict = {} solution: Solution = {}
for (varname, vardict) in self._all_vars.items(): for (varname, vardict) in self._all_vars.items():
solution[varname] = {} solution[varname] = {}
for (idx, var) in vardict.items(): for (idx, var) in vardict.items():
solution[varname][idx] = None solution[varname][idx] = None
return solution return solution
def add_constraint(self, constraint, name=""): def add_constraint(
self,
constraint: Any,
name: str = "",
) -> None:
assert self.model is not None
if type(constraint) is tuple: if type(constraint) is tuple:
lhs, sense, rhs, name = constraint lhs, sense, rhs, name = constraint
if self.cb_where in [self.GRB.Callback.MIPSOL, self.GRB.Callback.MIPNODE]: if self.cb_where in [
self.gp.GRB.Callback.MIPSOL,
self.gp.GRB.Callback.MIPNODE,
]:
self.model.cbLazy(lhs, sense, rhs) self.model.cbLazy(lhs, sense, rhs)
else: else:
self.model.addConstr(lhs, sense, rhs, name) self.model.addConstr(lhs, sense, rhs, name)
else: else:
if self.cb_where in [self.GRB.Callback.MIPSOL, self.GRB.Callback.MIPNODE]: if self.cb_where in [
self.gp.GRB.Callback.MIPSOL,
self.gp.GRB.Callback.MIPNODE,
]:
self.model.cbLazy(constraint) self.model.cbLazy(constraint)
else: else:
self.model.addConstr(constraint, name=name) self.model.addConstr(constraint, name=name)
@ -285,16 +313,16 @@ class GurobiSolver(InternalSolver):
def _clear_warm_start(self) -> None: def _clear_warm_start(self) -> None:
for (varname, vardict) in self._all_vars.items(): for (varname, vardict) in self._all_vars.items():
for (idx, var) in vardict.items(): for (idx, var) in vardict.items():
var.start = self.GRB.UNDEFINED var.start = self.gp.GRB.UNDEFINED
def fix(self, solution): def fix(self, solution: Solution) -> None:
self._raise_if_callback() self._raise_if_callback()
for (varname, vardict) in solution.items(): for (varname, vardict) in solution.items():
for (idx, value) in vardict.items(): for (idx, value) in vardict.items():
if value is None: if value is None:
continue continue
var = self._all_vars[varname][idx] var = self._all_vars[varname][idx]
var.vtype = self.GRB.CONTINUOUS var.vtype = self.gp.GRB.CONTINUOUS
var.lb = value var.lb = value
var.ub = value var.ub = value
@ -330,6 +358,7 @@ class GurobiSolver(InternalSolver):
raise Exception("Unknown sense: %s" % sense) raise Exception("Unknown sense: %s" % sense)
def get_inequality_slacks(self) -> Dict[str, float]: def get_inequality_slacks(self) -> Dict[str, float]:
assert self.model is not None
ineqs = [c for c in self.model.getConstrs() if c.sense != "="] ineqs = [c for c in self.model.getConstrs() if c.sense != "="]
return {c.ConstrName: c.Slack for c in ineqs} return {c.ConstrName: c.Slack for c in ineqs}
@ -342,6 +371,7 @@ class GurobiSolver(InternalSolver):
return c.Sense return c.Sense
def relax(self) -> None: def relax(self) -> None:
assert self.model is not None
self.model = self.model.relax() self.model = self.model.relax()
self._update_vars() self._update_vars()
@ -372,11 +402,9 @@ class GurobiSolver(InternalSolver):
} }
def __setstate__(self, state): def __setstate__(self, state):
from gurobipy import GRB
self.params = state["params"] self.params = state["params"]
self.lazy_cb_where = state["lazy_cb_where"] self.lazy_cb_where = state["lazy_cb_where"]
self.GRB = GRB
self.instance = None self.instance = None
self.model = None self.model = None
self._all_vars = None self._all_vars = None

@ -13,6 +13,8 @@ from miplearn.types import (
LazyCallback, LazyCallback,
MIPSolveStats, MIPSolveStats,
VarIndex, VarIndex,
Solution,
BranchPriorities,
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -79,7 +81,7 @@ class InternalSolver(ABC):
pass pass
@abstractmethod @abstractmethod
def get_solution(self) -> Optional[Dict]: def get_solution(self) -> Optional[Solution]:
""" """
Returns current solution found by the solver. Returns current solution found by the solver.
@ -93,7 +95,7 @@ class InternalSolver(ABC):
pass pass
@abstractmethod @abstractmethod
def set_warm_start(self, solution: Dict) -> None: def set_warm_start(self, solution: Solution) -> None:
""" """
Sets the warm start to be used by the solver. Sets the warm start to be used by the solver.
@ -125,7 +127,7 @@ class InternalSolver(ABC):
pass pass
@abstractmethod @abstractmethod
def fix(self, solution: Dict) -> None: def fix(self, solution: Solution) -> None:
""" """
Fixes the values of a subset of decision variables. Fixes the values of a subset of decision variables.
@ -135,7 +137,7 @@ class InternalSolver(ABC):
""" """
pass pass
def set_branching_priorities(self, priorities: Dict) -> None: def set_branching_priorities(self, priorities: BranchPriorities) -> None:
""" """
Sets the branching priorities for the given decision variables. Sets the branching priorities for the given decision variables.
@ -147,7 +149,7 @@ class InternalSolver(ABC):
`get_solution`. Missing values indicate variables whose priorities `get_solution`. Missing values indicate variables whose priorities
should not be modified. should not be modified.
""" """
raise NotImplementedError() raise Exception("Not implemented")
@abstractmethod @abstractmethod
def get_constraint_ids(self) -> List[str]: def get_constraint_ids(self) -> List[str]:

@ -12,6 +12,7 @@ import pyomo
from pyomo import environ as pe from pyomo import environ as pe
from pyomo.core import Var, Constraint from pyomo.core import Var, Constraint
from pyomo.opt import TerminationCondition from pyomo.opt import TerminationCondition
from pyomo.opt.base.solvers import SolverFactory
from miplearn.instance import Instance from miplearn.instance import Instance
from miplearn.solvers import RedirectOutput from miplearn.solvers import RedirectOutput
@ -22,7 +23,7 @@ from miplearn.solvers.internal import (
LazyCallback, LazyCallback,
MIPSolveStats, MIPSolveStats,
) )
from miplearn.types import VarIndex from miplearn.types import VarIndex, SolverParams, Solution
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -34,19 +35,20 @@ class BasePyomoSolver(InternalSolver):
def __init__( def __init__(
self, self,
solver_factory, solver_factory: SolverFactory,
params, params: SolverParams,
): ) -> None:
self.instance = None self.instance: Optional[Instance] = None
self.model = None self.model: Optional[pe.ConcreteModel] = None
self._all_vars = None self._all_vars: List[pe.Var] = []
self._bin_vars = None self._bin_vars: List[pe.Var] = []
self._is_warm_start_available = False self._is_warm_start_available: bool = False
self._pyomo_solver = solver_factory self._pyomo_solver: SolverFactory = solver_factory
self._obj_sense = None self._obj_sense: str = "min"
self._varname_to_var = {} self._varname_to_var: Dict[str, pe.Var] = {}
self._cname_to_constr = {} self._cname_to_constr: Dict[str, pe.Constraint] = {}
self._termination_condition = None self._termination_condition: str = ""
for (key, value) in params.items(): for (key, value) in params.items():
self._pyomo_solver.options[key] = value self._pyomo_solver.options[key] = value
@ -88,8 +90,6 @@ class BasePyomoSolver(InternalSolver):
streams += [sys.stdout] streams += [sys.stdout]
if iteration_cb is None: if iteration_cb is None:
iteration_cb = lambda: False iteration_cb = lambda: False
self.instance.found_violated_lazy_constraints = []
self.instance.found_violated_user_cuts = []
while True: while True:
logger.debug("Solving MIP...") logger.debug("Solving MIP...")
with RedirectOutput(streams): with RedirectOutput(streams):
@ -121,10 +121,11 @@ class BasePyomoSolver(InternalSolver):
} }
return stats return stats
def get_solution(self) -> Optional[Dict]: def get_solution(self) -> Optional[Solution]:
assert self.model is not None
if self.is_infeasible(): if self.is_infeasible():
return None return None
solution: Dict = {} solution: Solution = {}
for var in self.model.component_objects(Var): for var in self.model.component_objects(Var):
solution[str(var)] = {} solution[str(var)] = {}
for index in var: for index in var:
@ -133,7 +134,7 @@ class BasePyomoSolver(InternalSolver):
solution[str(var)][index] = var[index].value solution[str(var)][index] = var[index].value
return solution return solution
def set_warm_start(self, solution: Dict) -> None: def set_warm_start(self, solution: Solution) -> None:
self._clear_warm_start() self._clear_warm_start()
count_total, count_fixed = 0, 0 count_total, count_fixed = 0, 0
for var_name in solution: for var_name in solution:
@ -172,8 +173,9 @@ class BasePyomoSolver(InternalSolver):
var = self._varname_to_var[var_name] var = self._varname_to_var[var_name]
return var[index].value return var[index].value
def get_empty_solution(self) -> Dict: def get_empty_solution(self) -> Solution:
solution: Dict = {} assert self.model is not None
solution: Solution = {}
for var in self.model.component_objects(Var): for var in self.model.component_objects(Var):
svar = str(var) svar = str(var)
solution[svar] = {} solution[svar] = {}
@ -195,6 +197,7 @@ class BasePyomoSolver(InternalSolver):
self._obj_sense = "min" self._obj_sense = "min"
def _update_vars(self) -> None: def _update_vars(self) -> None:
assert self.model is not None
self._all_vars = [] self._all_vars = []
self._bin_vars = [] self._bin_vars = []
self._varname_to_var = {} self._varname_to_var = {}
@ -206,6 +209,7 @@ class BasePyomoSolver(InternalSolver):
self._bin_vars += [var[idx]] self._bin_vars += [var[idx]]
def _update_constrs(self) -> None: def _update_constrs(self) -> None:
assert self.model is not None
self._cname_to_constr = {} self._cname_to_constr = {}
for constr in self.model.component_objects(Constraint): for constr in self.model.component_objects(Constraint):
self._cname_to_constr[constr.name] = constr self._cname_to_constr[constr.name] = constr

@ -1,11 +1,13 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization # MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
from typing import Optional
from pyomo import environ as pe from pyomo import environ as pe
from scipy.stats import randint from scipy.stats import randint
from miplearn.solvers.pyomo.base import BasePyomoSolver from miplearn.solvers.pyomo.base import BasePyomoSolver
from miplearn.types import SolverParams
class CplexPyomoSolver(BasePyomoSolver): class CplexPyomoSolver(BasePyomoSolver):
@ -19,13 +21,19 @@ class CplexPyomoSolver(BasePyomoSolver):
{"mip_display": 5} to increase the log verbosity. {"mip_display": 5} to increase the log verbosity.
""" """
def __init__(self, params=None): def __init__(
self,
params: Optional[SolverParams] = None,
) -> None:
if params is None:
params = {}
if "randomseed" not in params.keys():
params["randomseed"] = randint(low=0, high=1000).rvs()
if "mip_display" not in params.keys():
params["mip_display"] = 4
super().__init__( super().__init__(
solver_factory=pe.SolverFactory("cplex_persistent"), solver_factory=pe.SolverFactory("cplex_persistent"),
params={ params=params,
"randomseed": randint(low=0, high=1000).rvs(),
"mip_display": 4,
},
) )
def _get_warm_start_regexp(self): def _get_warm_start_regexp(self):

@ -3,11 +3,13 @@
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
import logging import logging
from typing import Optional
from pyomo import environ as pe from pyomo import environ as pe
from scipy.stats import randint from scipy.stats import randint
from miplearn.solvers.pyomo.base import BasePyomoSolver from miplearn.solvers.pyomo.base import BasePyomoSolver
from miplearn.types import SolverParams, BranchPriorities
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -23,28 +25,35 @@ class GurobiPyomoSolver(BasePyomoSolver):
{"Threads": 4} to set the number of threads. {"Threads": 4} to set the number of threads.
""" """
def __init__(self, params=None): def __init__(
self,
params: SolverParams = None,
) -> None:
if params is None:
params = {}
if "seed" not in params.keys():
params["seed"] = randint(low=0, high=1000).rvs()
super().__init__( super().__init__(
solver_factory=pe.SolverFactory("gurobi_persistent"), solver_factory=pe.SolverFactory("gurobi_persistent"),
params={ params=params,
"Seed": randint(low=0, high=1000).rvs(),
},
) )
def _extract_node_count(self, log): def _extract_node_count(self, log: str) -> int:
return max(1, int(self._pyomo_solver._solver_model.getAttr("NodeCount"))) return max(1, int(self._pyomo_solver._solver_model.getAttr("NodeCount")))
def _get_warm_start_regexp(self): def _get_warm_start_regexp(self) -> str:
return "MIP start with objective ([0-9.e+-]*)" return "MIP start with objective ([0-9.e+-]*)"
def _get_node_count_regexp(self): def _get_node_count_regexp(self) -> Optional[str]:
return None return None
def set_branching_priorities(self, priorities): def set_branching_priorities(self, priorities: BranchPriorities) -> None:
from gurobipy import GRB from gurobipy import GRB
for varname in priorities.keys(): for varname in priorities.keys():
var = self._varname_to_var[varname] var = self._varname_to_var[varname]
for (index, priority) in priorities[varname].items(): for (index, priority) in priorities[varname].items():
if priority is None:
continue
gvar = self._pyomo_solver._pyomo_var_to_solver_var_map[var[index]] gvar = self._pyomo_solver._pyomo_var_to_solver_var_map[var[index]]
gvar.setAttr(GRB.Attr.BranchPriority, int(round(priority))) gvar.setAttr(GRB.Attr.BranchPriority, int(round(priority)))

@ -8,6 +8,7 @@ from pyomo import environ as pe
from scipy.stats import randint from scipy.stats import randint
from miplearn.solvers.pyomo.base import BasePyomoSolver from miplearn.solvers.pyomo.base import BasePyomoSolver
from miplearn.types import SolverParams
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -23,10 +24,12 @@ class XpressPyomoSolver(BasePyomoSolver):
{"Threads": 4} to set the number of threads. {"Threads": 4} to set the number of threads.
""" """
def __init__(self, params=None): def __init__(self, params: SolverParams = None) -> None:
if params is None:
params = {}
if "randomseed" not in params.keys():
params["randomseed"] = randint(low=0, high=1000).rvs()
super().__init__( super().__init__(
solver_factory=pe.SolverFactory("xpress_persistent"), solver_factory=pe.SolverFactory("xpress_persistent"),
params={ params=params,
"randomseed": randint(low=0, high=1000).rvs(),
},
) )

@ -7,7 +7,7 @@ from typing import List, Callable, Any
from pyomo import environ as pe from pyomo import environ as pe
from miplearn.instance import Instance, PyomoInstance from miplearn.instance import Instance
from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance
from miplearn.solvers.gurobi import GurobiSolver from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.internal import InternalSolver from miplearn.solvers.internal import InternalSolver
@ -16,7 +16,7 @@ from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver
from miplearn.solvers.pyomo.xpress import XpressPyomoSolver from miplearn.solvers.pyomo.xpress import XpressPyomoSolver
class InfeasiblePyomoInstance(PyomoInstance): class InfeasiblePyomoInstance(Instance):
def to_model(self) -> pe.ConcreteModel: def to_model(self) -> pe.ConcreteModel:
model = pe.ConcreteModel() model = pe.ConcreteModel()
model.x = pe.Var([0], domain=pe.Binary) model.x = pe.Var([0], domain=pe.Binary)

@ -6,15 +6,19 @@ from typing import Optional, Dict, Callable, Any, Union, List
from mypy_extensions import TypedDict from mypy_extensions import TypedDict
VarIndex = Union[str, int, List[Union[str, int]]]
Solution = Dict[str, Dict[VarIndex, Optional[float]]]
TrainingSample = TypedDict( TrainingSample = TypedDict(
"TrainingSample", "TrainingSample",
{ {
"LP log": str, "LP log": str,
"LP solution": Optional[Dict], "LP solution": Optional[Solution],
"LP value": Optional[float], "LP value": Optional[float],
"Lower bound": Optional[float], "Lower bound": Optional[float],
"MIP log": str, "MIP log": str,
"Solution": Optional[Dict], "Solution": Optional[Solution],
"Upper bound": Optional[float], "Upper bound": Optional[float],
"slacks": Dict, "slacks": Dict,
}, },
@ -47,4 +51,6 @@ IterationCallback = Callable[[], bool]
LazyCallback = Callable[[Any, Any], None] LazyCallback = Callable[[Any, Any], None]
VarIndex = Union[str, int, List[Union[str, int]]] SolverParams = Dict[str, Any]
BranchPriorities = Solution

Loading…
Cancel
Save