mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Extract LHS as a sparse matrix
This commit is contained in:
@@ -39,7 +39,7 @@ class FeaturesExtractor:
|
|||||||
sample.put_array("static_var_types", variables.types)
|
sample.put_array("static_var_types", variables.types)
|
||||||
sample.put_array("static_var_upper_bounds", variables.upper_bounds)
|
sample.put_array("static_var_upper_bounds", variables.upper_bounds)
|
||||||
sample.put_array("static_constr_names", constraints.names)
|
sample.put_array("static_constr_names", constraints.names)
|
||||||
# sample.put("static_constr_lhs", constraints.lhs)
|
sample.put_sparse("static_constr_lhs", constraints.lhs)
|
||||||
sample.put_array("static_constr_rhs", constraints.rhs)
|
sample.put_array("static_constr_rhs", constraints.rhs)
|
||||||
sample.put_array("static_constr_senses", constraints.senses)
|
sample.put_array("static_constr_senses", constraints.senses)
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,9 @@ class Sample(ABC):
|
|||||||
assert value.dtype.kind in "biufS", f"Unsupported dtype: {value.dtype}"
|
assert value.dtype.kind in "biufS", f"Unsupported dtype: {value.dtype}"
|
||||||
|
|
||||||
def _assert_is_sparse(self, value: Any) -> None:
|
def _assert_is_sparse(self, value: Any) -> None:
|
||||||
assert isinstance(value, coo_matrix)
|
assert isinstance(
|
||||||
|
value, coo_matrix
|
||||||
|
), f"coo_matrix expected; found: {value.__class__}"
|
||||||
self._assert_is_array(value.data)
|
self._assert_is_array(value.data)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from typing import List, Any, Dict, Optional, TYPE_CHECKING
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from overrides import overrides
|
from overrides import overrides
|
||||||
|
from scipy.sparse import coo_matrix, lil_matrix
|
||||||
|
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.solvers import _RedirectOutput
|
from miplearn.solvers import _RedirectOutput
|
||||||
@@ -99,17 +100,19 @@ class GurobiSolver(InternalSolver):
|
|||||||
assert cf.lhs is not None
|
assert cf.lhs is not None
|
||||||
assert cf.rhs is not None
|
assert cf.rhs is not None
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
|
lhs = cf.lhs.tocsr()
|
||||||
for i in range(len(cf.names)):
|
for i in range(len(cf.names)):
|
||||||
sense = cf.senses[i]
|
sense = cf.senses[i]
|
||||||
lhs = self.gp.quicksum(
|
row = lhs[i, :]
|
||||||
self._varname_to_var[varname] * coeff for (varname, coeff) in cf.lhs[i]
|
row_expr = self.gp.quicksum(
|
||||||
|
self._gp_vars[row.indices[j]] * row.data[j] for j in range(row.getnnz())
|
||||||
)
|
)
|
||||||
if sense == b"=":
|
if sense == b"=":
|
||||||
self.model.addConstr(lhs == cf.rhs[i], name=cf.names[i])
|
self.model.addConstr(row_expr == cf.rhs[i], name=cf.names[i])
|
||||||
elif sense == b"<":
|
elif sense == b"<":
|
||||||
self.model.addConstr(lhs <= cf.rhs[i], name=cf.names[i])
|
self.model.addConstr(row_expr <= cf.rhs[i], name=cf.names[i])
|
||||||
elif sense == b">":
|
elif sense == b">":
|
||||||
self.model.addConstr(lhs >= cf.rhs[i], name=cf.names[i])
|
self.model.addConstr(row_expr >= cf.rhs[i], name=cf.names[i])
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Unknown sense: {sense}")
|
raise Exception(f"Unknown sense: {sense}")
|
||||||
self.model.update()
|
self.model.update()
|
||||||
@@ -133,18 +136,18 @@ class GurobiSolver(InternalSolver):
|
|||||||
assert cf.rhs is not None
|
assert cf.rhs is not None
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
result = []
|
result = []
|
||||||
|
x = np.array(self.model.getAttr("x", self.model.getVars()))
|
||||||
|
lhs = cf.lhs.tocsr() * x
|
||||||
for i in range(len(cf.names)):
|
for i in range(len(cf.names)):
|
||||||
sense = cf.senses[i]
|
sense = cf.senses[i]
|
||||||
lhs = sum(
|
if sense == b"<":
|
||||||
self._varname_to_var[varname].x * coeff
|
result.append(lhs[i] <= cf.rhs[i] + tol)
|
||||||
for (varname, coeff) in cf.lhs[i]
|
elif sense == b">":
|
||||||
)
|
result.append(lhs[i] >= cf.rhs[i] - tol)
|
||||||
if sense == "<":
|
elif sense == b"<":
|
||||||
result.append(lhs <= cf.rhs[i] + tol)
|
result.append(abs(cf.rhs[i] - lhs[i]) <= tol)
|
||||||
elif sense == ">":
|
|
||||||
result.append(lhs >= cf.rhs[i] - tol)
|
|
||||||
else:
|
else:
|
||||||
result.append(abs(cf.rhs[i] - lhs) <= tol)
|
raise Exception(f"unknown sense: {sense}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
@@ -214,7 +217,7 @@ class GurobiSolver(InternalSolver):
|
|||||||
|
|
||||||
gp_constrs = model.getConstrs()
|
gp_constrs = model.getConstrs()
|
||||||
constr_names = np.array(model.getAttr("constrName", gp_constrs), dtype="S")
|
constr_names = np.array(model.getAttr("constrName", gp_constrs), dtype="S")
|
||||||
lhs: Optional[List] = None
|
lhs: Optional[coo_matrix] = None
|
||||||
rhs, senses, slacks, basis_status = None, None, None, None
|
rhs, senses, slacks, basis_status = None, None, None, None
|
||||||
dual_value, basis_status, sa_rhs_up, sa_rhs_down = None, None, None, None
|
dual_value, basis_status, sa_rhs_up, sa_rhs_down = None, None, None, None
|
||||||
|
|
||||||
@@ -222,13 +225,14 @@ class GurobiSolver(InternalSolver):
|
|||||||
rhs = np.array(model.getAttr("rhs", gp_constrs), dtype=float)
|
rhs = np.array(model.getAttr("rhs", gp_constrs), dtype=float)
|
||||||
senses = np.array(model.getAttr("sense", gp_constrs), dtype="S")
|
senses = np.array(model.getAttr("sense", gp_constrs), dtype="S")
|
||||||
if with_lhs:
|
if with_lhs:
|
||||||
lhs = [None for _ in gp_constrs]
|
nrows = len(gp_constrs)
|
||||||
|
ncols = len(self._var_names)
|
||||||
|
tmp = lil_matrix((nrows, ncols), dtype=float)
|
||||||
for (i, gp_constr) in enumerate(gp_constrs):
|
for (i, gp_constr) in enumerate(gp_constrs):
|
||||||
expr = model.getRow(gp_constr)
|
expr = model.getRow(gp_constr)
|
||||||
lhs[i] = [
|
for j in range(expr.size()):
|
||||||
(self._var_names[expr.getVar(j).index], expr.getCoeff(j))
|
tmp[i, j] = expr.getCoeff(j)
|
||||||
for j in range(expr.size())
|
lhs = tmp.tocoo()
|
||||||
]
|
|
||||||
|
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
dual_value = np.array(model.getAttr("pi", gp_constrs), dtype=float)
|
dual_value = np.array(model.getAttr("pi", gp_constrs), dtype=float)
|
||||||
|
|||||||
@@ -5,9 +5,10 @@
|
|||||||
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, Optional, List, Tuple, TYPE_CHECKING
|
from typing import Any, Optional, List, TYPE_CHECKING
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from scipy.sparse import coo_matrix
|
||||||
|
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.types import (
|
from miplearn.types import (
|
||||||
@@ -71,7 +72,7 @@ class Constraints:
|
|||||||
basis_status: Optional[np.ndarray] = None
|
basis_status: Optional[np.ndarray] = None
|
||||||
dual_values: Optional[np.ndarray] = None
|
dual_values: Optional[np.ndarray] = None
|
||||||
lazy: Optional[np.ndarray] = None
|
lazy: Optional[np.ndarray] = None
|
||||||
lhs: Optional[List[List[Tuple[bytes, float]]]] = None
|
lhs: Optional[coo_matrix] = None
|
||||||
names: Optional[np.ndarray] = None
|
names: Optional[np.ndarray] = None
|
||||||
rhs: Optional[np.ndarray] = None
|
rhs: Optional[np.ndarray] = None
|
||||||
sa_rhs_down: Optional[np.ndarray] = None
|
sa_rhs_down: Optional[np.ndarray] = None
|
||||||
@@ -104,7 +105,7 @@ class Constraints:
|
|||||||
),
|
),
|
||||||
names=(None if self.names is None else self.names[selected]),
|
names=(None if self.names is None else self.names[selected]),
|
||||||
lazy=(None if self.lazy is None else self.lazy[selected]),
|
lazy=(None if self.lazy is None else self.lazy[selected]),
|
||||||
lhs=self._filter(self.lhs, selected),
|
lhs=(None if self.lhs is None else self.lhs.tocsr()[selected].tocoo()),
|
||||||
rhs=(None if self.rhs is None else self.rhs[selected]),
|
rhs=(None if self.rhs is None else self.rhs[selected]),
|
||||||
sa_rhs_down=(
|
sa_rhs_down=(
|
||||||
None if self.sa_rhs_down is None else self.sa_rhs_down[selected]
|
None if self.sa_rhs_down is None else self.sa_rhs_down[selected]
|
||||||
@@ -114,15 +115,6 @@ class Constraints:
|
|||||||
slacks=(None if self.slacks is None else self.slacks[selected]),
|
slacks=(None if self.slacks is None else 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):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import Any, List, Dict, Optional, Tuple
|
from typing import Any, List, Dict, Optional
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyomo
|
import pyomo
|
||||||
@@ -18,6 +18,7 @@ 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
|
||||||
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 scipy.sparse import coo_matrix
|
||||||
|
|
||||||
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
|
||||||
@@ -58,6 +59,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
self._pyomo_solver: SolverFactory = solver_factory
|
self._pyomo_solver: SolverFactory = solver_factory
|
||||||
self._obj_sense: str = "min"
|
self._obj_sense: str = "min"
|
||||||
self._varname_to_var: Dict[bytes, pe.Var] = {}
|
self._varname_to_var: Dict[bytes, pe.Var] = {}
|
||||||
|
self._varname_to_idx: Dict[str, int] = {}
|
||||||
self._cname_to_constr: Dict[str, pe.Constraint] = {}
|
self._cname_to_constr: Dict[str, pe.Constraint] = {}
|
||||||
self._termination_condition: str = ""
|
self._termination_condition: str = ""
|
||||||
self._has_lp_solution = False
|
self._has_lp_solution = False
|
||||||
@@ -84,23 +86,24 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
assert cf.lhs is not None
|
assert cf.lhs is not None
|
||||||
assert cf.rhs is not None
|
assert cf.rhs is not None
|
||||||
assert self.model is not None
|
assert self.model is not None
|
||||||
for (i, name) in enumerate(cf.names):
|
lhs = cf.lhs.tocsr()
|
||||||
lhs = 0.0
|
for i in range(len(cf.names)):
|
||||||
for (varname, coeff) in cf.lhs[i]:
|
row = lhs[i, :]
|
||||||
var = self._varname_to_var[varname]
|
lhsi = 0.0
|
||||||
lhs += var * coeff
|
for j in range(row.getnnz()):
|
||||||
|
lhsi += self._all_vars[row.indices[j]] * row.data[j]
|
||||||
if cf.senses[i] == b"=":
|
if cf.senses[i] == b"=":
|
||||||
expr = lhs == cf.rhs[i]
|
expr = lhsi == cf.rhs[i]
|
||||||
elif cf.senses[i] == b"<":
|
elif cf.senses[i] == b"<":
|
||||||
expr = lhs <= cf.rhs[i]
|
expr = lhsi <= cf.rhs[i]
|
||||||
elif cf.senses[i] == b">":
|
elif cf.senses[i] == b">":
|
||||||
expr = lhs >= cf.rhs[i]
|
expr = lhsi >= cf.rhs[i]
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Unknown sense: {cf.senses[i]}")
|
raise Exception(f"Unknown sense: {cf.senses[i]}")
|
||||||
cl = pe.Constraint(expr=expr, name=name)
|
cl = pe.Constraint(expr=expr, name=cf.names[i])
|
||||||
self.model.add_component(name.decode(), cl)
|
self.model.add_component(cf.names[i].decode(), cl)
|
||||||
self._pyomo_solver.add_constraint(cl)
|
self._pyomo_solver.add_constraint(cl)
|
||||||
self._cname_to_constr[name] = cl
|
self._cname_to_constr[cf.names[i]] = cl
|
||||||
self._termination_condition = ""
|
self._termination_condition = ""
|
||||||
self._has_lp_solution = False
|
self._has_lp_solution = False
|
||||||
self._has_mip_solution = False
|
self._has_mip_solution = False
|
||||||
@@ -119,18 +122,18 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
assert cf.lhs is not None
|
assert cf.lhs is not None
|
||||||
assert cf.rhs is not None
|
assert cf.rhs is not None
|
||||||
assert cf.senses is not None
|
assert cf.senses is not None
|
||||||
|
x = [v.value for v in self._all_vars]
|
||||||
|
lhs = cf.lhs.tocsr() * x
|
||||||
result = []
|
result = []
|
||||||
for (i, name) in enumerate(cf.names):
|
for i in range(len(lhs)):
|
||||||
lhs = 0.0
|
if cf.senses[i] == b"<":
|
||||||
for (varname, coeff) in cf.lhs[i]:
|
result.append(lhs[i] <= cf.rhs[i] + tol)
|
||||||
var = self._varname_to_var[varname]
|
elif cf.senses[i] == b">":
|
||||||
lhs += var.value * coeff
|
result.append(lhs[i] >= cf.rhs[i] - tol)
|
||||||
if cf.senses[i] == "<":
|
elif cf.senses[i] == b"=":
|
||||||
result.append(lhs <= cf.rhs[i] + tol)
|
result.append(abs(cf.rhs[i] - lhs[i]) < tol)
|
||||||
elif cf.senses[i] == ">":
|
|
||||||
result.append(lhs >= cf.rhs[i] - tol)
|
|
||||||
else:
|
else:
|
||||||
result.append(abs(cf.rhs[i] - lhs) < tol)
|
raise Exception(f"unknown sense: {cf.senses[i]}")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
@@ -163,15 +166,17 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
) -> Constraints:
|
) -> Constraints:
|
||||||
model = self.model
|
model = self.model
|
||||||
assert model is not None
|
assert model is not None
|
||||||
|
|
||||||
names: List[str] = []
|
names: List[str] = []
|
||||||
rhs: List[float] = []
|
rhs: List[float] = []
|
||||||
lhs: List[List[Tuple[bytes, float]]] = []
|
|
||||||
senses: List[str] = []
|
senses: List[str] = []
|
||||||
dual_values: List[float] = []
|
dual_values: List[float] = []
|
||||||
slacks: List[float] = []
|
slacks: List[float] = []
|
||||||
|
lhs_row: List[int] = []
|
||||||
|
lhs_col: List[int] = []
|
||||||
|
lhs_data: List[float] = []
|
||||||
|
lhs: Optional[coo_matrix] = None
|
||||||
|
|
||||||
def _parse_constraint(c: pe.Constraint) -> None:
|
def _parse_constraint(c: pe.Constraint, row: int) -> None:
|
||||||
assert model is not None
|
assert model is not None
|
||||||
if with_static:
|
if with_static:
|
||||||
# Extract RHS and sense
|
# Extract RHS and sense
|
||||||
@@ -192,30 +197,31 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
|
|
||||||
if with_lhs:
|
if with_lhs:
|
||||||
# Extract LHS
|
# Extract LHS
|
||||||
lhsc = []
|
|
||||||
expr = c.body
|
expr = c.body
|
||||||
if isinstance(expr, SumExpression):
|
if isinstance(expr, SumExpression):
|
||||||
for term in expr._args_:
|
for term in expr._args_:
|
||||||
if isinstance(term, MonomialTermExpression):
|
if isinstance(term, MonomialTermExpression):
|
||||||
lhsc.append(
|
lhs_row.append(row)
|
||||||
(
|
lhs_col.append(
|
||||||
term._args_[1].name.encode(),
|
self._varname_to_idx[term._args_[1].name]
|
||||||
float(term._args_[0]),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
lhs_data.append(float(term._args_[0]))
|
||||||
elif isinstance(term, _GeneralVarData):
|
elif isinstance(term, _GeneralVarData):
|
||||||
lhsc.append((term.name.encode(), 1.0))
|
lhs_row.append(row)
|
||||||
|
lhs_col.append(self._varname_to_idx[term.name])
|
||||||
|
lhs_data.append(1.0)
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
f"Unknown term type: {term.__class__.__name__}"
|
f"Unknown term type: {term.__class__.__name__}"
|
||||||
)
|
)
|
||||||
elif isinstance(expr, _GeneralVarData):
|
elif isinstance(expr, _GeneralVarData):
|
||||||
lhsc.append((expr.name.encode(), 1.0))
|
lhs_row.append(row)
|
||||||
|
lhs_col.append(self._varname_to_idx[expr.name])
|
||||||
|
lhs_data.append(1.0)
|
||||||
else:
|
else:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
f"Unknown expression type: {expr.__class__.__name__}"
|
f"Unknown expression type: {expr.__class__.__name__}"
|
||||||
)
|
)
|
||||||
lhs.append(lhsc)
|
|
||||||
|
|
||||||
# Extract dual values
|
# Extract dual values
|
||||||
if self._has_lp_solution:
|
if self._has_lp_solution:
|
||||||
@@ -225,20 +231,26 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
if self._has_mip_solution or self._has_lp_solution:
|
if self._has_mip_solution or self._has_lp_solution:
|
||||||
slacks.append(model.slack[c])
|
slacks.append(model.slack[c])
|
||||||
|
|
||||||
for constr in model.component_objects(pyomo.core.Constraint):
|
curr_row = 0
|
||||||
|
for (i, constr) in enumerate(model.component_objects(pyomo.core.Constraint)):
|
||||||
if isinstance(constr, pe.ConstraintList):
|
if isinstance(constr, pe.ConstraintList):
|
||||||
for idx in constr:
|
for idx in constr:
|
||||||
names.append(f"{constr.name}[{idx}]")
|
names.append(constr[idx].name)
|
||||||
_parse_constraint(constr[idx])
|
_parse_constraint(constr[idx], curr_row)
|
||||||
|
curr_row += 1
|
||||||
else:
|
else:
|
||||||
names.append(constr.name)
|
names.append(constr.name)
|
||||||
_parse_constraint(constr)
|
_parse_constraint(constr, curr_row)
|
||||||
|
curr_row += 1
|
||||||
|
|
||||||
|
if len(lhs_data) > 0:
|
||||||
|
lhs = coo_matrix((lhs_data, (lhs_row, lhs_col))).tocoo()
|
||||||
|
|
||||||
return Constraints(
|
return Constraints(
|
||||||
names=_none_if_empty(np.array(names, dtype="S")),
|
names=_none_if_empty(np.array(names, dtype="S")),
|
||||||
rhs=_none_if_empty(np.array(rhs, dtype=float)),
|
rhs=_none_if_empty(np.array(rhs, dtype=float)),
|
||||||
senses=_none_if_empty(np.array(senses, dtype="S")),
|
senses=_none_if_empty(np.array(senses, dtype="S")),
|
||||||
lhs=_none_if_empty(lhs),
|
lhs=lhs,
|
||||||
slacks=_none_if_empty(np.array(slacks, dtype=float)),
|
slacks=_none_if_empty(np.array(slacks, dtype=float)),
|
||||||
dual_values=_none_if_empty(np.array(dual_values, dtype=float)),
|
dual_values=_none_if_empty(np.array(dual_values, dtype=float)),
|
||||||
)
|
)
|
||||||
@@ -264,7 +276,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
for index in var:
|
for index in var:
|
||||||
if var[index].fixed:
|
if var[index].fixed:
|
||||||
continue
|
continue
|
||||||
solution[f"{var}[{index}]".encode()] = var[index].value
|
solution[var[index].name.encode()] = var[index].value
|
||||||
return solution
|
return solution
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
@@ -289,9 +301,9 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
|
|
||||||
# Variable name
|
# Variable name
|
||||||
if idx is None:
|
if idx is None:
|
||||||
names.append(str(var))
|
names.append(var.name)
|
||||||
else:
|
else:
|
||||||
names.append(f"{var}[{idx}]")
|
names.append(var[idx].name)
|
||||||
|
|
||||||
if with_static:
|
if with_static:
|
||||||
# Variable type
|
# Variable type
|
||||||
@@ -556,12 +568,14 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
self._all_vars = []
|
self._all_vars = []
|
||||||
self._bin_vars = []
|
self._bin_vars = []
|
||||||
self._varname_to_var = {}
|
self._varname_to_var = {}
|
||||||
|
self._varname_to_idx = {}
|
||||||
for var in self.model.component_objects(Var):
|
for var in self.model.component_objects(Var):
|
||||||
for idx in var:
|
for idx in var:
|
||||||
varname = f"{var.name}[{idx}]".encode()
|
varname = var.name
|
||||||
if idx is None:
|
if idx is not None:
|
||||||
varname = var.name.encode()
|
varname = var[idx].name
|
||||||
self._varname_to_var[varname] = var[idx]
|
self._varname_to_var[varname.encode()] = var[idx]
|
||||||
|
self._varname_to_idx[varname] = len(self._all_vars)
|
||||||
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]]
|
||||||
@@ -575,7 +589,7 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
for constr in self.model.component_objects(pyomo.core.Constraint):
|
for constr in self.model.component_objects(pyomo.core.Constraint):
|
||||||
if isinstance(constr, pe.ConstraintList):
|
if isinstance(constr, pe.ConstraintList):
|
||||||
for idx in constr:
|
for idx in constr:
|
||||||
self._cname_to_constr[f"{constr.name}[{idx}]"] = constr[idx]
|
self._cname_to_constr[constr[idx].name] = constr[idx]
|
||||||
else:
|
else:
|
||||||
self._cname_to_constr[constr.name] = constr
|
self._cname_to_constr[constr.name] = constr
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
from typing import Any, List
|
from typing import Any, List
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from scipy.sparse import coo_matrix
|
||||||
|
|
||||||
from miplearn.solvers.internal import InternalSolver, Variables, Constraints
|
from miplearn.solvers.internal import InternalSolver, Variables, Constraints
|
||||||
|
|
||||||
@@ -55,15 +56,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
Constraints(
|
Constraints(
|
||||||
names=np.array(["eq_capacity"], dtype="S"),
|
names=np.array(["eq_capacity"], dtype="S"),
|
||||||
rhs=np.array([0.0]),
|
rhs=np.array([0.0]),
|
||||||
lhs=[
|
lhs=coo_matrix([[23.0, 26.0, 20.0, 18.0, -1.0]]),
|
||||||
[
|
|
||||||
(b"x[0]", 23.0),
|
|
||||||
(b"x[1]", 26.0),
|
|
||||||
(b"x[2]", 20.0),
|
|
||||||
(b"x[3]", 18.0),
|
|
||||||
(b"z", -1.0),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
senses=np.array(["="], dtype="S"),
|
senses=np.array(["="], dtype="S"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -162,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 = Constraints(
|
cf = Constraints(
|
||||||
names=np.array(["cut"], dtype="S"),
|
names=np.array(["cut"], dtype="S"),
|
||||||
lhs=[[(b"x[0]", 1.0)]],
|
lhs=coo_matrix([[1.0, 0.0, 0.0, 0.0, 0.0]]),
|
||||||
rhs=np.array([0.0]),
|
rhs=np.array([0.0]),
|
||||||
senses=np.array(["<"], dtype="S"),
|
senses=np.array(["<"], dtype="S"),
|
||||||
)
|
)
|
||||||
@@ -177,18 +170,12 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
Constraints(
|
Constraints(
|
||||||
names=np.array(["eq_capacity", "cut"], dtype="S"),
|
names=np.array(["eq_capacity", "cut"], dtype="S"),
|
||||||
rhs=np.array([0.0, 0.0]),
|
rhs=np.array([0.0, 0.0]),
|
||||||
lhs=[
|
lhs=coo_matrix(
|
||||||
[
|
[
|
||||||
(b"x[0]", 23.0),
|
[23.0, 26.0, 20.0, 18.0, -1.0],
|
||||||
(b"x[1]", 26.0),
|
[1.0, 0.0, 0.0, 0.0, 0.0],
|
||||||
(b"x[2]", 20.0),
|
]
|
||||||
(b"x[3]", 18.0),
|
),
|
||||||
(b"z", -1.0),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
(b"x[0]", 1.0),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
senses=np.array(["=", "<"], dtype="S"),
|
senses=np.array(["=", "<"], dtype="S"),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -275,6 +262,8 @@ def _equals_preprocess(obj: Any) -> Any:
|
|||||||
return np.round(obj, decimals=6).tolist()
|
return np.round(obj, decimals=6).tolist()
|
||||||
else:
|
else:
|
||||||
return obj.tolist()
|
return obj.tolist()
|
||||||
|
elif isinstance(obj, coo_matrix):
|
||||||
|
return obj.todense().tolist()
|
||||||
elif isinstance(obj, (int, str, bool, np.bool_, np.bytes_, bytes, bytearray)):
|
elif isinstance(obj, (int, str, bool, np.bool_, np.bytes_, bytes, bytearray)):
|
||||||
return obj
|
return obj
|
||||||
elif isinstance(obj, float):
|
elif isinstance(obj, float):
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from typing import Any
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import gurobipy as gp
|
import gurobipy as gp
|
||||||
|
from scipy.sparse import coo_matrix
|
||||||
|
|
||||||
from miplearn.features.extractor import FeaturesExtractor
|
from miplearn.features.extractor import FeaturesExtractor
|
||||||
from miplearn.features.sample import Hdf5Sample, MemorySample
|
from miplearn.features.sample import Hdf5Sample, MemorySample
|
||||||
@@ -76,18 +77,10 @@ def test_knapsack() -> None:
|
|||||||
sample.get_array("static_constr_names"),
|
sample.get_array("static_constr_names"),
|
||||||
np.array(["eq_capacity"], dtype="S"),
|
np.array(["eq_capacity"], dtype="S"),
|
||||||
)
|
)
|
||||||
# assert_equals(
|
assert_equals(
|
||||||
# sample.get_array("static_constr_lhs"),
|
sample.get_sparse("static_constr_lhs"),
|
||||||
# [
|
[[23.0, 26.0, 20.0, 18.0, -1.0]],
|
||||||
# [
|
)
|
||||||
# ("x[0]", 23.0),
|
|
||||||
# ("x[1]", 26.0),
|
|
||||||
# ("x[2]", 20.0),
|
|
||||||
# ("x[3]", 18.0),
|
|
||||||
# ("z", -1.0),
|
|
||||||
# ],
|
|
||||||
# ],
|
|
||||||
# )
|
|
||||||
assert_equals(
|
assert_equals(
|
||||||
sample.get_array("static_constr_rhs"),
|
sample.get_array("static_constr_rhs"),
|
||||||
np.array([0.0]),
|
np.array([0.0]),
|
||||||
@@ -321,20 +314,13 @@ def test_constraint_getindex() -> None:
|
|||||||
names=np.array(["c1", "c2", "c3"], dtype="S"),
|
names=np.array(["c1", "c2", "c3"], dtype="S"),
|
||||||
rhs=np.array([1.0, 2.0, 3.0]),
|
rhs=np.array([1.0, 2.0, 3.0]),
|
||||||
senses=np.array(["=", "<", ">"], dtype="S"),
|
senses=np.array(["=", "<", ">"], dtype="S"),
|
||||||
lhs=[
|
lhs=coo_matrix(
|
||||||
[
|
[
|
||||||
(b"x1", 1.0),
|
[1, 2, 3],
|
||||||
(b"x2", 1.0),
|
[4, 5, 6],
|
||||||
],
|
[7, 8, 9],
|
||||||
[
|
]
|
||||||
(b"x2", 2.0),
|
),
|
||||||
(b"x3", 2.0),
|
|
||||||
],
|
|
||||||
[
|
|
||||||
(b"x3", 3.0),
|
|
||||||
(b"x4", 3.0),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
cf[[True, False, True]],
|
cf[[True, False, True]],
|
||||||
@@ -342,16 +328,12 @@ def test_constraint_getindex() -> None:
|
|||||||
names=np.array(["c1", "c3"], dtype="S"),
|
names=np.array(["c1", "c3"], dtype="S"),
|
||||||
rhs=np.array([1.0, 3.0]),
|
rhs=np.array([1.0, 3.0]),
|
||||||
senses=np.array(["=", ">"], dtype="S"),
|
senses=np.array(["=", ">"], dtype="S"),
|
||||||
lhs=[
|
lhs=coo_matrix(
|
||||||
[
|
[
|
||||||
(b"x1", 1.0),
|
[1, 2, 3],
|
||||||
(b"x2", 1.0),
|
[7, 8, 9],
|
||||||
],
|
]
|
||||||
[
|
),
|
||||||
(b"x3", 3.0),
|
|
||||||
(b"x4", 3.0),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user