diff --git a/miplearn/components/cuts/mem.py b/miplearn/components/cuts/mem.py index 38ca663..3aa93e2 100644 --- a/miplearn/components/cuts/mem.py +++ b/miplearn/components/cuts/mem.py @@ -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: diff --git a/miplearn/problems/stab.py b/miplearn/problems/stab.py index 9a5e502..0f39eb4 100644 --- a/miplearn/problems/stab.py +++ b/miplearn/problems/stab.py @@ -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 diff --git a/miplearn/problems/tsp.py b/miplearn/problems/tsp.py index eb494e1..61ddcf3 100644 --- a/miplearn/problems/tsp.py +++ b/miplearn/problems/tsp.py @@ -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 diff --git a/miplearn/solvers/gurobi.py b/miplearn/solvers/gurobi.py index 38a4da4..18634b3 100644 --- a/miplearn/solvers/gurobi.py +++ b/miplearn/solvers/gurobi.py @@ -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, diff --git a/tests/components/cuts/test_mem.py b/tests/components/cuts/test_mem.py index b029e3d..11797ee 100644 --- a/tests/components/cuts/test_mem.py +++ b/tests/components/cuts/test_mem.py @@ -28,17 +28,17 @@ def test_mem_component_gp( clf.fit.assert_called() x, y = clf.fit.call_args.args assert x.shape == (3, 50) - assert y.shape == (3, 415) + assert y.shape == (3, 412) y = y.tolist() - assert y[0][:20] == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - assert y[1][:20] == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - assert y[2][:20] == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1] + assert y[0][40:50] == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + assert y[1][40:50] == [1, 1, 0, 1, 1, 1, 1, 1, 1, 1] + assert y[2][40:50] == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] # Should store violations assert comp.constrs_ is not None assert comp.n_features_ == 50 - assert comp.n_targets_ == 415 - assert len(comp.constrs_) == 415 + assert comp.n_targets_ == 412 + assert len(comp.constrs_) == 412 # Call before-mip stats: Dict[str, Any] = {} @@ -52,7 +52,7 @@ def test_mem_component_gp( # Should set cuts_aot_ assert model.cuts_aot_ is not None - assert len(model.cuts_aot_) == 285 + assert len(model.cuts_aot_) == 256 def test_usage_stab( diff --git a/tests/fixtures/stab-gp-n50-00000.h5 b/tests/fixtures/stab-gp-n50-00000.h5 index c7b6b94..720476a 100644 Binary files a/tests/fixtures/stab-gp-n50-00000.h5 and b/tests/fixtures/stab-gp-n50-00000.h5 differ diff --git a/tests/fixtures/stab-gp-n50-00000.mps.gz b/tests/fixtures/stab-gp-n50-00000.mps.gz index 97dcf77..c6b6ffb 100644 Binary files a/tests/fixtures/stab-gp-n50-00000.mps.gz and b/tests/fixtures/stab-gp-n50-00000.mps.gz differ diff --git a/tests/fixtures/stab-gp-n50-00000.pkl.gz b/tests/fixtures/stab-gp-n50-00000.pkl.gz index 857ff69..39d687c 100644 Binary files a/tests/fixtures/stab-gp-n50-00000.pkl.gz and b/tests/fixtures/stab-gp-n50-00000.pkl.gz differ diff --git a/tests/fixtures/stab-gp-n50-00001.h5 b/tests/fixtures/stab-gp-n50-00001.h5 index 4b81630..b72b25d 100644 Binary files a/tests/fixtures/stab-gp-n50-00001.h5 and b/tests/fixtures/stab-gp-n50-00001.h5 differ diff --git a/tests/fixtures/stab-gp-n50-00001.mps.gz b/tests/fixtures/stab-gp-n50-00001.mps.gz index c15de10..5c9d091 100644 Binary files a/tests/fixtures/stab-gp-n50-00001.mps.gz and b/tests/fixtures/stab-gp-n50-00001.mps.gz differ diff --git a/tests/fixtures/stab-gp-n50-00001.pkl.gz b/tests/fixtures/stab-gp-n50-00001.pkl.gz index 366c897..5b00d9e 100644 Binary files a/tests/fixtures/stab-gp-n50-00001.pkl.gz and b/tests/fixtures/stab-gp-n50-00001.pkl.gz differ diff --git a/tests/fixtures/stab-gp-n50-00002.h5 b/tests/fixtures/stab-gp-n50-00002.h5 index bfb2bc8..e2f0af6 100644 Binary files a/tests/fixtures/stab-gp-n50-00002.h5 and b/tests/fixtures/stab-gp-n50-00002.h5 differ diff --git a/tests/fixtures/stab-gp-n50-00002.mps.gz b/tests/fixtures/stab-gp-n50-00002.mps.gz index df177f9..3292356 100644 Binary files a/tests/fixtures/stab-gp-n50-00002.mps.gz and b/tests/fixtures/stab-gp-n50-00002.mps.gz differ diff --git a/tests/fixtures/stab-gp-n50-00002.pkl.gz b/tests/fixtures/stab-gp-n50-00002.pkl.gz index dcf673a..a51419a 100644 Binary files a/tests/fixtures/stab-gp-n50-00002.pkl.gz and b/tests/fixtures/stab-gp-n50-00002.pkl.gz differ diff --git a/tests/fixtures/stab-pyo-n50-00000.h5 b/tests/fixtures/stab-pyo-n50-00000.h5 index c22e820..1d6e921 100644 Binary files a/tests/fixtures/stab-pyo-n50-00000.h5 and b/tests/fixtures/stab-pyo-n50-00000.h5 differ diff --git a/tests/fixtures/stab-pyo-n50-00000.mps.gz b/tests/fixtures/stab-pyo-n50-00000.mps.gz index ad90a35..6f96752 100644 Binary files a/tests/fixtures/stab-pyo-n50-00000.mps.gz and b/tests/fixtures/stab-pyo-n50-00000.mps.gz differ diff --git a/tests/fixtures/stab-pyo-n50-00000.pkl.gz b/tests/fixtures/stab-pyo-n50-00000.pkl.gz index e6f426f..9ba08f1 100644 Binary files a/tests/fixtures/stab-pyo-n50-00000.pkl.gz and b/tests/fixtures/stab-pyo-n50-00000.pkl.gz differ diff --git a/tests/fixtures/stab-pyo-n50-00001.h5 b/tests/fixtures/stab-pyo-n50-00001.h5 index 518c5b0..902b8d3 100644 Binary files a/tests/fixtures/stab-pyo-n50-00001.h5 and b/tests/fixtures/stab-pyo-n50-00001.h5 differ diff --git a/tests/fixtures/stab-pyo-n50-00001.mps.gz b/tests/fixtures/stab-pyo-n50-00001.mps.gz index 20fdf5c..55127a7 100644 Binary files a/tests/fixtures/stab-pyo-n50-00001.mps.gz and b/tests/fixtures/stab-pyo-n50-00001.mps.gz differ diff --git a/tests/fixtures/stab-pyo-n50-00001.pkl.gz b/tests/fixtures/stab-pyo-n50-00001.pkl.gz index 933b70e..dc77411 100644 Binary files a/tests/fixtures/stab-pyo-n50-00001.pkl.gz and b/tests/fixtures/stab-pyo-n50-00001.pkl.gz differ diff --git a/tests/fixtures/stab-pyo-n50-00002.h5 b/tests/fixtures/stab-pyo-n50-00002.h5 index 4b33fdd..3b1052b 100644 Binary files a/tests/fixtures/stab-pyo-n50-00002.h5 and b/tests/fixtures/stab-pyo-n50-00002.h5 differ diff --git a/tests/fixtures/stab-pyo-n50-00002.mps.gz b/tests/fixtures/stab-pyo-n50-00002.mps.gz index 5fceef5..cd5aecc 100644 Binary files a/tests/fixtures/stab-pyo-n50-00002.mps.gz and b/tests/fixtures/stab-pyo-n50-00002.mps.gz differ diff --git a/tests/fixtures/stab-pyo-n50-00002.pkl.gz b/tests/fixtures/stab-pyo-n50-00002.pkl.gz index 0f38344..5ccd75c 100644 Binary files a/tests/fixtures/stab-pyo-n50-00002.pkl.gz and b/tests/fixtures/stab-pyo-n50-00002.pkl.gz differ diff --git a/tests/fixtures/tsp-gp-n20-00000.h5 b/tests/fixtures/tsp-gp-n20-00000.h5 index 9313b70..92296fb 100644 Binary files a/tests/fixtures/tsp-gp-n20-00000.h5 and b/tests/fixtures/tsp-gp-n20-00000.h5 differ diff --git a/tests/fixtures/tsp-gp-n20-00000.mps.gz b/tests/fixtures/tsp-gp-n20-00000.mps.gz index a2d48c6..92ed39d 100644 Binary files a/tests/fixtures/tsp-gp-n20-00000.mps.gz and b/tests/fixtures/tsp-gp-n20-00000.mps.gz differ diff --git a/tests/fixtures/tsp-gp-n20-00000.pkl.gz b/tests/fixtures/tsp-gp-n20-00000.pkl.gz index ef6c835..e45f152 100644 Binary files a/tests/fixtures/tsp-gp-n20-00000.pkl.gz and b/tests/fixtures/tsp-gp-n20-00000.pkl.gz differ diff --git a/tests/fixtures/tsp-gp-n20-00001.h5 b/tests/fixtures/tsp-gp-n20-00001.h5 index 6ab818d..ddac4b8 100644 Binary files a/tests/fixtures/tsp-gp-n20-00001.h5 and b/tests/fixtures/tsp-gp-n20-00001.h5 differ diff --git a/tests/fixtures/tsp-gp-n20-00001.mps.gz b/tests/fixtures/tsp-gp-n20-00001.mps.gz index a12acd7..906ef83 100644 Binary files a/tests/fixtures/tsp-gp-n20-00001.mps.gz and b/tests/fixtures/tsp-gp-n20-00001.mps.gz differ diff --git a/tests/fixtures/tsp-gp-n20-00001.pkl.gz b/tests/fixtures/tsp-gp-n20-00001.pkl.gz index b95efd0..7e8c5a8 100644 Binary files a/tests/fixtures/tsp-gp-n20-00001.pkl.gz and b/tests/fixtures/tsp-gp-n20-00001.pkl.gz differ diff --git a/tests/fixtures/tsp-gp-n20-00002.h5 b/tests/fixtures/tsp-gp-n20-00002.h5 index 7cafa84..e92772e 100644 Binary files a/tests/fixtures/tsp-gp-n20-00002.h5 and b/tests/fixtures/tsp-gp-n20-00002.h5 differ diff --git a/tests/fixtures/tsp-gp-n20-00002.mps.gz b/tests/fixtures/tsp-gp-n20-00002.mps.gz index 4a99cce..ce6af6a 100644 Binary files a/tests/fixtures/tsp-gp-n20-00002.mps.gz and b/tests/fixtures/tsp-gp-n20-00002.mps.gz differ diff --git a/tests/fixtures/tsp-gp-n20-00002.pkl.gz b/tests/fixtures/tsp-gp-n20-00002.pkl.gz index 313606c..ba1d9bd 100644 Binary files a/tests/fixtures/tsp-gp-n20-00002.pkl.gz and b/tests/fixtures/tsp-gp-n20-00002.pkl.gz differ diff --git a/tests/fixtures/tsp-pyo-n20-00000.h5 b/tests/fixtures/tsp-pyo-n20-00000.h5 index 02a0f98..ef58e0b 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00000.h5 and b/tests/fixtures/tsp-pyo-n20-00000.h5 differ diff --git a/tests/fixtures/tsp-pyo-n20-00000.mps.gz b/tests/fixtures/tsp-pyo-n20-00000.mps.gz index 667cb01..1063074 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00000.mps.gz and b/tests/fixtures/tsp-pyo-n20-00000.mps.gz differ diff --git a/tests/fixtures/tsp-pyo-n20-00000.pkl.gz b/tests/fixtures/tsp-pyo-n20-00000.pkl.gz index d927584..c650ba3 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00000.pkl.gz and b/tests/fixtures/tsp-pyo-n20-00000.pkl.gz differ diff --git a/tests/fixtures/tsp-pyo-n20-00001.h5 b/tests/fixtures/tsp-pyo-n20-00001.h5 index 35e07dd..6065822 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00001.h5 and b/tests/fixtures/tsp-pyo-n20-00001.h5 differ diff --git a/tests/fixtures/tsp-pyo-n20-00001.mps.gz b/tests/fixtures/tsp-pyo-n20-00001.mps.gz index 3855d9a..04a3d21 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00001.mps.gz and b/tests/fixtures/tsp-pyo-n20-00001.mps.gz differ diff --git a/tests/fixtures/tsp-pyo-n20-00001.pkl.gz b/tests/fixtures/tsp-pyo-n20-00001.pkl.gz index 293e30c..3f0f568 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00001.pkl.gz and b/tests/fixtures/tsp-pyo-n20-00001.pkl.gz differ diff --git a/tests/fixtures/tsp-pyo-n20-00002.h5 b/tests/fixtures/tsp-pyo-n20-00002.h5 index 06cd880..b200077 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00002.h5 and b/tests/fixtures/tsp-pyo-n20-00002.h5 differ diff --git a/tests/fixtures/tsp-pyo-n20-00002.mps.gz b/tests/fixtures/tsp-pyo-n20-00002.mps.gz index 1de5170..5996b94 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00002.mps.gz and b/tests/fixtures/tsp-pyo-n20-00002.mps.gz differ diff --git a/tests/fixtures/tsp-pyo-n20-00002.pkl.gz b/tests/fixtures/tsp-pyo-n20-00002.pkl.gz index d24be47..087a067 100644 Binary files a/tests/fixtures/tsp-pyo-n20-00002.pkl.gz and b/tests/fixtures/tsp-pyo-n20-00002.pkl.gz differ