Store cuts and lazy constraints as JSON in H5

This commit is contained in:
2024-02-01 10:06:21 -06:00
parent 2774edae8c
commit 281508f44c
41 changed files with 30 additions and 19 deletions

View File

@@ -2,8 +2,9 @@
# Copyright (C) 2020-2022, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
import json
import logging
from typing import List, Dict, Any, Hashable, Union
from typing import List, Dict, Any, Hashable
import numpy as np
from sklearn.preprocessing import MultiLabelBinarizer
@@ -15,6 +16,15 @@ from miplearn.solvers.abstract import AbstractModel
logger = logging.getLogger(__name__)
def convert_lists_to_tuples(obj: Any) -> Any:
if isinstance(obj, list):
return tuple(convert_lists_to_tuples(item) for item in obj)
elif isinstance(obj, dict):
return {key: convert_lists_to_tuples(value) for key, value in obj.items()}
else:
return obj
class _BaseMemorizingConstrComponent:
def __init__(self, clf: Any, extractor: FeaturesExtractor, field: str) -> None:
self.clf = clf
@@ -38,8 +48,7 @@ class _BaseMemorizingConstrComponent:
sample_constrs_str = h5.get_scalar(self.field)
assert sample_constrs_str is not None
assert isinstance(sample_constrs_str, str)
sample_constrs = eval(sample_constrs_str)
assert isinstance(sample_constrs, list)
sample_constrs = convert_lists_to_tuples(json.loads(sample_constrs_str))
y_sample = []
for c in sample_constrs:
if c not in constr_to_idx:

View File

@@ -170,11 +170,11 @@ def _stab_read(data: Union[str, MaxWeightStableSetData]) -> MaxWeightStableSetDa
return data
def _stab_separate(data: MaxWeightStableSetData, x_val: List[float]) -> List[Hashable]:
def _stab_separate(data: MaxWeightStableSetData, x_val: List[float]) -> List:
# Check that we selected at most one vertex for each
# clique in the graph (sum <= 1)
violations: List[Hashable] = []
violations: List[Any] = []
for clique in nx.find_cliques(data.graph):
if sum(x_val[i] for i in clique) > 1.0001:
violations.append(tuple(sorted(clique)))
violations.append(sorted(clique))
return violations

View File

@@ -231,18 +231,18 @@ def _tsp_separate(
x_val: dict[Tuple[int, int], float],
edges: List[Tuple[int, int]],
n_cities: int,
) -> List[Tuple[Tuple[int, int], ...]]:
) -> List:
violations = []
selected_edges = [e for e in edges if x_val[e] > 0.5]
graph = nx.Graph()
graph.add_edges_from(selected_edges)
for component in list(nx.connected_components(graph)):
if len(component) < n_cities:
cut_edges = tuple(
(e[0], e[1])
cut_edges = [
[e[0], e[1]]
for e in edges
if (e[0] in component and e[1] not in component)
or (e[0] not in component and e[1] in component)
)
]
violations.append(cut_edges)
return violations

View File

@@ -1,7 +1,9 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2022, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
import logging
import json
from typing import Dict, Optional, Callable, Any, List
import gurobipy as gp
@@ -167,9 +169,9 @@ class GurobiModel(AbstractModel):
pass
self._extract_after_mip_solution_pool(h5)
if self.lazy_ is not None:
h5.put_scalar("mip_lazy", repr(self.lazy_))
h5.put_scalar("mip_lazy", json.dumps(self.lazy_))
if self.cuts_ is not None:
h5.put_scalar("mip_cuts", repr(self.cuts_))
h5.put_scalar("mip_cuts", json.dumps(self.cuts_))
def fix_variables(
self,