Merge branch 'feature/new-py-api' into feature/docs

This commit is contained in:
2022-02-25 08:36:43 -06:00
28 changed files with 490 additions and 277 deletions

View File

@@ -2,6 +2,7 @@
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from dataclasses import dataclass
from typing import List, Dict, Optional
import numpy as np
@@ -13,6 +14,13 @@ from scipy.stats.distributions import rv_frozen
from miplearn.instance.base import Instance
@dataclass
class MultiKnapsackData:
prices: np.ndarray
capacities: np.ndarray
weights: np.ndarray
class MultiKnapsackInstance(Instance):
"""Representation of the Multidimensional 0-1 Knapsack Problem.

View File

@@ -2,7 +2,8 @@
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from typing import List, Dict
from dataclasses import dataclass
from typing import List
import networkx as nx
import numpy as np
@@ -15,6 +16,12 @@ from scipy.stats.distributions import rv_frozen
from miplearn.instance.base import Instance
@dataclass
class MaxWeightStableSetData:
graph: Graph
weights: np.ndarray
class MaxWeightStableSetInstance(Instance):
"""An instance of the Maximum-Weight Stable Set Problem.
@@ -87,16 +94,30 @@ class MaxWeightStableSetGenerator:
if fix_graph:
self.graph = self._generate_graph()
def generate(self, n_samples: int) -> List[MaxWeightStableSetInstance]:
def _sample() -> MaxWeightStableSetInstance:
def generate(self, n_samples: int) -> List[MaxWeightStableSetData]:
def _sample() -> MaxWeightStableSetData:
if self.graph is not None:
graph = self.graph
else:
graph = self._generate_graph()
weights = self.w.rvs(graph.number_of_nodes())
return MaxWeightStableSetInstance(graph, weights)
return MaxWeightStableSetData(graph, weights)
return [_sample() for _ in range(n_samples)]
def _generate_graph(self) -> Graph:
return nx.generators.random_graphs.binomial_graph(self.n.rvs(), self.p.rvs())
def build_stab_model(data: MaxWeightStableSetData) -> pe.ConcreteModel:
model = pe.ConcreteModel()
nodes = list(data.graph.nodes)
model.x = pe.Var(nodes, domain=pe.Binary)
model.OBJ = pe.Objective(
expr=sum(model.x[v] * data.weights[v] for v in nodes),
sense=pe.maximize,
)
model.clique_eqs = pe.ConstraintList()
for clique in nx.find_cliques(data.graph):
model.clique_eqs.add(sum(model.x[v] for v in clique) <= 1)
return model

View File

@@ -1,7 +1,9 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from typing import List, Tuple, FrozenSet, Any, Optional, Dict
from dataclasses import dataclass
from typing import List, Tuple, Any, Optional, Dict
import networkx as nx
import numpy as np
@@ -17,6 +19,12 @@ from miplearn.solvers.pyomo.base import BasePyomoSolver
from miplearn.types import ConstraintName
@dataclass
class TravelingSalesmanData:
n_cities: int
distances: np.ndarray
class TravelingSalesmanInstance(Instance):
"""An instance ot the Traveling Salesman Problem.
@@ -62,14 +70,15 @@ class TravelingSalesmanInstance(Instance):
self,
solver: InternalSolver,
model: Any,
) -> List[ConstraintName]:
) -> Dict[ConstraintName, List]:
selected_edges = [e for e in self.edges if model.x[e].value > 0.5]
graph = nx.Graph()
graph.add_edges_from(selected_edges)
violations = []
violations = {}
for c in list(nx.connected_components(graph)):
if len(c) < self.n_cities:
violations.append(",".join(map(str, c)).encode())
cname = ("st[" + ",".join(map(str, c)) + "]").encode()
violations[cname] = list(c)
return violations
@overrides
@@ -77,10 +86,9 @@ class TravelingSalesmanInstance(Instance):
self,
solver: InternalSolver,
model: Any,
violation: ConstraintName,
component: List,
) -> None:
assert isinstance(solver, BasePyomoSolver)
component = [int(v) for v in violation.decode().split(",")]
cut_edges = [
e
for e in self.edges
@@ -156,8 +164,8 @@ class TravelingSalesmanGenerator:
self.fixed_n = None
self.fixed_cities = None
def generate(self, n_samples: int) -> List[TravelingSalesmanInstance]:
def _sample() -> TravelingSalesmanInstance:
def generate(self, n_samples: int) -> List[TravelingSalesmanData]:
def _sample() -> TravelingSalesmanData:
if self.fixed_cities is not None:
assert self.fixed_n is not None
n, cities = self.fixed_n, self.fixed_cities
@@ -167,7 +175,7 @@ class TravelingSalesmanGenerator:
distances = np.tril(distances) + np.triu(distances.T, 1)
if self.round:
distances = distances.round()
return TravelingSalesmanInstance(n, distances)
return TravelingSalesmanData(n, distances)
return [_sample() for _ in range(n_samples)]