mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Use np.ndarray in Variables
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, List, TextIO, cast
|
from typing import Any, List, TextIO, cast, TypeVar, Optional, Sized
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -38,7 +38,10 @@ class _RedirectOutput:
|
|||||||
sys.stderr = self._original_stderr
|
sys.stderr = self._original_stderr
|
||||||
|
|
||||||
|
|
||||||
def _none_if_empty(obj: Any) -> Any:
|
T = TypeVar("T", bound=Sized)
|
||||||
|
|
||||||
|
|
||||||
|
def _none_if_empty(obj: T) -> Optional[T]:
|
||||||
if len(obj) == 0:
|
if len(obj) == 0:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ 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, Optional, Tuple, TYPE_CHECKING
|
from typing import List, Any, Dict, Optional, TYPE_CHECKING
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
from overrides import overrides
|
from overrides import overrides
|
||||||
|
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
@@ -79,9 +80,9 @@ class GurobiSolver(InternalSolver):
|
|||||||
self._var_names: List[str] = []
|
self._var_names: List[str] = []
|
||||||
self._constr_names: List[str] = []
|
self._constr_names: List[str] = []
|
||||||
self._var_types: List[str] = []
|
self._var_types: List[str] = []
|
||||||
self._var_lbs: List[float] = []
|
self._var_lbs: np.ndarray = np.empty(0)
|
||||||
self._var_ubs: List[float] = []
|
self._var_ubs: np.ndarray = np.empty(0)
|
||||||
self._var_obj_coeffs: List[float] = []
|
self._var_obj_coeffs: np.ndarray = np.empty(0)
|
||||||
|
|
||||||
if self.lazy_cb_frequency == 1:
|
if self.lazy_cb_frequency == 1:
|
||||||
self.lazy_cb_where = [self.gp.GRB.Callback.MIPSOL]
|
self.lazy_cb_where = [self.gp.GRB.Callback.MIPSOL]
|
||||||
@@ -338,15 +339,33 @@ class GurobiSolver(InternalSolver):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if with_sa:
|
if with_sa:
|
||||||
sa_obj_up = model.getAttr("saobjUp", self._gp_vars)
|
sa_obj_up = np.array(
|
||||||
sa_obj_down = model.getAttr("saobjLow", self._gp_vars)
|
model.getAttr("saobjUp", self._gp_vars),
|
||||||
sa_ub_up = model.getAttr("saubUp", self._gp_vars)
|
dtype=float,
|
||||||
sa_ub_down = model.getAttr("saubLow", self._gp_vars)
|
)
|
||||||
sa_lb_up = model.getAttr("salbUp", self._gp_vars)
|
sa_obj_down = np.array(
|
||||||
sa_lb_down = model.getAttr("salbLow", self._gp_vars)
|
model.getAttr("saobjLow", self._gp_vars),
|
||||||
|
dtype=float,
|
||||||
|
)
|
||||||
|
sa_ub_up = np.array(
|
||||||
|
model.getAttr("saubUp", self._gp_vars),
|
||||||
|
dtype=float,
|
||||||
|
)
|
||||||
|
sa_ub_down = np.array(
|
||||||
|
model.getAttr("saubLow", self._gp_vars),
|
||||||
|
dtype=float,
|
||||||
|
)
|
||||||
|
sa_lb_up = np.array(
|
||||||
|
model.getAttr("salbUp", self._gp_vars),
|
||||||
|
dtype=float,
|
||||||
|
)
|
||||||
|
sa_lb_down = np.array(
|
||||||
|
model.getAttr("salbLow", self._gp_vars),
|
||||||
|
dtype=float,
|
||||||
|
)
|
||||||
|
|
||||||
if model.solCount > 0:
|
if model.solCount > 0:
|
||||||
values = model.getAttr("x", self._gp_vars)
|
values = np.array(model.getAttr("x", self._gp_vars), dtype=float)
|
||||||
|
|
||||||
return Variables(
|
return Variables(
|
||||||
names=self._var_names,
|
names=self._var_names,
|
||||||
@@ -565,9 +584,18 @@ class GurobiSolver(InternalSolver):
|
|||||||
gp_constrs: List["gurobipy.Constr"] = self.model.getConstrs()
|
gp_constrs: List["gurobipy.Constr"] = self.model.getConstrs()
|
||||||
var_names: List[str] = self.model.getAttr("varName", gp_vars)
|
var_names: List[str] = self.model.getAttr("varName", gp_vars)
|
||||||
var_types: List[str] = self.model.getAttr("vtype", gp_vars)
|
var_types: List[str] = self.model.getAttr("vtype", gp_vars)
|
||||||
var_ubs: List[float] = self.model.getAttr("ub", gp_vars)
|
var_ubs: np.ndarray = np.array(
|
||||||
var_lbs: List[float] = self.model.getAttr("lb", gp_vars)
|
self.model.getAttr("ub", gp_vars),
|
||||||
var_obj_coeffs: List[float] = self.model.getAttr("obj", gp_vars)
|
dtype=float,
|
||||||
|
)
|
||||||
|
var_lbs: np.ndarray = np.array(
|
||||||
|
self.model.getAttr("lb", gp_vars),
|
||||||
|
dtype=float,
|
||||||
|
)
|
||||||
|
var_obj_coeffs: np.ndarray = np.array(
|
||||||
|
self.model.getAttr("obj", gp_vars),
|
||||||
|
dtype=float,
|
||||||
|
)
|
||||||
constr_names: List[str] = self.model.getAttr("constrName", gp_constrs)
|
constr_names: List[str] = self.model.getAttr("constrName", gp_constrs)
|
||||||
varname_to_var: Dict = {}
|
varname_to_var: Dict = {}
|
||||||
cname_to_constr: Dict = {}
|
cname_to_constr: Dict = {}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ 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, Tuple, TYPE_CHECKING
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
from miplearn.instance.base import Instance
|
from miplearn.instance.base import Instance
|
||||||
from miplearn.types import (
|
from miplearn.types import (
|
||||||
IterationCallback,
|
IterationCallback,
|
||||||
@@ -50,18 +52,18 @@ class MIPSolveStats:
|
|||||||
class Variables:
|
class Variables:
|
||||||
names: Optional[List[str]] = None
|
names: Optional[List[str]] = None
|
||||||
basis_status: Optional[List[str]] = None
|
basis_status: Optional[List[str]] = None
|
||||||
lower_bounds: Optional[List[float]] = None
|
lower_bounds: Optional[np.ndarray] = None
|
||||||
obj_coeffs: Optional[List[float]] = None
|
obj_coeffs: Optional[np.ndarray] = None
|
||||||
reduced_costs: Optional[List[float]] = None
|
reduced_costs: Optional[np.ndarray] = None
|
||||||
sa_lb_down: Optional[List[float]] = None
|
sa_lb_down: Optional[np.ndarray] = None
|
||||||
sa_lb_up: Optional[List[float]] = None
|
sa_lb_up: Optional[np.ndarray] = None
|
||||||
sa_obj_down: Optional[List[float]] = None
|
sa_obj_down: Optional[np.ndarray] = None
|
||||||
sa_obj_up: Optional[List[float]] = None
|
sa_obj_up: Optional[np.ndarray] = None
|
||||||
sa_ub_down: Optional[List[float]] = None
|
sa_ub_down: Optional[np.ndarray] = None
|
||||||
sa_ub_up: Optional[List[float]] = None
|
sa_ub_up: Optional[np.ndarray] = None
|
||||||
types: Optional[List[str]] = None
|
types: Optional[List[str]] = None
|
||||||
upper_bounds: Optional[List[float]] = None
|
upper_bounds: Optional[np.ndarray] = None
|
||||||
values: Optional[List[float]] = None
|
values: Optional[np.ndarray] = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|||||||
@@ -330,11 +330,11 @@ class BasePyomoSolver(InternalSolver):
|
|||||||
return Variables(
|
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(np.array(upper_bounds, dtype=float)),
|
||||||
lower_bounds=_none_if_empty(lower_bounds),
|
lower_bounds=_none_if_empty(np.array(lower_bounds, dtype=float)),
|
||||||
obj_coeffs=_none_if_empty(obj_coeffs),
|
obj_coeffs=_none_if_empty(np.array(obj_coeffs, dtype=float)),
|
||||||
reduced_costs=_none_if_empty(reduced_costs),
|
reduced_costs=_none_if_empty(np.array(reduced_costs, dtype=float)),
|
||||||
values=_none_if_empty(values),
|
values=_none_if_empty(np.array(values, dtype=float)),
|
||||||
)
|
)
|
||||||
|
|
||||||
@overrides
|
@overrides
|
||||||
|
|||||||
@@ -41,10 +41,10 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
solver.get_variables(),
|
solver.get_variables(),
|
||||||
Variables(
|
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=np.array([0.0, 0.0, 0.0, 0.0, 0.0]),
|
||||||
upper_bounds=[1.0, 1.0, 1.0, 1.0, 67.0],
|
upper_bounds=np.array([1.0, 1.0, 1.0, 1.0, 67.0]),
|
||||||
types=["B", "B", "B", "B", "C"],
|
types=["B", "B", "B", "B", "C"],
|
||||||
obj_coeffs=[505.0, 352.0, 458.0, 220.0, 0.0],
|
obj_coeffs=np.array([505.0, 352.0, 458.0, 220.0, 0.0]),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,14 +85,18 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
Variables(
|
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=np.array(
|
||||||
sa_lb_down=[-inf, -inf, -inf, -0.111111, -inf],
|
[193.615385, 0.0, 187.230769, -23.692308, 13.538462]
|
||||||
sa_lb_up=[1.0, 0.923077, 1.0, 1.0, 67.0],
|
),
|
||||||
sa_obj_down=[311.384615, 317.777778, 270.769231, -inf, -13.538462],
|
sa_lb_down=np.array([-inf, -inf, -inf, -0.111111, -inf]),
|
||||||
sa_obj_up=[inf, 570.869565, inf, 243.692308, inf],
|
sa_lb_up=np.array([1.0, 0.923077, 1.0, 1.0, 67.0]),
|
||||||
sa_ub_down=[0.913043, 0.923077, 0.9, 0.0, 43.0],
|
sa_obj_down=np.array(
|
||||||
sa_ub_up=[2.043478, inf, 2.2, inf, 69.0],
|
[311.384615, 317.777778, 270.769231, -inf, -13.538462]
|
||||||
values=[1.0, 0.923077, 1.0, 0.0, 67.0],
|
),
|
||||||
|
sa_obj_up=np.array([inf, 570.869565, inf, 243.692308, inf]),
|
||||||
|
sa_ub_down=np.array([0.913043, 0.923077, 0.9, 0.0, 43.0]),
|
||||||
|
sa_ub_up=np.array([2.043478, inf, 2.2, inf, 69.0]),
|
||||||
|
values=np.array([1.0, 0.923077, 1.0, 0.0, 67.0]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -137,7 +141,7 @@ def run_basic_usage_tests(solver: InternalSolver) -> None:
|
|||||||
solver.get_variable_attrs(),
|
solver.get_variable_attrs(),
|
||||||
Variables(
|
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=np.array([1.0, 0.0, 1.0, 1.0, 61.0]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def sample() -> Sample:
|
|||||||
{
|
{
|
||||||
"static_var_names": ["x[0]", "x[1]", "x[2]", "x[3]"],
|
"static_var_names": ["x[0]", "x[1]", "x[2]", "x[3]"],
|
||||||
"static_var_categories": ["default", None, "default", "default"],
|
"static_var_categories": ["default", None, "default", "default"],
|
||||||
"mip_var_values": [0.0, 1.0, 1.0, 0.0],
|
"mip_var_values": np.array([0.0, 1.0, 1.0, 0.0]),
|
||||||
"static_instance_features": [5.0],
|
"static_instance_features": [5.0],
|
||||||
"static_var_features": [
|
"static_var_features": [
|
||||||
[0.0, 0.0],
|
[0.0, 0.0],
|
||||||
|
|||||||
@@ -32,7 +32,8 @@ def test_knapsack() -> None:
|
|||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
extractor.extract_after_load_features(instance, solver, sample)
|
extractor.extract_after_load_features(instance, solver, sample)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
sample.get_vector("static_var_names"), ["x[0]", "x[1]", "x[2]", "x[3]", "z"]
|
sample.get_vector("static_var_names"),
|
||||||
|
["x[0]", "x[1]", "x[2]", "x[3]", "z"],
|
||||||
)
|
)
|
||||||
assert_equals(
|
assert_equals(
|
||||||
sample.get_vector("static_var_lower_bounds"), [0.0, 0.0, 0.0, 0.0, 0.0]
|
sample.get_vector("static_var_lower_bounds"), [0.0, 0.0, 0.0, 0.0, 0.0]
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def test_usage() -> None:
|
|||||||
# Save instance to disk
|
# Save instance to disk
|
||||||
filename = tempfile.mktemp()
|
filename = tempfile.mktemp()
|
||||||
FileInstance.save(original, filename)
|
FileInstance.save(original, filename)
|
||||||
sample = Hdf5Sample(filename)
|
sample = Hdf5Sample(filename, check_data=True)
|
||||||
assert len(sample.get_bytes("pickled")) > 0
|
assert len(sample.get_bytes("pickled")) > 0
|
||||||
|
|
||||||
# Solve instance from disk
|
# Solve instance from disk
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from scipy.stats import uniform, randint
|
|||||||
|
|
||||||
from miplearn.problems.tsp import TravelingSalesmanGenerator, TravelingSalesmanInstance
|
from miplearn.problems.tsp import TravelingSalesmanGenerator, TravelingSalesmanInstance
|
||||||
from miplearn.solvers.learning import LearningSolver
|
from miplearn.solvers.learning import LearningSolver
|
||||||
|
from miplearn.solvers.tests import assert_equals
|
||||||
|
|
||||||
|
|
||||||
def test_generator() -> None:
|
def test_generator() -> None:
|
||||||
@@ -41,7 +42,7 @@ def test_instance() -> None:
|
|||||||
solver.solve(instance)
|
solver.solve(instance)
|
||||||
assert len(instance.get_samples()) == 1
|
assert len(instance.get_samples()) == 1
|
||||||
sample = instance.get_samples()[0]
|
sample = instance.get_samples()[0]
|
||||||
assert sample.get_vector("mip_var_values") == [1.0, 0.0, 1.0, 1.0, 0.0, 1.0]
|
assert_equals(sample.get_vector("mip_var_values"), [1.0, 0.0, 1.0, 1.0, 0.0, 1.0])
|
||||||
assert sample.get_scalar("mip_lower_bound") == 4.0
|
assert sample.get_scalar("mip_lower_bound") == 4.0
|
||||||
assert sample.get_scalar("mip_upper_bound") == 4.0
|
assert sample.get_scalar("mip_upper_bound") == 4.0
|
||||||
|
|
||||||
@@ -68,22 +69,25 @@ def test_subtour() -> None:
|
|||||||
lazy_enforced = sample.get_set("mip_constr_lazy_enforced")
|
lazy_enforced = sample.get_set("mip_constr_lazy_enforced")
|
||||||
assert lazy_enforced is not None
|
assert lazy_enforced is not None
|
||||||
assert len(lazy_enforced) > 0
|
assert len(lazy_enforced) > 0
|
||||||
assert sample.get_vector("mip_var_values") == [
|
assert_equals(
|
||||||
1.0,
|
sample.get_vector("mip_var_values"),
|
||||||
0.0,
|
[
|
||||||
0.0,
|
1.0,
|
||||||
1.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
1.0,
|
||||||
0.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
1.0,
|
0.0,
|
||||||
]
|
1.0,
|
||||||
|
1.0,
|
||||||
|
],
|
||||||
|
)
|
||||||
solver.fit([instance])
|
solver.fit([instance])
|
||||||
solver.solve(instance)
|
solver.solve(instance)
|
||||||
|
|||||||
@@ -38,7 +38,9 @@ def test_learning_solver(
|
|||||||
assert len(instance.get_samples()) > 0
|
assert len(instance.get_samples()) > 0
|
||||||
sample = instance.get_samples()[0]
|
sample = instance.get_samples()[0]
|
||||||
|
|
||||||
assert sample.get_vector("mip_var_values") == [1.0, 0.0, 1.0, 1.0, 61.0]
|
assert_equals(
|
||||||
|
sample.get_vector("mip_var_values"), [1.0, 0.0, 1.0, 1.0, 61.0]
|
||||||
|
)
|
||||||
assert sample.get_scalar("mip_lower_bound") == 1183.0
|
assert sample.get_scalar("mip_lower_bound") == 1183.0
|
||||||
assert sample.get_scalar("mip_upper_bound") == 1183.0
|
assert sample.get_scalar("mip_upper_bound") == 1183.0
|
||||||
mip_log = sample.get_scalar("mip_log")
|
mip_log = sample.get_scalar("mip_log")
|
||||||
|
|||||||
Reference in New Issue
Block a user