Source code for eqc_models.combinatorics.setcover
- r"""
- SetCoverModel solves the mathematical programming problem
- $$
- \mathrm{minimize}_x \sum_{x_i:X_i \in X} c_i x_i
- $$
- Subject to
- $$
- \sum_{i:a\in X_i} x_j \geq 1 \, \forall a \in A}
- $$$
- and
- $$
- x_i \in \{0, 1\} \forall {x_i: X_i \in X}
- $$
- Where $S$ is a set of all elements, $X$ is a collection of sets $X_i$, and the union of all is equal to $S$.
- """
- from typing import List
- import numpy as np
- from eqc_models.base import ConstrainedQuadraticModel
- [docs]
- class SetCoverModel(ConstrainedQuadraticModel):
- """
- Parameters
- -------------
- subsets : List
- List of sets where the union of all sets is S
- weights : List
- List of weights where each weight is the cost of choosing the subset
- corresponding to the index of the weight.
- >>> X = [set(['A', 'B']), set(['B', 'C']), set(['C'])]
- >>> weights = [2, 2, 1]
- >>> model = SetCoverModel(X, weights)
- >>> model.penalty_multiplier = 2
- >>> from eqc_models.solvers import Dirac3IntegerCloudSolver
- >>> solver = Dirac3IntegerCloudSolver()
- >>> response = solver.solve(model, relaxation_schedule=1, num_samples=5) #doctest: +ELLIPSIS
- 20...
- >>> solutions = response["results"]["solutions"]
- >>> solutions[0]
- [1, 0, 1, 0, 0, 0]
- >>> model.decode(solutions[0])
- [{'B', 'A'}, {'C'}]
- """
- def __init__(self, subsets, weights):
-
- self.X = X = list(subsets)
- self.S = S = set()
- for x in subsets:
- S = S.union(x)
-
- elements = [a for a in S]
- elements.sort()
-
- A = []
- b = []
- variables = [f'x_{i}' for i in range(len(X))]
- pos = 0
- for a in elements:
- variables.append(f"s_{pos}")
- constraint = [1 if a in X[i] else 0 for i in range(len(X))]
- slacks = [0 for i in range(len(S))]
- slacks[pos] = -1
- A.append(constraint + slacks)
- pos += 1
- b.append(1)
- n = len(variables)
- J = np.zeros((n, n))
- h = np.zeros((n, ))
- h[:len(weights)] = weights
-
- super(SetCoverModel, self).__init__(h, J, np.array(A), np.array(b))
-
- self.upper_bound = np.array([1 for i in range(len(weights))] + [len(X)-1 for i in range(n-len(weights))])
- [docs]
- def decode(self, solution) -> List:
- xbar = []
- for i in range(len(self.X)):
- if solution[i] > 0.5:
- xbar.append(self.X[i])
- return xbar