eqc-models 0.9.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- eqc_models-0.9.8.data/platlib/compile_extensions.py +23 -0
- eqc_models-0.9.8.data/platlib/eqc_models/__init__.py +15 -0
- eqc_models-0.9.8.data/platlib/eqc_models/algorithms/__init__.py +4 -0
- eqc_models-0.9.8.data/platlib/eqc_models/algorithms/base.py +10 -0
- eqc_models-0.9.8.data/platlib/eqc_models/algorithms/penaltymultiplier.py +169 -0
- eqc_models-0.9.8.data/platlib/eqc_models/allocation/__init__.py +6 -0
- eqc_models-0.9.8.data/platlib/eqc_models/allocation/allocation.py +367 -0
- eqc_models-0.9.8.data/platlib/eqc_models/allocation/portbase.py +128 -0
- eqc_models-0.9.8.data/platlib/eqc_models/allocation/portmomentum.py +137 -0
- eqc_models-0.9.8.data/platlib/eqc_models/assignment/__init__.py +5 -0
- eqc_models-0.9.8.data/platlib/eqc_models/assignment/qap.py +82 -0
- eqc_models-0.9.8.data/platlib/eqc_models/assignment/setpartition.py +170 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/__init__.py +72 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/base.py +150 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/constraints.py +276 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/operators.py +201 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.c +11363 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.cpython-310-darwin.so +0 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.pyx +72 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/polynomial.py +274 -0
- eqc_models-0.9.8.data/platlib/eqc_models/base/quadratic.py +250 -0
- eqc_models-0.9.8.data/platlib/eqc_models/decoding.py +20 -0
- eqc_models-0.9.8.data/platlib/eqc_models/graph/__init__.py +5 -0
- eqc_models-0.9.8.data/platlib/eqc_models/graph/base.py +63 -0
- eqc_models-0.9.8.data/platlib/eqc_models/graph/hypergraph.py +307 -0
- eqc_models-0.9.8.data/platlib/eqc_models/graph/maxcut.py +155 -0
- eqc_models-0.9.8.data/platlib/eqc_models/graph/maxkcut.py +184 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/__init__.py +15 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierbase.py +99 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierqboost.py +423 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierqsvm.py +237 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/clustering.py +323 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/clusteringbase.py +112 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/decomposition.py +363 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/forecast.py +255 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/forecastbase.py +139 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/regressor.py +220 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/regressorbase.py +97 -0
- eqc_models-0.9.8.data/platlib/eqc_models/ml/reservoir.py +106 -0
- eqc_models-0.9.8.data/platlib/eqc_models/sequence/__init__.py +5 -0
- eqc_models-0.9.8.data/platlib/eqc_models/sequence/tsp.py +217 -0
- eqc_models-0.9.8.data/platlib/eqc_models/solvers/__init__.py +12 -0
- eqc_models-0.9.8.data/platlib/eqc_models/solvers/qciclient.py +707 -0
- eqc_models-0.9.8.data/platlib/eqc_models/utilities/__init__.py +6 -0
- eqc_models-0.9.8.data/platlib/eqc_models/utilities/fileio.py +38 -0
- eqc_models-0.9.8.data/platlib/eqc_models/utilities/polynomial.py +137 -0
- eqc_models-0.9.8.data/platlib/eqc_models/utilities/qplib.py +375 -0
- eqc_models-0.9.8.dist-info/LICENSE.txt +202 -0
- eqc_models-0.9.8.dist-info/METADATA +139 -0
- eqc_models-0.9.8.dist-info/RECORD +52 -0
- eqc_models-0.9.8.dist-info/WHEEL +5 -0
- eqc_models-0.9.8.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# (C) Quantum Computing Inc., 2024.
|
|
2
|
+
"""
|
|
3
|
+
# Traveling Salesman Problem
|
|
4
|
+
|
|
5
|
+
## Class listing
|
|
6
|
+
|
|
7
|
+
`class TSPModel` - a base class
|
|
8
|
+
`class MTZTSPModel` - a concrete class for the MTZ formulation of a TSP
|
|
9
|
+
|
|
10
|
+
MTZ has been chosen for a demonstration of the integer capability of the
|
|
11
|
+
Dirac devices. A feasible solution will produce the valid sequence of
|
|
12
|
+
visits by simply ordering the nodes by the $s_i$ variables. In the literature,
|
|
13
|
+
the ordering variables are named with $u$, but this is avoided because the
|
|
14
|
+
graph notation with $u$ being a tail node and $v$ being a head node is used
|
|
15
|
+
here. In conjunction with this notation, the variables $x_{ij}$ reference the
|
|
16
|
+
node index where the index of node $u$ is $i$ and the index of node $v$ is
|
|
17
|
+
$j$.
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from typing import (Dict, Tuple)
|
|
22
|
+
import numpy as np
|
|
23
|
+
from eqc_models.base import ConstrainedPolynomialModel, InequalitiesMixin
|
|
24
|
+
from eqc_models.base.operators import Polynomial
|
|
25
|
+
|
|
26
|
+
class TSPModel(ConstrainedPolynomialModel):
|
|
27
|
+
"""
|
|
28
|
+
The TSPModel class implements the basics for building different formualtions of
|
|
29
|
+
TSP models.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, D : Dict[Tuple[int, int], float]):
|
|
34
|
+
self.D = D
|
|
35
|
+
self.nodes = nodes = set()
|
|
36
|
+
self.edges = edges = set()
|
|
37
|
+
for (u, v) in D.keys():
|
|
38
|
+
nodes.add(u)
|
|
39
|
+
nodes.add(v)
|
|
40
|
+
assert (u, v) not in edges, "Only a single edge from tail to head is allowed"
|
|
41
|
+
edges.add((u, v))
|
|
42
|
+
# set N to the number of nodes
|
|
43
|
+
self.N = len(nodes)
|
|
44
|
+
self.variables = None
|
|
45
|
+
|
|
46
|
+
def distance(self, i : int, j : int) -> float:
|
|
47
|
+
"""
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
i: int
|
|
51
|
+
index of first node
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
|
|
55
|
+
float
|
|
56
|
+
|
|
57
|
+
Retrieves the distance between nodes at indexes i and j. Supports asymmetric
|
|
58
|
+
(D[i, j] <> D[j, i]) distances.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
return self.D[i, j]
|
|
64
|
+
|
|
65
|
+
def cost(self, solution : np.ndarray) -> float:
|
|
66
|
+
"""
|
|
67
|
+
Solution cost is the sum of all D values where (i,j) is chosen
|
|
68
|
+
|
|
69
|
+
Parameters:
|
|
70
|
+
:solution: np.ndarray - An array of of 0,1 values which describe a route
|
|
71
|
+
depending on the formulation chosen.
|
|
72
|
+
Returns: float
|
|
73
|
+
|
|
74
|
+
"""
|
|
75
|
+
raise NotImplementedError("Subclass must implement cost method")
|
|
76
|
+
|
|
77
|
+
class MTZTSPModel(InequalitiesMixin, TSPModel):
|
|
78
|
+
"""
|
|
79
|
+
Using the Miller Tucker Zemlin (1960) formulation of TSP, create a model
|
|
80
|
+
instance that contains variables for node order (to eliminate subtours)
|
|
81
|
+
and edge choices.
|
|
82
|
+
|
|
83
|
+
>>> D = {(1, 2): 1, (2, 1): 1, (1, 3): 2, (3, 1): 2, (2, 3): 3, (3, 2): 3}
|
|
84
|
+
>>> model = MTZTSPModel(D)
|
|
85
|
+
>>> model.penalty_multiplier = 10
|
|
86
|
+
>>> model.distance(1, 2)
|
|
87
|
+
1
|
|
88
|
+
>>> model.distance(3, 2)
|
|
89
|
+
3
|
|
90
|
+
>>> solution = np.array([1, 0, 0, 1, 1, 0, 1, 2, 3, 4, 4, 2, 5])
|
|
91
|
+
>>> model.cost(solution)
|
|
92
|
+
6
|
|
93
|
+
>>> lhs, rhs = model.constraints
|
|
94
|
+
>>> (lhs@solution - rhs == 0).all()
|
|
95
|
+
True
|
|
96
|
+
>>> poly = model.polynomial
|
|
97
|
+
>>> poly.evaluate(solution) + model.alpha * model.offset
|
|
98
|
+
6.0
|
|
99
|
+
>>> infeasible = np.array([0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4, 2, 5])
|
|
100
|
+
>>> Pl, Pq = -2 * rhs.T@lhs, lhs.T@lhs
|
|
101
|
+
>>> Pl.T@solution + solution.T@Pq@solution + model.offset
|
|
102
|
+
0.0
|
|
103
|
+
>>> Pl.T@infeasible + infeasible.T@Pq@infeasible + model.offset > 0
|
|
104
|
+
True
|
|
105
|
+
>>> poly.evaluate(infeasible) + model.alpha * model.offset > 0
|
|
106
|
+
True
|
|
107
|
+
>>> infeasible = 1 - np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
108
|
+
>>> poly.evaluate(infeasible) + model.alpha * model.offset > 6
|
|
109
|
+
True
|
|
110
|
+
>>> val1 = poly.evaluate(infeasible) + model.alpha * model.offset
|
|
111
|
+
>>> model.penalty_multiplier *= 2
|
|
112
|
+
>>> poly2 = model.polynomial
|
|
113
|
+
>>> val2 = poly2.evaluate(infeasible) + model.alpha * model.offset
|
|
114
|
+
>>> val1 < val2
|
|
115
|
+
True
|
|
116
|
+
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
def __init__(self, D : Dict[Tuple[int, int], float]) -> None:
|
|
120
|
+
super(MTZTSPModel, self).__init__(D)
|
|
121
|
+
self.variables = variables = []
|
|
122
|
+
coefficients = []
|
|
123
|
+
indices = []
|
|
124
|
+
for (u, v) in D.keys():
|
|
125
|
+
varname = f"x_{u}_{v}"
|
|
126
|
+
varidx = len(variables)
|
|
127
|
+
variables.append(varname)
|
|
128
|
+
indices.append((0, varidx+1))
|
|
129
|
+
coefficients.append(D[(u, v)])
|
|
130
|
+
for u in self.nodes:
|
|
131
|
+
variables.append(f"s_{u}")
|
|
132
|
+
self.coefficients = coefficients
|
|
133
|
+
self.indices = indices
|
|
134
|
+
self.max_order = 2
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def constraints(self) -> Tuple[np.ndarray, np.ndarray]:
|
|
138
|
+
"""
|
|
139
|
+
Build the constraints: Two constraints for every node, one for the
|
|
140
|
+
edge chosen to enter and another for the edge chosen to leave.
|
|
141
|
+
One constraint for every edge not leading to the depot.
|
|
142
|
+
|
|
143
|
+
Returns: 2-Tuple of numpy arrays, one as lefthand side and the other
|
|
144
|
+
for the righthand side. $Ax = b$
|
|
145
|
+
|
|
146
|
+
"""
|
|
147
|
+
# choose a depot node
|
|
148
|
+
depot = list(self.nodes)[0]
|
|
149
|
+
depot_in = [uv for uv in self.edges if uv[-1] == depot]
|
|
150
|
+
m = 2*self.N+len(self.edges) - len(depot_in)
|
|
151
|
+
lhs = np.ndarray((m, self.n), dtype=np.int32)
|
|
152
|
+
rhs = np.ndarray((m,), dtype=np.int32)
|
|
153
|
+
senses = ["EQ" for i in range(m)]
|
|
154
|
+
for idx, node in enumerate(self.nodes):
|
|
155
|
+
rhs[idx] = 1
|
|
156
|
+
rhs[self.N + idx] = 1
|
|
157
|
+
for (u, v) in self.edges:
|
|
158
|
+
if v == node:
|
|
159
|
+
varname = f"x_{u}_{v}"
|
|
160
|
+
varidx = self.variables.index(varname)
|
|
161
|
+
lhs[idx, varidx] = 1
|
|
162
|
+
elif u == node:
|
|
163
|
+
varname = f"x_{u}_{v}"
|
|
164
|
+
varidx = self.variables.index(varname)
|
|
165
|
+
lhs[self.N+idx, varidx] = 1
|
|
166
|
+
# build these subtour elimination constraints
|
|
167
|
+
# s_i - s_j + N x_{ij} <= N - 1
|
|
168
|
+
idx = 0
|
|
169
|
+
for (u, v) in self.edges:
|
|
170
|
+
if v != depot:
|
|
171
|
+
senses[2*self.N + idx] = "LE"
|
|
172
|
+
lhs[2*self.N + idx, varidx] = self.N - 1
|
|
173
|
+
vidx = self.variables.index(f"s_{v}")
|
|
174
|
+
lhs[2*self.N + idx, vidx] = -1
|
|
175
|
+
uidx = self.variables.index(f"s_{u}")
|
|
176
|
+
lhs[2*self.N + idx, uidx] = 1
|
|
177
|
+
rhs[2*self.N + idx] = self.N
|
|
178
|
+
idx += 1
|
|
179
|
+
# update the constraint senses
|
|
180
|
+
self.senses = senses
|
|
181
|
+
self.lhs = lhs
|
|
182
|
+
self.rhs = rhs
|
|
183
|
+
# let the superclass handle the rest
|
|
184
|
+
return super(MTZTSPModel, self).constraints
|
|
185
|
+
|
|
186
|
+
def cost(self, solution : np.ndarray) -> float:
|
|
187
|
+
"""
|
|
188
|
+
Solution cost is the sum of all D values where (i,j) is chosen
|
|
189
|
+
|
|
190
|
+
Parameters:
|
|
191
|
+
:solution: np.ndarray - An array of of 0,1 values which describe a route
|
|
192
|
+
by setting variables representing active edges to 1.
|
|
193
|
+
Returns: float
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
# get the route selection variables
|
|
198
|
+
cost_val = 0
|
|
199
|
+
for (u, v) in self.edges:
|
|
200
|
+
varname = f"x_{u}_{v}"
|
|
201
|
+
varidx = self.variables.index(varname)
|
|
202
|
+
cost_val += solution[varidx] * self.D[(u, v)]
|
|
203
|
+
return cost_val
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def upper_bound(self) -> np.ndarray:
|
|
207
|
+
"""
|
|
208
|
+
For all route variables, the domain is {0, 1}. The sequence variables
|
|
209
|
+
can take on values in [0, 1, 2, ..., N].
|
|
210
|
+
|
|
211
|
+
"""
|
|
212
|
+
# Since the sequence variables are all required, but the route variables
|
|
213
|
+
# are not, just reference the count from the end to specify the sequence vars
|
|
214
|
+
upper_bound = np.ones((len(self.variables),))
|
|
215
|
+
upper_bound[-self.N:] = self.N
|
|
216
|
+
return upper_bound
|
|
217
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# (C) Quantum Computing Inc., 2024.
|
|
2
|
+
try:
|
|
3
|
+
from .eqcdirect import Dirac3DirectSolver, EqcDirectSolver
|
|
4
|
+
except ImportError:
|
|
5
|
+
# eqc-direct is not available
|
|
6
|
+
Dirac3DirectSolver = None
|
|
7
|
+
from .qciclient import (Dirac1CloudSolver, Dirac3CloudSolver, QciClientSolver,
|
|
8
|
+
Dirac3IntegerCloudSolver, Dirac3ContinuousCloudSolver)
|
|
9
|
+
|
|
10
|
+
__all__ = ["Dirac3DirectSolver", "Dirac1CloudSolver", "Dirac3CloudSolver",
|
|
11
|
+
"EqcDirectSolver", "QciClientSolver", "Dirac3IntegerCloudSolver",
|
|
12
|
+
"Dirac3ContinuousCloudSolver"]
|