mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 01:18:52 -06:00
Pyomo: Collect variable bounds, obj_coeff, value, type
This commit is contained in:
@@ -26,6 +26,7 @@ from miplearn.types import (
|
|||||||
UserCutCallback,
|
UserCutCallback,
|
||||||
Solution,
|
Solution,
|
||||||
VariableName,
|
VariableName,
|
||||||
|
Category,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -67,6 +68,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
self.lazy_cb_frequency = lazy_cb_frequency
|
self.lazy_cb_frequency = lazy_cb_frequency
|
||||||
self._bin_vars: List["gurobipy.Var"] = []
|
self._bin_vars: List["gurobipy.Var"] = []
|
||||||
self._varname_to_var: Dict[str, "gurobipy.Var"] = {}
|
self._varname_to_var: Dict[str, "gurobipy.Var"] = {}
|
||||||
|
self._original_vtype: Dict["gurobipy.Var", str] = {}
|
||||||
self._dirty = True
|
self._dirty = True
|
||||||
self._has_lp_solution = False
|
self._has_lp_solution = False
|
||||||
self._has_mip_solution = False
|
self._has_mip_solution = False
|
||||||
@@ -101,6 +103,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
def _update_vars(self) -> None:
|
def _update_vars(self) -> None:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
self._varname_to_var.clear()
|
self._varname_to_var.clear()
|
||||||
|
self._original_vtype = {}
|
||||||
self._bin_vars.clear()
|
self._bin_vars.clear()
|
||||||
for var in self.model.getVars():
|
for var in self.model.getVars():
|
||||||
assert var.varName not in self._varname_to_var, (
|
assert var.varName not in self._varname_to_var, (
|
||||||
@@ -112,6 +115,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
"Only binary and continuous variables are currently supported. "
|
"Only binary and continuous variables are currently supported. "
|
||||||
"Variable {var.varName} has type {var.vtype}."
|
"Variable {var.varName} has type {var.vtype}."
|
||||||
)
|
)
|
||||||
|
self._original_vtype[var] = var.vtype
|
||||||
if var.vtype == "B":
|
if var.vtype == "B":
|
||||||
self._bin_vars.append(var)
|
self._bin_vars.append(var)
|
||||||
|
|
||||||
@@ -433,7 +437,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
var.lower_bound = gp_var.lb
|
var.lower_bound = gp_var.lb
|
||||||
var.upper_bound = gp_var.ub
|
var.upper_bound = gp_var.ub
|
||||||
var.obj_coeff = gp_var.obj
|
var.obj_coeff = gp_var.obj
|
||||||
var.type = gp_var.vtype
|
var.type = self._original_vtype[gp_var]
|
||||||
|
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
var.reduced_cost = gp_var.rc
|
var.reduced_cost = gp_var.rc
|
||||||
@@ -587,8 +591,9 @@ class GurobiTestInstanceKnapsack(PyomoTestInstanceKnapsack):
|
|||||||
model = gp.Model("Knapsack")
|
model = gp.Model("Knapsack")
|
||||||
n = len(self.weights)
|
n = len(self.weights)
|
||||||
x = model.addVars(n, vtype=GRB.BINARY, name="x")
|
x = model.addVars(n, vtype=GRB.BINARY, name="x")
|
||||||
|
z = model.addVar(vtype=GRB.CONTINUOUS, name="z", ub=self.capacity)
|
||||||
model.addConstr(
|
model.addConstr(
|
||||||
gp.quicksum(x[i] * self.weights[i] for i in range(n)) <= self.capacity,
|
gp.quicksum(x[i] * self.weights[i] for i in range(n)) == z,
|
||||||
"eq_capacity",
|
"eq_capacity",
|
||||||
)
|
)
|
||||||
model.setObjective(
|
model.setObjective(
|
||||||
|
|||||||
@@ -253,7 +253,6 @@ class LearningSolver:
|
|||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
logger.info("Extracting features (after-mip)...")
|
logger.info("Extracting features (after-mip)...")
|
||||||
features = FeaturesExtractor(self.internal_solver).extract(instance)
|
features = FeaturesExtractor(self.internal_solver).extract(instance)
|
||||||
features.lp_solve = lp_stats
|
|
||||||
features.mip_solve = mip_stats
|
features.mip_solve = mip_stats
|
||||||
instance.features_after_mip.append(features)
|
instance.features_after_mip.append(features)
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import numpy as np
|
|||||||
import pyomo
|
import pyomo
|
||||||
from overrides import overrides
|
from overrides import overrides
|
||||||
from pyomo import environ as pe
|
from pyomo import environ as pe
|
||||||
from pyomo.core import Var, Suffix
|
from pyomo.core import Var, Suffix, Objective
|
||||||
from pyomo.core.base import _GeneralVarData
|
from pyomo.core.base import _GeneralVarData
|
||||||
from pyomo.core.base.constraint import ConstraintList
|
from pyomo.core.base.constraint import ConstraintList
|
||||||
from pyomo.core.expr.numeric_expr import SumExpression, MonomialTermExpression
|
from pyomo.core.expr.numeric_expr import SumExpression, MonomialTermExpression
|
||||||
@@ -64,6 +64,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
self._termination_condition: str = ""
|
self._termination_condition: str = ""
|
||||||
self._has_lp_solution = False
|
self._has_lp_solution = False
|
||||||
self._has_mip_solution = False
|
self._has_mip_solution = False
|
||||||
|
self._obj: Dict[str, float] = {}
|
||||||
|
|
||||||
for (key, value) in params.items():
|
for (key, value) in params.items():
|
||||||
self._pyomo_solver.options[key] = value
|
self._pyomo_solver.options[key] = value
|
||||||
@@ -223,6 +224,9 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
self._all_vars += [var[idx]]
|
self._all_vars += [var[idx]]
|
||||||
if var[idx].domain == pyomo.core.base.set_types.Binary:
|
if var[idx].domain == pyomo.core.base.set_types.Binary:
|
||||||
self._bin_vars += [var[idx]]
|
self._bin_vars += [var[idx]]
|
||||||
|
for obj in self.model.component_objects(Objective):
|
||||||
|
self._obj = self._parse_pyomo_expr(obj.expr)
|
||||||
|
break
|
||||||
|
|
||||||
def _update_constrs(self) -> None:
|
def _update_constrs(self) -> None:
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
@@ -371,11 +375,42 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
for var in self.model.component_objects(pyomo.core.Var):
|
for var in self.model.component_objects(pyomo.core.Var):
|
||||||
for idx in var:
|
for idx in var:
|
||||||
varname = f"{var}[{idx}]"
|
varname = f"{var}[{idx}]"
|
||||||
|
if idx is None:
|
||||||
|
varname = str(var)
|
||||||
variables[varname] = self._parse_pyomo_variable(var[idx])
|
variables[varname] = self._parse_pyomo_variable(var[idx])
|
||||||
return variables
|
return variables
|
||||||
|
|
||||||
def _parse_pyomo_variable(self, var: pyomo.core.Var) -> Variable:
|
def _parse_pyomo_variable(self, var: pyomo.core.Var) -> Variable:
|
||||||
return Variable()
|
# Variable type
|
||||||
|
vtype: Optional[str] = None
|
||||||
|
if var.domain == pyomo.core.Binary:
|
||||||
|
vtype = "B"
|
||||||
|
elif var.domain in [
|
||||||
|
pyomo.core.Reals,
|
||||||
|
pyomo.core.NonNegativeReals,
|
||||||
|
pyomo.core.NonPositiveReals,
|
||||||
|
pyomo.core.NegativeReals,
|
||||||
|
pyomo.core.PositiveReals,
|
||||||
|
]:
|
||||||
|
vtype = "C"
|
||||||
|
if vtype is None:
|
||||||
|
raise Exception(f"unknown variable domain: {var.domain}")
|
||||||
|
|
||||||
|
# Bounds
|
||||||
|
lb, ub = var.bounds
|
||||||
|
|
||||||
|
# Objective coefficient
|
||||||
|
obj_coeff = 0.0
|
||||||
|
if var.name in self._obj:
|
||||||
|
obj_coeff = self._obj[var.name]
|
||||||
|
|
||||||
|
return Variable(
|
||||||
|
value=var.value,
|
||||||
|
type=vtype,
|
||||||
|
lower_bound=float(lb),
|
||||||
|
upper_bound=float(ub),
|
||||||
|
obj_coeff=obj_coeff,
|
||||||
|
)
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def get_constraints(self) -> Dict[str, Constraint]:
|
def get_constraints(self) -> Dict[str, Constraint]:
|
||||||
@@ -408,10 +443,10 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
assert (
|
assert (
|
||||||
(not has_lb) or (not has_ub) or pyomo_constr.upper() == pyomo_constr.lower()
|
(not has_lb) or (not has_ub) or pyomo_constr.upper() == pyomo_constr.lower()
|
||||||
), "range constraints not supported"
|
), "range constraints not supported"
|
||||||
if has_lb:
|
if not has_ub:
|
||||||
constr.sense = ">"
|
constr.sense = ">"
|
||||||
constr.rhs = pyomo_constr.lower()
|
constr.rhs = pyomo_constr.lower()
|
||||||
elif has_ub:
|
elif not has_lb:
|
||||||
constr.sense = "<"
|
constr.sense = "<"
|
||||||
constr.rhs = pyomo_constr.upper()
|
constr.rhs = pyomo_constr.upper()
|
||||||
else:
|
else:
|
||||||
@@ -419,22 +454,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
constr.rhs = pyomo_constr.upper()
|
constr.rhs = pyomo_constr.upper()
|
||||||
|
|
||||||
# Extract LHS
|
# Extract LHS
|
||||||
lhs = {}
|
constr.lhs = self._parse_pyomo_expr(pyomo_constr.body)
|
||||||
if isinstance(pyomo_constr.body, SumExpression):
|
|
||||||
for term in pyomo_constr.body._args_:
|
|
||||||
if isinstance(term, MonomialTermExpression):
|
|
||||||
lhs[term._args_[1].name] = term._args_[0]
|
|
||||||
elif isinstance(term, _GeneralVarData):
|
|
||||||
lhs[term.name] = 1.0
|
|
||||||
else:
|
|
||||||
raise Exception(f"Unknown term type: {term.__class__.__name__}")
|
|
||||||
elif isinstance(pyomo_constr.body, _GeneralVarData):
|
|
||||||
lhs[pyomo_constr.body.name] = 1.0
|
|
||||||
else:
|
|
||||||
raise Exception(
|
|
||||||
f"Unknown expression type: {pyomo_constr.body.__class__.__name__}"
|
|
||||||
)
|
|
||||||
constr.lhs = lhs
|
|
||||||
|
|
||||||
# Extract solution attributes
|
# Extract solution attributes
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
@@ -446,6 +466,22 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
# Build constraint
|
# Build constraint
|
||||||
return constr
|
return constr
|
||||||
|
|
||||||
|
def _parse_pyomo_expr(self, expr):
|
||||||
|
lhs = {}
|
||||||
|
if isinstance(expr, SumExpression):
|
||||||
|
for term in expr._args_:
|
||||||
|
if isinstance(term, MonomialTermExpression):
|
||||||
|
lhs[term._args_[1].name] = float(term._args_[0])
|
||||||
|
elif isinstance(term, _GeneralVarData):
|
||||||
|
lhs[term.name] = 1.0
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unknown term type: {term.__class__.__name__}")
|
||||||
|
elif isinstance(expr, _GeneralVarData):
|
||||||
|
lhs[expr.name] = 1.0
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unknown expression type: {expr.__class__.__name__}")
|
||||||
|
return lhs
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def are_callbacks_supported(self) -> bool:
|
def are_callbacks_supported(self) -> bool:
|
||||||
return False
|
return False
|
||||||
@@ -453,23 +489,20 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
@overrides
|
@overrides
|
||||||
def get_constraint_attrs(self) -> List[str]:
|
def get_constraint_attrs(self) -> List[str]:
|
||||||
return [
|
return [
|
||||||
"category",
|
|
||||||
"dual_value",
|
"dual_value",
|
||||||
"lazy",
|
"lazy",
|
||||||
"lhs",
|
"lhs",
|
||||||
"rhs",
|
"rhs",
|
||||||
"sense",
|
"sense",
|
||||||
"slack",
|
"slack",
|
||||||
"user_features",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
def get_variable_attrs(self) -> List[str]:
|
def get_variable_attrs(self) -> List[str]:
|
||||||
return [
|
return [
|
||||||
# "basis_status",
|
# "basis_status",
|
||||||
# "category",
|
"lower_bound",
|
||||||
# "lower_bound",
|
"obj_coeff",
|
||||||
# "obj_coeff",
|
|
||||||
# "reduced_cost",
|
# "reduced_cost",
|
||||||
# "sa_lb_down",
|
# "sa_lb_down",
|
||||||
# "sa_lb_up",
|
# "sa_lb_up",
|
||||||
@@ -477,10 +510,9 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
# "sa_obj_up",
|
# "sa_obj_up",
|
||||||
# "sa_ub_down",
|
# "sa_ub_down",
|
||||||
# "sa_ub_up",
|
# "sa_ub_up",
|
||||||
# "type",
|
"type",
|
||||||
# "upper_bound",
|
"upper_bound",
|
||||||
# "user_features",
|
"value",
|
||||||
# "value",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@@ -529,12 +561,13 @@ class PyomoTestInstanceKnapsack(Instance):
|
|||||||
model = pe.ConcreteModel()
|
model = pe.ConcreteModel()
|
||||||
items = range(len(self.weights))
|
items = range(len(self.weights))
|
||||||
model.x = pe.Var(items, domain=pe.Binary)
|
model.x = pe.Var(items, domain=pe.Binary)
|
||||||
|
model.z = pe.Var(domain=pe.Reals, bounds=(0, self.capacity))
|
||||||
model.OBJ = pe.Objective(
|
model.OBJ = pe.Objective(
|
||||||
expr=sum(model.x[v] * self.prices[v] for v in items),
|
expr=sum(model.x[v] * self.prices[v] for v in items),
|
||||||
sense=pe.maximize,
|
sense=pe.maximize,
|
||||||
)
|
)
|
||||||
model.eq_capacity = pe.Constraint(
|
model.eq_capacity = pe.Constraint(
|
||||||
expr=sum(model.x[v] * self.weights[v] for v in items) <= self.capacity
|
expr=sum(model.x[v] * self.weights[v] for v in items) == model.z
|
||||||
)
|
)
|
||||||
return model
|
return model
|
||||||
|
|
||||||
@@ -552,3 +585,9 @@ class PyomoTestInstanceKnapsack(Instance):
|
|||||||
self.weights[item],
|
self.weights[item],
|
||||||
self.prices[item],
|
self.prices[item],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@overrides
|
||||||
|
def get_variable_category(self, var_name: VariableName) -> Optional[Category]:
|
||||||
|
if var_name.startswith("x"):
|
||||||
|
return "default"
|
||||||
|
return None
|
||||||
|
|||||||
@@ -117,6 +117,12 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
type="B",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
),
|
),
|
||||||
|
"z": Variable(
|
||||||
|
lower_bound=0.0,
|
||||||
|
obj_coeff=0.0,
|
||||||
|
type="C",
|
||||||
|
upper_bound=67.0,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -127,9 +133,9 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
{
|
{
|
||||||
"eq_capacity": Constraint(
|
"eq_capacity": Constraint(
|
||||||
lazy=False,
|
lazy=False,
|
||||||
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0},
|
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0, "z": -1.0},
|
||||||
rhs=67.0,
|
rhs=0.0,
|
||||||
sense="<",
|
sense="=",
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -161,7 +167,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
sa_obj_up=inf,
|
sa_obj_up=inf,
|
||||||
sa_ub_down=0.913043,
|
sa_ub_down=0.913043,
|
||||||
sa_ub_up=2.043478,
|
sa_ub_up=2.043478,
|
||||||
type="C",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
value=1.0,
|
value=1.0,
|
||||||
),
|
),
|
||||||
@@ -176,7 +182,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
sa_obj_up=570.869565,
|
sa_obj_up=570.869565,
|
||||||
sa_ub_down=0.923077,
|
sa_ub_down=0.923077,
|
||||||
sa_ub_up=inf,
|
sa_ub_up=inf,
|
||||||
type="C",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
value=0.923077,
|
value=0.923077,
|
||||||
),
|
),
|
||||||
@@ -191,7 +197,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
sa_obj_up=inf,
|
sa_obj_up=inf,
|
||||||
sa_ub_down=0.9,
|
sa_ub_down=0.9,
|
||||||
sa_ub_up=2.2,
|
sa_ub_up=2.2,
|
||||||
type="C",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
value=1.0,
|
value=1.0,
|
||||||
),
|
),
|
||||||
@@ -206,10 +212,25 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
sa_obj_up=243.692308,
|
sa_obj_up=243.692308,
|
||||||
sa_ub_down=0.0,
|
sa_ub_down=0.0,
|
||||||
sa_ub_up=inf,
|
sa_ub_up=inf,
|
||||||
type="C",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
value=0.0,
|
value=0.0,
|
||||||
),
|
),
|
||||||
|
"z": Variable(
|
||||||
|
basis_status="U",
|
||||||
|
lower_bound=0.0,
|
||||||
|
obj_coeff=0.0,
|
||||||
|
reduced_cost=13.538462,
|
||||||
|
sa_lb_down=-inf,
|
||||||
|
sa_lb_up=67.0,
|
||||||
|
sa_obj_down=-13.538462,
|
||||||
|
sa_obj_up=inf,
|
||||||
|
sa_ub_down=43.0,
|
||||||
|
sa_ub_up=69.0,
|
||||||
|
type="C",
|
||||||
|
upper_bound=67.0,
|
||||||
|
value=67.0,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -221,15 +242,21 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
solver,
|
solver,
|
||||||
{
|
{
|
||||||
"eq_capacity": Constraint(
|
"eq_capacity": Constraint(
|
||||||
lazy=False,
|
|
||||||
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0},
|
|
||||||
rhs=67.0,
|
|
||||||
sense="<",
|
|
||||||
slack=0.0,
|
|
||||||
dual_value=13.538462,
|
|
||||||
sa_rhs_down=43.0,
|
|
||||||
sa_rhs_up=69.0,
|
|
||||||
basis_status="N",
|
basis_status="N",
|
||||||
|
dual_value=13.538462,
|
||||||
|
lazy=False,
|
||||||
|
lhs={
|
||||||
|
"x[0]": 23.0,
|
||||||
|
"x[1]": 26.0,
|
||||||
|
"x[2]": 20.0,
|
||||||
|
"x[3]": 18.0,
|
||||||
|
"z": -1.0,
|
||||||
|
},
|
||||||
|
rhs=0.0,
|
||||||
|
sa_rhs_down=-24.0,
|
||||||
|
sa_rhs_up=1.9999999999999987,
|
||||||
|
sense="=",
|
||||||
|
slack=0.0,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -238,9 +265,6 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
# Solve MIP
|
# Solve MIP
|
||||||
mip_stats = solver.solve(
|
mip_stats = solver.solve(
|
||||||
tee=True,
|
tee=True,
|
||||||
iteration_cb=None,
|
|
||||||
lazy_cb=None,
|
|
||||||
user_cut_cb=None,
|
|
||||||
)
|
)
|
||||||
assert not solver.is_infeasible()
|
assert not solver.is_infeasible()
|
||||||
assert mip_stats.mip_log is not None
|
assert mip_stats.mip_log is not None
|
||||||
@@ -289,6 +313,13 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
value=1.0,
|
value=1.0,
|
||||||
),
|
),
|
||||||
|
"z": Variable(
|
||||||
|
lower_bound=0.0,
|
||||||
|
obj_coeff=0.0,
|
||||||
|
type="C",
|
||||||
|
upper_bound=67.0,
|
||||||
|
value=61.0,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -298,11 +329,12 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
_round_constraints(solver.get_constraints()),
|
_round_constraints(solver.get_constraints()),
|
||||||
{
|
{
|
||||||
"eq_capacity": Constraint(
|
"eq_capacity": Constraint(
|
||||||
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0},
|
lazy=False,
|
||||||
rhs=67.0,
|
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0, "z": -1.0},
|
||||||
sense="<",
|
rhs=0.0,
|
||||||
slack=6.0,
|
sense="=",
|
||||||
),
|
slack=0.0,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -317,11 +349,13 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
_round_constraints(solver.get_constraints()),
|
_round_constraints(solver.get_constraints()),
|
||||||
{
|
{
|
||||||
"eq_capacity": Constraint(
|
"eq_capacity": Constraint(
|
||||||
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0},
|
lazy=False,
|
||||||
rhs=67.0,
|
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0, "z": -1.0},
|
||||||
sense="<",
|
rhs=0.0,
|
||||||
|
sense="=",
|
||||||
),
|
),
|
||||||
"cut": Constraint(
|
"cut": Constraint(
|
||||||
|
lazy=False,
|
||||||
lhs={"x[0]": 1.0},
|
lhs={"x[0]": 1.0},
|
||||||
rhs=0.0,
|
rhs=0.0,
|
||||||
sense="<",
|
sense="<",
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ def test_parallel_solve(
|
|||||||
for instance in instances:
|
for instance in instances:
|
||||||
data = instance.training_data[0]
|
data = instance.training_data[0]
|
||||||
assert data.solution is not None
|
assert data.solution is not None
|
||||||
assert len(data.solution.keys()) == 4
|
assert len(data.solution.keys()) == 5
|
||||||
|
|
||||||
|
|
||||||
def test_solve_fit_from_disk(
|
def test_solve_fit_from_disk(
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ def test_knapsack() -> None:
|
|||||||
sa_obj_up=inf,
|
sa_obj_up=inf,
|
||||||
sa_ub_down=0.913043,
|
sa_ub_down=0.913043,
|
||||||
sa_ub_up=2.043478,
|
sa_ub_up=2.043478,
|
||||||
type="C",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
user_features=[23.0, 505.0],
|
user_features=[23.0, 505.0],
|
||||||
value=1.0,
|
value=1.0,
|
||||||
@@ -59,7 +59,7 @@ def test_knapsack() -> None:
|
|||||||
sa_obj_up=570.869565,
|
sa_obj_up=570.869565,
|
||||||
sa_ub_down=0.923077,
|
sa_ub_down=0.923077,
|
||||||
sa_ub_up=inf,
|
sa_ub_up=inf,
|
||||||
type="C",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
user_features=[26.0, 352.0],
|
user_features=[26.0, 352.0],
|
||||||
value=0.923077,
|
value=0.923077,
|
||||||
@@ -86,7 +86,7 @@ def test_knapsack() -> None:
|
|||||||
sa_obj_up=inf,
|
sa_obj_up=inf,
|
||||||
sa_ub_down=0.9,
|
sa_ub_down=0.9,
|
||||||
sa_ub_up=2.2,
|
sa_ub_up=2.2,
|
||||||
type="C",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
user_features=[20.0, 458.0],
|
user_features=[20.0, 458.0],
|
||||||
value=1.0,
|
value=1.0,
|
||||||
@@ -104,12 +104,30 @@ def test_knapsack() -> None:
|
|||||||
sa_obj_up=243.692308,
|
sa_obj_up=243.692308,
|
||||||
sa_ub_down=0.0,
|
sa_ub_down=0.0,
|
||||||
sa_ub_up=inf,
|
sa_ub_up=inf,
|
||||||
type="C",
|
type="B",
|
||||||
upper_bound=1.0,
|
upper_bound=1.0,
|
||||||
user_features=[18.0, 220.0],
|
user_features=[18.0, 220.0],
|
||||||
value=0.0,
|
value=0.0,
|
||||||
alvarez_2017=[1.0, 0.143322, 0.0, 0.0, 1.0, -1.0, 46.051702, 3.16515],
|
alvarez_2017=[1.0, 0.143322, 0.0, 0.0, 1.0, -1.0, 46.051702, 3.16515],
|
||||||
),
|
),
|
||||||
|
"z": Variable(
|
||||||
|
basis_status="U",
|
||||||
|
category=None,
|
||||||
|
lower_bound=0.0,
|
||||||
|
obj_coeff=0.0,
|
||||||
|
reduced_cost=13.538462,
|
||||||
|
sa_lb_down=-inf,
|
||||||
|
sa_lb_up=67.0,
|
||||||
|
sa_obj_down=-13.538462,
|
||||||
|
sa_obj_up=inf,
|
||||||
|
sa_ub_down=43.0,
|
||||||
|
sa_ub_up=69.0,
|
||||||
|
type="C",
|
||||||
|
upper_bound=67.0,
|
||||||
|
user_features=None,
|
||||||
|
value=67.0,
|
||||||
|
alvarez_2017=[0.0, 0.0, 0.0, 0.0, 1.0, -1.0, 0.0, 0.0],
|
||||||
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
@@ -120,11 +138,11 @@ def test_knapsack() -> None:
|
|||||||
category="eq_capacity",
|
category="eq_capacity",
|
||||||
dual_value=13.538462,
|
dual_value=13.538462,
|
||||||
lazy=False,
|
lazy=False,
|
||||||
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0},
|
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0, "z": -1.0},
|
||||||
rhs=67.0,
|
rhs=0.0,
|
||||||
sa_rhs_down=43.0,
|
sa_rhs_down=-24.0,
|
||||||
sa_rhs_up=69.0,
|
sa_rhs_up=1.9999999999999987,
|
||||||
sense="<",
|
sense="=",
|
||||||
slack=0.0,
|
slack=0.0,
|
||||||
user_features=[0.0],
|
user_features=[0.0],
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user