GurobiModel: Capture static_var_obj_coeffs_quad

This commit is contained in:
2025-06-11 13:19:36 -05:00
parent 1c6912cc51
commit 2ca2794457
3 changed files with 82 additions and 10 deletions

View File

@@ -80,6 +80,7 @@ class MaxCutGenerator:
def _generate_graph(self) -> Graph: def _generate_graph(self) -> Graph:
return nx.generators.random_graphs.binomial_graph(self.n.rvs(), self.p.rvs()) return nx.generators.random_graphs.binomial_graph(self.n.rvs(), self.p.rvs())
def build_maxcut_model_gurobipy( def build_maxcut_model_gurobipy(
data: Union[str, MaxCutData], data: Union[str, MaxCutData],
params: Optional[dict[str, Any]] = None, params: Optional[dict[str, Any]] = None,
@@ -97,13 +98,16 @@ def build_maxcut_model_gurobipy(
x = model.addVars(nodes, vtype=gp.GRB.BINARY, name="x") x = model.addVars(nodes, vtype=gp.GRB.BINARY, name="x")
# Add the objective function # Add the objective function
model.setObjective(quicksum( model.setObjective(
- data.weights[i] * x[e[0]] * (1 - x[e[1]]) for (i, e) in enumerate(edges) quicksum(
)) -data.weights[i] * x[e[0]] * (1 - x[e[1]]) for (i, e) in enumerate(edges)
)
)
model.update() model.update()
return GurobiModel(model) return GurobiModel(model)
def _maxcut_read(data: Union[str, MaxCutData]) -> MaxCutData: def _maxcut_read(data: Union[str, MaxCutData]) -> MaxCutData:
if isinstance(data, str): if isinstance(data, str):
data = read_pkl_gz(data) data = read_pkl_gz(data)

View File

@@ -264,6 +264,13 @@ class GurobiModel(AbstractModel):
h5.put_array( h5.put_array(
h5_field, np.array(self.inner.getAttr(gp_field, gp_vars), dtype=float) h5_field, np.array(self.inner.getAttr(gp_field, gp_vars), dtype=float)
) )
obj = self.inner.getObjective()
if isinstance(obj, gp.QuadExpr):
nvars = len(self.inner.getVars())
obj_q = np.zeros((nvars, nvars))
for i in range(obj.size()):
obj_q[obj.getVar1(i).index, obj.getVar2(i).index] = obj.getCoeff(i)
h5.put_array("static_var_obj_coeffs_quad", obj_q)
def _extract_after_load_constrs(self, h5: H5File) -> None: def _extract_after_load_constrs(self, h5: H5File) -> None:
gp_constrs = self.inner.getConstrs() gp_constrs = self.inner.getConstrs()

View File

@@ -1,17 +1,22 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization # MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved. # Copyright (C) 2020-2025, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
import random import random
from tempfile import TemporaryDirectory
import numpy as np import numpy as np
from miplearn.problems.maxcut import MaxCutGenerator, build_maxcut_model_gurobipy
from scipy.stats import randint, uniform from scipy.stats import randint, uniform
from miplearn.h5 import H5File
from miplearn.problems.maxcut import MaxCutGenerator, build_maxcut_model_gurobipy
def _set_seed(): def _set_seed():
random.seed(42) random.seed(42)
np.random.seed(42) np.random.seed(42)
def test_maxcut_generator_not_fixed() -> None: def test_maxcut_generator_not_fixed() -> None:
_set_seed() _set_seed()
gen = MaxCutGenerator( gen = MaxCutGenerator(
@@ -22,12 +27,20 @@ def test_maxcut_generator_not_fixed() -> None:
data = gen.generate(3) data = gen.generate(3)
assert len(data) == 3 assert len(data) == 3
assert list(data[0].graph.nodes()) == [0, 1, 2, 3, 4] assert list(data[0].graph.nodes()) == [0, 1, 2, 3, 4]
assert list(data[0].graph.edges()) == [(0, 2), (0, 3), (0, 4), (2, 3), (2, 4), (3, 4)] assert list(data[0].graph.edges()) == [
(0, 2),
(0, 3),
(0, 4),
(2, 3),
(2, 4),
(3, 4),
]
assert data[0].weights.tolist() == [-1, 1, -1, -1, -1, 1] assert data[0].weights.tolist() == [-1, 1, -1, -1, -1, 1]
assert list(data[1].graph.nodes()) == [0, 1, 2, 3, 4] assert list(data[1].graph.nodes()) == [0, 1, 2, 3, 4]
assert list(data[1].graph.edges()) == [(0, 1), (0, 3), (0, 4), (1, 4), (3, 4)] assert list(data[1].graph.edges()) == [(0, 1), (0, 3), (0, 4), (1, 4), (3, 4)]
assert data[1].weights.tolist() == [-1, -1, -1, 1, -1] assert data[1].weights.tolist() == [-1, -1, -1, 1, -1]
def test_maxcut_generator_fixed() -> None: def test_maxcut_generator_fixed() -> None:
random.seed(42) random.seed(42)
np.random.seed(42) np.random.seed(42)
@@ -39,19 +52,67 @@ def test_maxcut_generator_fixed() -> None:
data = gen.generate(3) data = gen.generate(3)
assert len(data) == 3 assert len(data) == 3
assert list(data[0].graph.nodes()) == [0, 1, 2, 3, 4] assert list(data[0].graph.nodes()) == [0, 1, 2, 3, 4]
assert list(data[0].graph.edges()) == [(0, 2), (0, 3), (0, 4), (2, 3), (2, 4), (3, 4)] assert list(data[0].graph.edges()) == [
(0, 2),
(0, 3),
(0, 4),
(2, 3),
(2, 4),
(3, 4),
]
assert data[0].weights.tolist() == [-1, 1, -1, -1, -1, 1] assert data[0].weights.tolist() == [-1, 1, -1, -1, -1, 1]
assert list(data[1].graph.nodes()) == [0, 1, 2, 3, 4] assert list(data[1].graph.nodes()) == [0, 1, 2, 3, 4]
assert list(data[1].graph.edges()) == [(0, 2), (0, 3), (0, 4), (2, 3), (2, 4), (3, 4)] assert list(data[1].graph.edges()) == [
(0, 2),
(0, 3),
(0, 4),
(2, 3),
(2, 4),
(3, 4),
]
assert data[1].weights.tolist() == [-1, -1, -1, 1, -1, -1] assert data[1].weights.tolist() == [-1, -1, -1, 1, -1, -1]
def test_maxcut_model(): def test_maxcut_model():
_set_seed() _set_seed()
data = MaxCutGenerator( data = MaxCutGenerator(
n=randint(low=20, high=21), n=randint(low=10, high=11),
p=uniform(loc=0.5, scale=0.0), p=uniform(loc=0.5, scale=0.0),
fix_graph=True, fix_graph=True,
).generate(1)[0] ).generate(1)[0]
model = build_maxcut_model_gurobipy(data) model = build_maxcut_model_gurobipy(data)
with TemporaryDirectory() as tempdir:
with H5File(f"{tempdir}/data.h5", "w") as h5:
model.extract_after_load(h5)
obj_lin = h5.get_array("static_var_obj_coeffs")
assert obj_lin is not None
assert obj_lin.tolist() == [
3.0,
1.0,
3.0,
1.0,
-1.0,
0.0,
-1.0,
0.0,
-1.0,
0.0,
]
obj_quad = h5.get_array("static_var_obj_coeffs_quad")
assert obj_quad is not None
assert obj_quad.tolist() == [
[0.0, 0.0, -1.0, 1.0, -1.0, 0.0, 0.0, 0.0, -1.0, -1.0],
[0.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, -1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, -1.0, 1.0, -1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, -1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
]
model.optimize() model.optimize()
assert model.inner.ObjVal == -26 assert model.inner.ObjVal == -4