mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 01:18:52 -06:00
Rename Variables and Constraints; move to internal.py
This commit is contained in:
@@ -12,7 +12,8 @@ from miplearn.classifiers import Classifier
|
|||||||
from miplearn.classifiers.counting import CountingClassifier
|
from miplearn.classifiers.counting import CountingClassifier
|
||||||
from miplearn.classifiers.threshold import MinProbabilityThreshold, Threshold
|
from miplearn.classifiers.threshold import MinProbabilityThreshold, Threshold
|
||||||
from miplearn.components.component import Component
|
from miplearn.components.component import Component
|
||||||
from miplearn.features import Sample, ConstraintFeatures
|
from miplearn.features import Sample
|
||||||
|
from miplearn.solvers.internal import Constraints
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.types import LearningSolveStats
|
from miplearn.types import LearningSolveStats
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ class StaticLazyConstraintsComponent(Component):
|
|||||||
self.threshold_prototype: Threshold = threshold
|
self.threshold_prototype: Threshold = threshold
|
||||||
self.classifiers: Dict[Hashable, Classifier] = {}
|
self.classifiers: Dict[Hashable, Classifier] = {}
|
||||||
self.thresholds: Dict[Hashable, Threshold] = {}
|
self.thresholds: Dict[Hashable, Threshold] = {}
|
||||||
self.pool: ConstraintFeatures = ConstraintFeatures()
|
self.pool: Constraints = Constraints()
|
||||||
self.violation_tolerance: float = violation_tolerance
|
self.violation_tolerance: float = violation_tolerance
|
||||||
self.enforced_cids: Set[Hashable] = set()
|
self.enforced_cids: Set[Hashable] = set()
|
||||||
self.n_restored: int = 0
|
self.n_restored: int = 0
|
||||||
@@ -82,7 +83,7 @@ class StaticLazyConstraintsComponent(Component):
|
|||||||
logger.info("Instance does not have static lazy constraints. Skipping.")
|
logger.info("Instance does not have static lazy constraints. Skipping.")
|
||||||
self.enforced_cids = set(self.sample_predict(sample))
|
self.enforced_cids = set(self.sample_predict(sample))
|
||||||
logger.info("Moving lazy constraints to the pool...")
|
logger.info("Moving lazy constraints to the pool...")
|
||||||
constraints = ConstraintFeatures.from_sample(sample)
|
constraints = Constraints.from_sample(sample)
|
||||||
assert constraints.lazy is not None
|
assert constraints.lazy is not None
|
||||||
assert constraints.names is not None
|
assert constraints.names is not None
|
||||||
selected = [
|
selected = [
|
||||||
|
|||||||
@@ -4,9 +4,8 @@
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
import numbers
|
import numbers
|
||||||
from dataclasses import dataclass
|
|
||||||
from math import log, isfinite
|
from math import log, isfinite
|
||||||
from typing import TYPE_CHECKING, Dict, Optional, List, Hashable, Tuple, Any
|
from typing import TYPE_CHECKING, Dict, Optional, List, Hashable, Any
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
@@ -15,76 +14,6 @@ if TYPE_CHECKING:
|
|||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class VariableFeatures:
|
|
||||||
names: Optional[List[str]] = None
|
|
||||||
basis_status: Optional[List[str]] = None
|
|
||||||
lower_bounds: Optional[List[float]] = None
|
|
||||||
obj_coeffs: Optional[List[float]] = None
|
|
||||||
reduced_costs: Optional[List[float]] = None
|
|
||||||
sa_lb_down: Optional[List[float]] = None
|
|
||||||
sa_lb_up: Optional[List[float]] = None
|
|
||||||
sa_obj_down: Optional[List[float]] = None
|
|
||||||
sa_obj_up: Optional[List[float]] = None
|
|
||||||
sa_ub_down: Optional[List[float]] = None
|
|
||||||
sa_ub_up: Optional[List[float]] = None
|
|
||||||
types: Optional[List[str]] = None
|
|
||||||
upper_bounds: Optional[List[float]] = None
|
|
||||||
values: Optional[List[float]] = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class ConstraintFeatures:
|
|
||||||
basis_status: Optional[List[str]] = None
|
|
||||||
dual_values: Optional[List[float]] = None
|
|
||||||
lazy: Optional[List[bool]] = None
|
|
||||||
lhs: Optional[List[List[Tuple[str, float]]]] = None
|
|
||||||
names: Optional[List[str]] = None
|
|
||||||
rhs: Optional[List[float]] = None
|
|
||||||
sa_rhs_down: Optional[List[float]] = None
|
|
||||||
sa_rhs_up: Optional[List[float]] = None
|
|
||||||
senses: Optional[List[str]] = None
|
|
||||||
slacks: Optional[List[float]] = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_sample(sample: "Sample") -> "ConstraintFeatures":
|
|
||||||
return ConstraintFeatures(
|
|
||||||
basis_status=sample.get("lp_constr_basis_status"),
|
|
||||||
dual_values=sample.get("lp_constr_dual_values"),
|
|
||||||
lazy=sample.get("constr_lazy"),
|
|
||||||
lhs=sample.get("constr_lhs"),
|
|
||||||
names=sample.get("constr_names"),
|
|
||||||
rhs=sample.get("constr_rhs"),
|
|
||||||
sa_rhs_down=sample.get("lp_constr_sa_rhs_down"),
|
|
||||||
sa_rhs_up=sample.get("lp_constr_sa_rhs_up"),
|
|
||||||
senses=sample.get("constr_senses"),
|
|
||||||
slacks=sample.get("lp_constr_slacks"),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __getitem__(self, selected: List[bool]) -> "ConstraintFeatures":
|
|
||||||
return ConstraintFeatures(
|
|
||||||
basis_status=self._filter(self.basis_status, selected),
|
|
||||||
dual_values=self._filter(self.dual_values, selected),
|
|
||||||
names=self._filter(self.names, selected),
|
|
||||||
lazy=self._filter(self.lazy, selected),
|
|
||||||
lhs=self._filter(self.lhs, selected),
|
|
||||||
rhs=self._filter(self.rhs, selected),
|
|
||||||
sa_rhs_down=self._filter(self.sa_rhs_down, selected),
|
|
||||||
sa_rhs_up=self._filter(self.sa_rhs_up, selected),
|
|
||||||
senses=self._filter(self.senses, selected),
|
|
||||||
slacks=self._filter(self.slacks, selected),
|
|
||||||
)
|
|
||||||
|
|
||||||
def _filter(
|
|
||||||
self,
|
|
||||||
obj: Optional[List],
|
|
||||||
selected: List[bool],
|
|
||||||
) -> Optional[List]:
|
|
||||||
if obj is None:
|
|
||||||
return None
|
|
||||||
return [obj[i] for (i, selected_i) in enumerate(selected) if selected_i]
|
|
||||||
|
|
||||||
|
|
||||||
class Sample:
|
class Sample:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from typing import List, Any, Dict, Optional, Hashable, Tuple, TYPE_CHECKING
|
|||||||
|
|
||||||
from overrides import overrides
|
from overrides import overrides
|
||||||
|
|
||||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.solvers import _RedirectOutput
|
from miplearn.solvers import _RedirectOutput
|
||||||
from miplearn.solvers.internal import (
|
from miplearn.solvers.internal import (
|
||||||
@@ -19,6 +18,8 @@ from miplearn.solvers.internal import (
|
|||||||
IterationCallback,
|
IterationCallback,
|
||||||
LazyCallback,
|
LazyCallback,
|
||||||
MIPSolveStats,
|
MIPSolveStats,
|
||||||
|
Variables,
|
||||||
|
Constraints,
|
||||||
)
|
)
|
||||||
from miplearn.solvers.pyomo.base import PyomoTestInstanceKnapsack
|
from miplearn.solvers.pyomo.base import PyomoTestInstanceKnapsack
|
||||||
from miplearn.types import (
|
from miplearn.types import (
|
||||||
@@ -91,7 +92,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
]
|
]
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def add_constraints(self, cf: ConstraintFeatures) -> None:
|
def add_constraints(self, cf: Constraints) -> None:
|
||||||
assert cf.names is not None
|
assert cf.names is not None
|
||||||
assert cf.senses is not None
|
assert cf.senses is not None
|
||||||
assert cf.lhs is not None
|
assert cf.lhs is not None
|
||||||
@@ -120,7 +121,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
@overrides
|
@overrides
|
||||||
def are_constraints_satisfied(
|
def are_constraints_satisfied(
|
||||||
self,
|
self,
|
||||||
cf: ConstraintFeatures,
|
cf: Constraints,
|
||||||
tol: float = 1e-5,
|
tol: float = 1e-5,
|
||||||
) -> List[bool]:
|
) -> List[bool]:
|
||||||
assert cf.names is not None
|
assert cf.names is not None
|
||||||
@@ -196,7 +197,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
with_static: bool = True,
|
with_static: bool = True,
|
||||||
with_sa: bool = True,
|
with_sa: bool = True,
|
||||||
with_lhs: bool = True,
|
with_lhs: bool = True,
|
||||||
) -> ConstraintFeatures:
|
) -> Constraints:
|
||||||
model = self.model
|
model = self.model
|
||||||
assert model is not None
|
assert model is not None
|
||||||
assert model.numVars == len(self._gp_vars)
|
assert model.numVars == len(self._gp_vars)
|
||||||
@@ -241,7 +242,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
if self._has_lp_solution or self._has_mip_solution:
|
if self._has_lp_solution or self._has_mip_solution:
|
||||||
slacks = model.getAttr("slack", gp_constrs)
|
slacks = model.getAttr("slack", gp_constrs)
|
||||||
|
|
||||||
return ConstraintFeatures(
|
return Constraints(
|
||||||
basis_status=basis_status,
|
basis_status=basis_status,
|
||||||
dual_values=dual_value,
|
dual_values=dual_value,
|
||||||
lhs=lhs,
|
lhs=lhs,
|
||||||
@@ -300,7 +301,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
self,
|
self,
|
||||||
with_static: bool = True,
|
with_static: bool = True,
|
||||||
with_sa: bool = True,
|
with_sa: bool = True,
|
||||||
) -> VariableFeatures:
|
) -> Variables:
|
||||||
model = self.model
|
model = self.model
|
||||||
assert model is not None
|
assert model is not None
|
||||||
|
|
||||||
@@ -347,7 +348,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
if model.solCount > 0:
|
if model.solCount > 0:
|
||||||
values = model.getAttr("x", self._gp_vars)
|
values = model.getAttr("x", self._gp_vars)
|
||||||
|
|
||||||
return VariableFeatures(
|
return Variables(
|
||||||
names=self._var_names,
|
names=self._var_names,
|
||||||
upper_bounds=upper_bounds,
|
upper_bounds=upper_bounds,
|
||||||
lower_bounds=lower_bounds,
|
lower_bounds=lower_bounds,
|
||||||
|
|||||||
@@ -5,9 +5,8 @@
|
|||||||
import logging
|
import logging
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Any, List, Optional, List
|
from typing import Any, Optional, List, Tuple, TYPE_CHECKING
|
||||||
|
|
||||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.types import (
|
from miplearn.types import (
|
||||||
IterationCallback,
|
IterationCallback,
|
||||||
@@ -18,6 +17,9 @@ from miplearn.types import (
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from miplearn.features import Sample
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class LPSolveStats:
|
class LPSolveStats:
|
||||||
@@ -44,20 +46,90 @@ class MIPSolveStats:
|
|||||||
mip_warm_start_value: Optional[float] = None
|
mip_warm_start_value: Optional[float] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Variables:
|
||||||
|
names: Optional[List[str]] = None
|
||||||
|
basis_status: Optional[List[str]] = None
|
||||||
|
lower_bounds: Optional[List[float]] = None
|
||||||
|
obj_coeffs: Optional[List[float]] = None
|
||||||
|
reduced_costs: Optional[List[float]] = None
|
||||||
|
sa_lb_down: Optional[List[float]] = None
|
||||||
|
sa_lb_up: Optional[List[float]] = None
|
||||||
|
sa_obj_down: Optional[List[float]] = None
|
||||||
|
sa_obj_up: Optional[List[float]] = None
|
||||||
|
sa_ub_down: Optional[List[float]] = None
|
||||||
|
sa_ub_up: Optional[List[float]] = None
|
||||||
|
types: Optional[List[str]] = None
|
||||||
|
upper_bounds: Optional[List[float]] = None
|
||||||
|
values: Optional[List[float]] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Constraints:
|
||||||
|
basis_status: Optional[List[str]] = None
|
||||||
|
dual_values: Optional[List[float]] = None
|
||||||
|
lazy: Optional[List[bool]] = None
|
||||||
|
lhs: Optional[List[List[Tuple[str, float]]]] = None
|
||||||
|
names: Optional[List[str]] = None
|
||||||
|
rhs: Optional[List[float]] = None
|
||||||
|
sa_rhs_down: Optional[List[float]] = None
|
||||||
|
sa_rhs_up: Optional[List[float]] = None
|
||||||
|
senses: Optional[List[str]] = None
|
||||||
|
slacks: Optional[List[float]] = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_sample(sample: "Sample") -> "Constraints":
|
||||||
|
return Constraints(
|
||||||
|
basis_status=sample.get("lp_constr_basis_status"),
|
||||||
|
dual_values=sample.get("lp_constr_dual_values"),
|
||||||
|
lazy=sample.get("constr_lazy"),
|
||||||
|
lhs=sample.get("constr_lhs"),
|
||||||
|
names=sample.get("constr_names"),
|
||||||
|
rhs=sample.get("constr_rhs"),
|
||||||
|
sa_rhs_down=sample.get("lp_constr_sa_rhs_down"),
|
||||||
|
sa_rhs_up=sample.get("lp_constr_sa_rhs_up"),
|
||||||
|
senses=sample.get("constr_senses"),
|
||||||
|
slacks=sample.get("lp_constr_slacks"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __getitem__(self, selected: List[bool]) -> "Constraints":
|
||||||
|
return Constraints(
|
||||||
|
basis_status=self._filter(self.basis_status, selected),
|
||||||
|
dual_values=self._filter(self.dual_values, selected),
|
||||||
|
names=self._filter(self.names, selected),
|
||||||
|
lazy=self._filter(self.lazy, selected),
|
||||||
|
lhs=self._filter(self.lhs, selected),
|
||||||
|
rhs=self._filter(self.rhs, selected),
|
||||||
|
sa_rhs_down=self._filter(self.sa_rhs_down, selected),
|
||||||
|
sa_rhs_up=self._filter(self.sa_rhs_up, selected),
|
||||||
|
senses=self._filter(self.senses, selected),
|
||||||
|
slacks=self._filter(self.slacks, selected),
|
||||||
|
)
|
||||||
|
|
||||||
|
def _filter(
|
||||||
|
self,
|
||||||
|
obj: Optional[List],
|
||||||
|
selected: List[bool],
|
||||||
|
) -> Optional[List]:
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
return [obj[i] for (i, selected_i) in enumerate(selected) if selected_i]
|
||||||
|
|
||||||
|
|
||||||
class InternalSolver(ABC):
|
class InternalSolver(ABC):
|
||||||
"""
|
"""
|
||||||
Abstract class representing the MIP solver used internally by LearningSolver.
|
Abstract class representing the MIP solver used internally by LearningSolver.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def add_constraints(self, cf: ConstraintFeatures) -> None:
|
def add_constraints(self, cf: Constraints) -> None:
|
||||||
"""Adds the given constraints to the model."""
|
"""Adds the given constraints to the model."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def are_constraints_satisfied(
|
def are_constraints_satisfied(
|
||||||
self,
|
self,
|
||||||
cf: ConstraintFeatures,
|
cf: Constraints,
|
||||||
tol: float = 1e-5,
|
tol: float = 1e-5,
|
||||||
) -> List[bool]:
|
) -> List[bool]:
|
||||||
"""
|
"""
|
||||||
@@ -133,7 +205,7 @@ class InternalSolver(ABC):
|
|||||||
with_static: bool = True,
|
with_static: bool = True,
|
||||||
with_sa: bool = True,
|
with_sa: bool = True,
|
||||||
with_lhs: bool = True,
|
with_lhs: bool = True,
|
||||||
) -> ConstraintFeatures:
|
) -> Constraints:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
@@ -149,7 +221,7 @@ class InternalSolver(ABC):
|
|||||||
self,
|
self,
|
||||||
with_static: bool = True,
|
with_static: bool = True,
|
||||||
with_sa: bool = True,
|
with_sa: bool = True,
|
||||||
) -> VariableFeatures:
|
) -> Variables:
|
||||||
"""
|
"""
|
||||||
Returns a description of the decision variables in the problem.
|
Returns a description of the decision variables in the problem.
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ from pyomo.core.expr.numeric_expr import SumExpression, MonomialTermExpression
|
|||||||
from pyomo.opt import TerminationCondition
|
from pyomo.opt import TerminationCondition
|
||||||
from pyomo.opt.base.solvers import SolverFactory
|
from pyomo.opt.base.solvers import SolverFactory
|
||||||
|
|
||||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.solvers import _RedirectOutput, _none_if_empty
|
from miplearn.solvers import _RedirectOutput, _none_if_empty
|
||||||
from miplearn.solvers.internal import (
|
from miplearn.solvers.internal import (
|
||||||
@@ -28,6 +27,8 @@ from miplearn.solvers.internal import (
|
|||||||
IterationCallback,
|
IterationCallback,
|
||||||
LazyCallback,
|
LazyCallback,
|
||||||
MIPSolveStats,
|
MIPSolveStats,
|
||||||
|
Variables,
|
||||||
|
Constraints,
|
||||||
)
|
)
|
||||||
from miplearn.types import (
|
from miplearn.types import (
|
||||||
SolverParams,
|
SolverParams,
|
||||||
@@ -79,7 +80,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
self._has_mip_solution = False
|
self._has_mip_solution = False
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def add_constraints(self, cf: ConstraintFeatures) -> None:
|
def add_constraints(self, cf: Constraints) -> None:
|
||||||
assert cf.names is not None
|
assert cf.names is not None
|
||||||
assert cf.senses is not None
|
assert cf.senses is not None
|
||||||
assert cf.lhs is not None
|
assert cf.lhs is not None
|
||||||
@@ -111,7 +112,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
@overrides
|
@overrides
|
||||||
def are_constraints_satisfied(
|
def are_constraints_satisfied(
|
||||||
self,
|
self,
|
||||||
cf: ConstraintFeatures,
|
cf: Constraints,
|
||||||
tol: float = 1e-5,
|
tol: float = 1e-5,
|
||||||
) -> List[bool]:
|
) -> List[bool]:
|
||||||
assert cf.names is not None
|
assert cf.names is not None
|
||||||
@@ -159,7 +160,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
with_static: bool = True,
|
with_static: bool = True,
|
||||||
with_sa: bool = True,
|
with_sa: bool = True,
|
||||||
with_lhs: bool = True,
|
with_lhs: bool = True,
|
||||||
) -> ConstraintFeatures:
|
) -> Constraints:
|
||||||
model = self.model
|
model = self.model
|
||||||
assert model is not None
|
assert model is not None
|
||||||
|
|
||||||
@@ -233,7 +234,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
names.append(constr.name)
|
names.append(constr.name)
|
||||||
_parse_constraint(constr)
|
_parse_constraint(constr)
|
||||||
|
|
||||||
return ConstraintFeatures(
|
return Constraints(
|
||||||
names=_none_if_empty(names),
|
names=_none_if_empty(names),
|
||||||
rhs=_none_if_empty(rhs),
|
rhs=_none_if_empty(rhs),
|
||||||
senses=_none_if_empty(senses),
|
senses=_none_if_empty(senses),
|
||||||
@@ -271,7 +272,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
self,
|
self,
|
||||||
with_static: bool = True,
|
with_static: bool = True,
|
||||||
with_sa: bool = True,
|
with_sa: bool = True,
|
||||||
) -> VariableFeatures:
|
) -> Variables:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
|
|
||||||
names: List[str] = []
|
names: List[str] = []
|
||||||
@@ -326,7 +327,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
if self._has_lp_solution or self._has_mip_solution:
|
if self._has_lp_solution or self._has_mip_solution:
|
||||||
values.append(v.value)
|
values.append(v.value)
|
||||||
|
|
||||||
return VariableFeatures(
|
return Variables(
|
||||||
names=_none_if_empty(names),
|
names=_none_if_empty(names),
|
||||||
types=_none_if_empty(types),
|
types=_none_if_empty(types),
|
||||||
upper_bounds=_none_if_empty(upper_bounds),
|
upper_bounds=_none_if_empty(upper_bounds),
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ from typing import Any, List
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from miplearn.features import VariableFeatures, ConstraintFeatures
|
from miplearn.solvers.internal import InternalSolver, Variables, Constraints
|
||||||
from miplearn.solvers.internal import InternalSolver
|
|
||||||
|
|
||||||
inf = float("inf")
|
inf = float("inf")
|
||||||
|
|
||||||
@@ -40,7 +39,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
# Fetch variables (after-load)
|
# Fetch variables (after-load)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
solver.get_variables(),
|
solver.get_variables(),
|
||||||
VariableFeatures(
|
Variables(
|
||||||
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
||||||
lower_bounds=[0.0, 0.0, 0.0, 0.0, 0.0],
|
lower_bounds=[0.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
upper_bounds=[1.0, 1.0, 1.0, 1.0, 67.0],
|
upper_bounds=[1.0, 1.0, 1.0, 1.0, 67.0],
|
||||||
@@ -52,7 +51,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
# Fetch constraints (after-load)
|
# Fetch constraints (after-load)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
solver.get_constraints(),
|
solver.get_constraints(),
|
||||||
ConstraintFeatures(
|
Constraints(
|
||||||
names=["eq_capacity"],
|
names=["eq_capacity"],
|
||||||
rhs=[0.0],
|
rhs=[0.0],
|
||||||
lhs=[
|
lhs=[
|
||||||
@@ -83,7 +82,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
solver.get_variables(with_static=False),
|
solver.get_variables(with_static=False),
|
||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_variable_attrs(),
|
solver.get_variable_attrs(),
|
||||||
VariableFeatures(
|
Variables(
|
||||||
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
||||||
basis_status=["U", "B", "U", "L", "U"],
|
basis_status=["U", "B", "U", "L", "U"],
|
||||||
reduced_costs=[193.615385, 0.0, 187.230769, -23.692308, 13.538462],
|
reduced_costs=[193.615385, 0.0, 187.230769, -23.692308, 13.538462],
|
||||||
@@ -103,7 +102,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
solver.get_constraints(with_static=False),
|
solver.get_constraints(with_static=False),
|
||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_constraint_attrs(),
|
solver.get_constraint_attrs(),
|
||||||
ConstraintFeatures(
|
Constraints(
|
||||||
basis_status=["N"],
|
basis_status=["N"],
|
||||||
dual_values=[13.538462],
|
dual_values=[13.538462],
|
||||||
names=["eq_capacity"],
|
names=["eq_capacity"],
|
||||||
@@ -136,7 +135,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
solver.get_variables(with_static=False),
|
solver.get_variables(with_static=False),
|
||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_variable_attrs(),
|
solver.get_variable_attrs(),
|
||||||
VariableFeatures(
|
Variables(
|
||||||
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
names=["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
||||||
values=[1.0, 0.0, 1.0, 1.0, 61.0],
|
values=[1.0, 0.0, 1.0, 1.0, 61.0],
|
||||||
),
|
),
|
||||||
@@ -148,7 +147,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
solver.get_constraints(with_static=False),
|
solver.get_constraints(with_static=False),
|
||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_constraint_attrs(),
|
solver.get_constraint_attrs(),
|
||||||
ConstraintFeatures(
|
Constraints(
|
||||||
names=["eq_capacity"],
|
names=["eq_capacity"],
|
||||||
slacks=[0.0],
|
slacks=[0.0],
|
||||||
),
|
),
|
||||||
@@ -156,7 +155,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Build new constraint and verify that it is violated
|
# Build new constraint and verify that it is violated
|
||||||
cf = ConstraintFeatures(
|
cf = Constraints(
|
||||||
names=["cut"],
|
names=["cut"],
|
||||||
lhs=[[("x[0]", 1.0)]],
|
lhs=[[("x[0]", 1.0)]],
|
||||||
rhs=[0.0],
|
rhs=[0.0],
|
||||||
@@ -170,7 +169,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
solver.get_constraints(with_static=True),
|
solver.get_constraints(with_static=True),
|
||||||
_filter_attrs(
|
_filter_attrs(
|
||||||
solver.get_constraint_attrs(),
|
solver.get_constraint_attrs(),
|
||||||
ConstraintFeatures(
|
Constraints(
|
||||||
names=["eq_capacity", "cut"],
|
names=["eq_capacity", "cut"],
|
||||||
rhs=[0.0, 0.0],
|
rhs=[0.0, 0.0],
|
||||||
lhs=[
|
lhs=[
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ from numpy.testing import assert_array_equal
|
|||||||
from miplearn.classifiers import Classifier
|
from miplearn.classifiers import Classifier
|
||||||
from miplearn.classifiers.threshold import Threshold, MinProbabilityThreshold
|
from miplearn.classifiers.threshold import Threshold, MinProbabilityThreshold
|
||||||
from miplearn.components.static_lazy import StaticLazyConstraintsComponent
|
from miplearn.components.static_lazy import StaticLazyConstraintsComponent
|
||||||
from miplearn.features import Sample, ConstraintFeatures
|
from miplearn.features import Sample
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.solvers.internal import InternalSolver
|
from miplearn.solvers.internal import InternalSolver, Constraints
|
||||||
from miplearn.solvers.learning import LearningSolver
|
from miplearn.solvers.learning import LearningSolver
|
||||||
from miplearn.types import (
|
from miplearn.types import (
|
||||||
LearningSolveStats,
|
LearningSolveStats,
|
||||||
@@ -118,7 +118,7 @@ def test_usage_with_solver(instance: Instance) -> None:
|
|||||||
|
|
||||||
# Should ask internal solver to verify if constraints in the pool are
|
# Should ask internal solver to verify if constraints in the pool are
|
||||||
# satisfied and add the ones that are not
|
# satisfied and add the ones that are not
|
||||||
c = ConstraintFeatures.from_sample(sample)[[False, False, True, False, False]]
|
c = Constraints.from_sample(sample)[[False, False, True, False, False]]
|
||||||
internal.are_constraints_satisfied.assert_called_once_with(c, tol=1.0)
|
internal.are_constraints_satisfied.assert_called_once_with(c, tol=1.0)
|
||||||
internal.are_constraints_satisfied.reset_mock()
|
internal.are_constraints_satisfied.reset_mock()
|
||||||
internal.add_constraints.assert_called_once_with(c)
|
internal.add_constraints.assert_called_once_with(c)
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ import numpy as np
|
|||||||
|
|
||||||
from miplearn.features import (
|
from miplearn.features import (
|
||||||
FeaturesExtractor,
|
FeaturesExtractor,
|
||||||
VariableFeatures,
|
|
||||||
ConstraintFeatures,
|
|
||||||
Sample,
|
Sample,
|
||||||
)
|
)
|
||||||
|
from miplearn.solvers.internal import Variables, Constraints
|
||||||
from miplearn.solvers.gurobi import GurobiSolver
|
from miplearn.solvers.gurobi import GurobiSolver
|
||||||
from miplearn.solvers.tests import assert_equals
|
from miplearn.solvers.tests import assert_equals
|
||||||
|
|
||||||
@@ -129,7 +128,7 @@ def test_knapsack() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_constraint_getindex() -> None:
|
def test_constraint_getindex() -> None:
|
||||||
cf = ConstraintFeatures(
|
cf = Constraints(
|
||||||
names=["c1", "c2", "c3"],
|
names=["c1", "c2", "c3"],
|
||||||
rhs=[1.0, 2.0, 3.0],
|
rhs=[1.0, 2.0, 3.0],
|
||||||
senses=["=", "<", ">"],
|
senses=["=", "<", ">"],
|
||||||
@@ -150,7 +149,7 @@ def test_constraint_getindex() -> None:
|
|||||||
)
|
)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
cf[[True, False, True]],
|
cf[[True, False, True]],
|
||||||
ConstraintFeatures(
|
Constraints(
|
||||||
names=["c1", "c3"],
|
names=["c1", "c3"],
|
||||||
rhs=[1.0, 3.0],
|
rhs=[1.0, 3.0],
|
||||||
senses=["=", ">"],
|
senses=["=", ">"],
|
||||||
@@ -177,8 +176,8 @@ def test_assert_equals() -> None:
|
|||||||
np.array([[1.0, 2.0], [3.0, 4.0]]),
|
np.array([[1.0, 2.0], [3.0, 4.0]]),
|
||||||
)
|
)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
VariableFeatures(values=np.array([1.0, 2.0])), # type: ignore
|
Variables(values=np.array([1.0, 2.0])), # type: ignore
|
||||||
VariableFeatures(values=np.array([1.0, 2.0])), # type: ignore
|
Variables(values=np.array([1.0, 2.0])), # type: ignore
|
||||||
)
|
)
|
||||||
assert_equals(np.array([True, True]), [True, True])
|
assert_equals(np.array([True, True]), [True, True])
|
||||||
assert_equals((1.0,), (1.0,))
|
assert_equals((1.0,), (1.0,))
|
||||||
|
|||||||
Reference in New Issue
Block a user