mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Remove unused classes and methods
This commit is contained in:
@@ -12,7 +12,7 @@ from miplearn.classifiers import Classifier
|
||||
from miplearn.classifiers.counting import CountingClassifier
|
||||
from miplearn.classifiers.threshold import MinProbabilityThreshold, Threshold
|
||||
from miplearn.components.component import Component
|
||||
from miplearn.features import Constraint, Sample, ConstraintFeatures
|
||||
from miplearn.features import Sample, ConstraintFeatures
|
||||
from miplearn.instance.base import Instance
|
||||
from miplearn.types import LearningSolveStats
|
||||
|
||||
@@ -45,7 +45,6 @@ class StaticLazyConstraintsComponent(Component):
|
||||
self.threshold_prototype: Threshold = threshold
|
||||
self.classifiers: Dict[Hashable, Classifier] = {}
|
||||
self.thresholds: Dict[Hashable, Threshold] = {}
|
||||
self.pool_old: Dict[str, Constraint] = {}
|
||||
self.pool: ConstraintFeatures = ConstraintFeatures()
|
||||
self.violation_tolerance: float = violation_tolerance
|
||||
self.enforced_cids: Set[Hashable] = set()
|
||||
|
||||
@@ -133,42 +133,6 @@ class ConstraintFeatures:
|
||||
return tuple(obj[i] for (i, selected_i) in enumerate(selected) if selected_i)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Constraint:
|
||||
basis_status: Optional[str] = None
|
||||
category: Optional[Hashable] = None
|
||||
dual_value: Optional[float] = None
|
||||
lazy: bool = False
|
||||
lhs: Optional[Dict[str, float]] = None
|
||||
rhs: float = 0.0
|
||||
sa_rhs_down: Optional[float] = None
|
||||
sa_rhs_up: Optional[float] = None
|
||||
sense: str = "<"
|
||||
slack: Optional[float] = None
|
||||
user_features: Optional[List[float]] = None
|
||||
|
||||
def to_list(self) -> List[float]:
|
||||
features: List[float] = []
|
||||
for attr in [
|
||||
"dual value",
|
||||
"rhs",
|
||||
"sa_rhs_down",
|
||||
"sa_rhs_up",
|
||||
"slack",
|
||||
]:
|
||||
if getattr(self, attr) is not None:
|
||||
features.append(getattr(self, attr))
|
||||
for attr in ["user_features"]:
|
||||
if getattr(self, attr) is not None:
|
||||
features.extend(getattr(self, attr))
|
||||
if self.lhs is not None and len(self.lhs) > 0:
|
||||
features.append(np.max(self.lhs.values()))
|
||||
features.append(np.average(self.lhs.values()))
|
||||
features.append(np.min(self.lhs.values()))
|
||||
_clip(features)
|
||||
return features
|
||||
|
||||
|
||||
@dataclass
|
||||
class Features:
|
||||
instance: Optional[InstanceFeatures] = None
|
||||
|
||||
@@ -10,7 +10,7 @@ from typing import List, Any, Dict, Optional, Hashable, Tuple, TYPE_CHECKING
|
||||
|
||||
from overrides import overrides
|
||||
|
||||
from miplearn.features import Constraint, VariableFeatures, ConstraintFeatures
|
||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
||||
from miplearn.instance.base import Instance
|
||||
from miplearn.solvers import _RedirectOutput
|
||||
from miplearn.solvers.internal import (
|
||||
@@ -81,7 +81,6 @@ class GurobiSolver(InternalSolver):
|
||||
self._var_lbs: Tuple[float, ...] = tuple()
|
||||
self._var_ubs: Tuple[float, ...] = tuple()
|
||||
self._var_obj_coeffs: Tuple[float, ...] = tuple()
|
||||
self._relaxed_constrs: Dict[str, Tuple["gurobipy.LinExpr", str, float]] = {}
|
||||
|
||||
if self.lazy_cb_frequency == 1:
|
||||
self.lazy_cb_where = [self.gp.GRB.Callback.MIPSOL]
|
||||
@@ -156,10 +155,6 @@ class GurobiSolver(InternalSolver):
|
||||
capacity=67.0,
|
||||
)
|
||||
|
||||
@overrides
|
||||
def build_test_instance_redundancy(self) -> Instance:
|
||||
return GurobiTestInstanceRedundancy()
|
||||
|
||||
@overrides
|
||||
def clone(self) -> "GurobiSolver":
|
||||
return GurobiSolver(
|
||||
@@ -167,17 +162,6 @@ class GurobiSolver(InternalSolver):
|
||||
lazy_cb_frequency=self.lazy_cb_frequency,
|
||||
)
|
||||
|
||||
def enforce_constraints(self, names: List[str]) -> None:
|
||||
assert self.model is not None
|
||||
constr = [self._relaxed_constrs[n] for n in names]
|
||||
for (i, (lhs, sense, rhs)) in enumerate(constr):
|
||||
if sense == "=":
|
||||
self.model.addConstr(lhs == rhs, name=names[i])
|
||||
elif sense == "<":
|
||||
self.model.addConstr(lhs <= rhs, name=names[i])
|
||||
else:
|
||||
self.model.addConstr(lhs >= rhs, name=names[i])
|
||||
|
||||
@overrides
|
||||
def fix(self, solution: Solution) -> None:
|
||||
self._raise_if_callback()
|
||||
@@ -385,15 +369,6 @@ class GurobiSolver(InternalSolver):
|
||||
assert self.model is not None
|
||||
return self.model.status in [self.gp.GRB.INFEASIBLE, self.gp.GRB.INF_OR_UNBD]
|
||||
|
||||
def relax_constraints(self, names: List[str]) -> None:
|
||||
assert self.model is not None
|
||||
constrs = [self._cname_to_constr[n] for n in names]
|
||||
for (i, name) in enumerate(names):
|
||||
c = constrs[i]
|
||||
self._relaxed_constrs[name] = self.model.getRow(c), c.sense, c.rhs
|
||||
self.model.remove(constrs)
|
||||
self.model.update()
|
||||
|
||||
@overrides
|
||||
def remove_constraints(self, names: Tuple[str, ...]) -> None:
|
||||
assert self.model is not None
|
||||
@@ -536,13 +511,6 @@ class GurobiSolver(InternalSolver):
|
||||
lp_wallclock_time=self.model.runtime,
|
||||
)
|
||||
|
||||
@overrides
|
||||
def relax(self) -> None:
|
||||
assert self.model is not None
|
||||
self.model.update()
|
||||
self.model = self.model.relax()
|
||||
self._update()
|
||||
|
||||
def _apply_params(self, streams: List[Any]) -> None:
|
||||
assert self.model is not None
|
||||
with _RedirectOutput(streams):
|
||||
@@ -666,20 +634,6 @@ class GurobiTestInstanceInfeasible(Instance):
|
||||
return model
|
||||
|
||||
|
||||
class GurobiTestInstanceRedundancy(Instance):
|
||||
@overrides
|
||||
def to_model(self) -> Any:
|
||||
import gurobipy as gp
|
||||
from gurobipy import GRB
|
||||
|
||||
model = gp.Model()
|
||||
x = model.addVars(2, vtype=GRB.BINARY, name="x")
|
||||
model.addConstr(x[0] + x[1] <= 1, name="c1")
|
||||
model.addConstr(x[0] + x[1] <= 2, name="c2")
|
||||
model.setObjective(x[0] + x[1], GRB.MAXIMIZE)
|
||||
return model
|
||||
|
||||
|
||||
class GurobiTestInstanceKnapsack(PyomoTestInstanceKnapsack):
|
||||
"""
|
||||
Simpler (one-dimensional) knapsack instance, implemented directly in Gurobi
|
||||
|
||||
@@ -5,14 +5,13 @@
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
from miplearn.features import Constraint, VariableFeatures, ConstraintFeatures
|
||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
||||
from miplearn.instance.base import Instance
|
||||
from miplearn.types import (
|
||||
IterationCallback,
|
||||
LazyCallback,
|
||||
BranchPriorities,
|
||||
UserCutCallback,
|
||||
Solution,
|
||||
)
|
||||
@@ -75,6 +74,9 @@ class InternalSolver(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def build_test_instance_infeasible(self) -> Instance:
|
||||
"""
|
||||
Returns an infeasible instance, for testing purposes.
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@@ -89,10 +91,6 @@ class InternalSolver(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def build_test_instance_redundancy(self) -> Instance:
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def clone(self) -> "InternalSolver":
|
||||
"""
|
||||
@@ -184,25 +182,6 @@ class InternalSolver(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def relax(self) -> None:
|
||||
"""
|
||||
Drops all integrality constraints from the model.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_branching_priorities(self, priorities: BranchPriorities) -> None:
|
||||
"""
|
||||
Sets the branching priorities for the given decision variables.
|
||||
|
||||
When the MIP solver needs to decide on which variable to branch, variables
|
||||
with higher priority are picked first, given that they are fractional.
|
||||
Ties are solved arbitrarily. By default, all variables have priority zero.
|
||||
|
||||
Missing values indicate variables whose priorities should not be modified.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abstractmethod
|
||||
def set_instance(
|
||||
self,
|
||||
|
||||
@@ -136,10 +136,6 @@ class BasePyomoSolver(InternalSolver):
|
||||
def build_test_instance_infeasible(self) -> Instance:
|
||||
return PyomoTestInstanceInfeasible()
|
||||
|
||||
@overrides
|
||||
def build_test_instance_redundancy(self) -> Instance:
|
||||
return PyomoTestInstanceRedundancy()
|
||||
|
||||
@overrides
|
||||
def build_test_instance_knapsack(self) -> Instance:
|
||||
return PyomoTestInstanceKnapsack(
|
||||
@@ -490,7 +486,7 @@ class BasePyomoSolver(InternalSolver):
|
||||
self,
|
||||
tee: bool = False,
|
||||
) -> LPSolveStats:
|
||||
self.relax()
|
||||
self._relax()
|
||||
streams: List[Any] = [StringIO()]
|
||||
if tee:
|
||||
streams += [sys.stdout]
|
||||
@@ -510,15 +506,6 @@ class BasePyomoSolver(InternalSolver):
|
||||
lp_wallclock_time=results["Solver"][0]["Wallclock time"],
|
||||
)
|
||||
|
||||
@overrides
|
||||
def relax(self) -> None:
|
||||
for var in self._bin_vars:
|
||||
lb, ub = var.bounds
|
||||
var.setlb(lb)
|
||||
var.setub(ub)
|
||||
var.domain = pyomo.core.base.set_types.Reals
|
||||
self._pyomo_solver.update_var(var)
|
||||
|
||||
def _clear_warm_start(self) -> None:
|
||||
for var in self._all_vars:
|
||||
if not var.fixed:
|
||||
@@ -575,6 +562,14 @@ class BasePyomoSolver(InternalSolver):
|
||||
raise Exception(f"Unknown expression type: {expr.__class__.__name__}")
|
||||
return lhs
|
||||
|
||||
def _relax(self) -> None:
|
||||
for var in self._bin_vars:
|
||||
lb, ub = var.bounds
|
||||
var.setlb(lb)
|
||||
var.setub(ub)
|
||||
var.domain = pyomo.core.base.set_types.Reals
|
||||
self._pyomo_solver.update_var(var)
|
||||
|
||||
def _restore_integrality(self) -> None:
|
||||
for var in self._bin_vars:
|
||||
var.domain = pyomo.core.base.set_types.Binary
|
||||
@@ -624,17 +619,6 @@ class PyomoTestInstanceInfeasible(Instance):
|
||||
return model
|
||||
|
||||
|
||||
class PyomoTestInstanceRedundancy(Instance):
|
||||
@overrides
|
||||
def to_model(self) -> pe.ConcreteModel:
|
||||
model = pe.ConcreteModel()
|
||||
model.x = pe.Var([0, 1], domain=pe.Binary)
|
||||
model.OBJ = pe.Objective(expr=model.x[0] + model.x[1], sense=pe.maximize)
|
||||
model.eq1 = pe.Constraint(expr=model.x[0] + model.x[1] <= 1)
|
||||
model.eq2 = pe.Constraint(expr=model.x[0] + model.x[1] <= 2)
|
||||
return model
|
||||
|
||||
|
||||
class PyomoTestInstanceKnapsack(Instance):
|
||||
"""
|
||||
Simpler (one-dimensional) Knapsack Problem, used for testing.
|
||||
|
||||
@@ -10,7 +10,7 @@ from pyomo import environ as pe
|
||||
from scipy.stats import randint
|
||||
|
||||
from miplearn.solvers.pyomo.base import BasePyomoSolver
|
||||
from miplearn.types import SolverParams, BranchPriorities
|
||||
from miplearn.types import SolverParams
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -42,17 +42,6 @@ class GurobiPyomoSolver(BasePyomoSolver):
|
||||
def clone(self) -> "GurobiPyomoSolver":
|
||||
return GurobiPyomoSolver(params=self.params)
|
||||
|
||||
@overrides
|
||||
def set_branching_priorities(self, priorities: BranchPriorities) -> None:
|
||||
from gurobipy import GRB
|
||||
|
||||
for (varname, priority) in priorities.items():
|
||||
if priority is None:
|
||||
continue
|
||||
var = self._varname_to_var[varname]
|
||||
gvar = self._pyomo_solver._pyomo_var_to_solver_var_map[var]
|
||||
gvar.setAttr(GRB.Attr.BranchPriority, int(round(priority)))
|
||||
|
||||
@overrides
|
||||
def _extract_node_count(self, log: str) -> int:
|
||||
return max(1, int(self._pyomo_solver._solver_model.getAttr("NodeCount")))
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
from typing import Any, Dict, List
|
||||
from typing import Any, List
|
||||
|
||||
from miplearn.features import Constraint, VariableFeatures, ConstraintFeatures
|
||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
||||
from miplearn.solvers.internal import InternalSolver
|
||||
|
||||
inf = float("inf")
|
||||
@@ -13,14 +13,6 @@ inf = float("inf")
|
||||
# This file is in the main source folder, so that it can be called from Julia.
|
||||
|
||||
|
||||
def _round_constraints(constraints: Dict[str, Constraint]) -> Dict[str, Constraint]:
|
||||
for (cname, c) in constraints.items():
|
||||
for attr in ["slack", "dual_value"]:
|
||||
if getattr(c, attr) is not None:
|
||||
setattr(c, attr, round(getattr(c, attr), 6))
|
||||
return constraints
|
||||
|
||||
|
||||
def _round(obj: Any) -> Any:
|
||||
if obj is None:
|
||||
return None
|
||||
@@ -46,20 +38,6 @@ def _filter_attrs(allowed_keys: List[str], obj: Any) -> Any:
|
||||
return obj
|
||||
|
||||
|
||||
def _remove_unsupported_constr_attrs(
|
||||
solver: InternalSolver,
|
||||
constraints: Dict[str, Constraint],
|
||||
) -> Dict[str, Constraint]:
|
||||
for (cname, c) in constraints.items():
|
||||
to_remove = []
|
||||
for k in c.__dict__.keys():
|
||||
if k not in solver.get_constraint_attrs():
|
||||
to_remove.append(k)
|
||||
for k in to_remove:
|
||||
setattr(c, k, None)
|
||||
return constraints
|
||||
|
||||
|
||||
def run_internal_solver_tests(solver: InternalSolver) -> None:
|
||||
run_basic_usage_tests(solver.clone())
|
||||
run_warm_start_tests(solver.clone())
|
||||
|
||||
@@ -10,7 +10,6 @@ if TYPE_CHECKING:
|
||||
# noinspection PyUnresolvedReferences
|
||||
from miplearn.solvers.learning import InternalSolver
|
||||
|
||||
BranchPriorities = Dict[str, Optional[float]]
|
||||
Category = Hashable
|
||||
IterationCallback = Callable[[], bool]
|
||||
LazyCallback = Callable[[Any, Any], None]
|
||||
|
||||
Reference in New Issue
Block a user