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.
Files changed (52) hide show
  1. eqc_models-0.9.8.data/platlib/compile_extensions.py +23 -0
  2. eqc_models-0.9.8.data/platlib/eqc_models/__init__.py +15 -0
  3. eqc_models-0.9.8.data/platlib/eqc_models/algorithms/__init__.py +4 -0
  4. eqc_models-0.9.8.data/platlib/eqc_models/algorithms/base.py +10 -0
  5. eqc_models-0.9.8.data/platlib/eqc_models/algorithms/penaltymultiplier.py +169 -0
  6. eqc_models-0.9.8.data/platlib/eqc_models/allocation/__init__.py +6 -0
  7. eqc_models-0.9.8.data/platlib/eqc_models/allocation/allocation.py +367 -0
  8. eqc_models-0.9.8.data/platlib/eqc_models/allocation/portbase.py +128 -0
  9. eqc_models-0.9.8.data/platlib/eqc_models/allocation/portmomentum.py +137 -0
  10. eqc_models-0.9.8.data/platlib/eqc_models/assignment/__init__.py +5 -0
  11. eqc_models-0.9.8.data/platlib/eqc_models/assignment/qap.py +82 -0
  12. eqc_models-0.9.8.data/platlib/eqc_models/assignment/setpartition.py +170 -0
  13. eqc_models-0.9.8.data/platlib/eqc_models/base/__init__.py +72 -0
  14. eqc_models-0.9.8.data/platlib/eqc_models/base/base.py +150 -0
  15. eqc_models-0.9.8.data/platlib/eqc_models/base/constraints.py +276 -0
  16. eqc_models-0.9.8.data/platlib/eqc_models/base/operators.py +201 -0
  17. eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.c +11363 -0
  18. eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.cpython-310-darwin.so +0 -0
  19. eqc_models-0.9.8.data/platlib/eqc_models/base/polyeval.pyx +72 -0
  20. eqc_models-0.9.8.data/platlib/eqc_models/base/polynomial.py +274 -0
  21. eqc_models-0.9.8.data/platlib/eqc_models/base/quadratic.py +250 -0
  22. eqc_models-0.9.8.data/platlib/eqc_models/decoding.py +20 -0
  23. eqc_models-0.9.8.data/platlib/eqc_models/graph/__init__.py +5 -0
  24. eqc_models-0.9.8.data/platlib/eqc_models/graph/base.py +63 -0
  25. eqc_models-0.9.8.data/platlib/eqc_models/graph/hypergraph.py +307 -0
  26. eqc_models-0.9.8.data/platlib/eqc_models/graph/maxcut.py +155 -0
  27. eqc_models-0.9.8.data/platlib/eqc_models/graph/maxkcut.py +184 -0
  28. eqc_models-0.9.8.data/platlib/eqc_models/ml/__init__.py +15 -0
  29. eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierbase.py +99 -0
  30. eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierqboost.py +423 -0
  31. eqc_models-0.9.8.data/platlib/eqc_models/ml/classifierqsvm.py +237 -0
  32. eqc_models-0.9.8.data/platlib/eqc_models/ml/clustering.py +323 -0
  33. eqc_models-0.9.8.data/platlib/eqc_models/ml/clusteringbase.py +112 -0
  34. eqc_models-0.9.8.data/platlib/eqc_models/ml/decomposition.py +363 -0
  35. eqc_models-0.9.8.data/platlib/eqc_models/ml/forecast.py +255 -0
  36. eqc_models-0.9.8.data/platlib/eqc_models/ml/forecastbase.py +139 -0
  37. eqc_models-0.9.8.data/platlib/eqc_models/ml/regressor.py +220 -0
  38. eqc_models-0.9.8.data/platlib/eqc_models/ml/regressorbase.py +97 -0
  39. eqc_models-0.9.8.data/platlib/eqc_models/ml/reservoir.py +106 -0
  40. eqc_models-0.9.8.data/platlib/eqc_models/sequence/__init__.py +5 -0
  41. eqc_models-0.9.8.data/platlib/eqc_models/sequence/tsp.py +217 -0
  42. eqc_models-0.9.8.data/platlib/eqc_models/solvers/__init__.py +12 -0
  43. eqc_models-0.9.8.data/platlib/eqc_models/solvers/qciclient.py +707 -0
  44. eqc_models-0.9.8.data/platlib/eqc_models/utilities/__init__.py +6 -0
  45. eqc_models-0.9.8.data/platlib/eqc_models/utilities/fileio.py +38 -0
  46. eqc_models-0.9.8.data/platlib/eqc_models/utilities/polynomial.py +137 -0
  47. eqc_models-0.9.8.data/platlib/eqc_models/utilities/qplib.py +375 -0
  48. eqc_models-0.9.8.dist-info/LICENSE.txt +202 -0
  49. eqc_models-0.9.8.dist-info/METADATA +139 -0
  50. eqc_models-0.9.8.dist-info/RECORD +52 -0
  51. eqc_models-0.9.8.dist-info/WHEEL +5 -0
  52. eqc_models-0.9.8.dist-info/top_level.txt +2 -0
@@ -0,0 +1,237 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+ # Import libs
3
+ import os
4
+ import sys
5
+ import time
6
+ import datetime
7
+ import json
8
+ import warnings
9
+ from functools import wraps
10
+ import numpy as np
11
+
12
+ from eqc_models.ml.classifierbase import ClassifierBase
13
+
14
+
15
+ class QSVMClassifier(ClassifierBase):
16
+ """An implementation of QSVM classifier that uses QCi's Dirac-3.
17
+
18
+ Parameters
19
+ ----------
20
+
21
+ relaxation_schedule: Relaxation schedule used by Dirac-3; default:
22
+ 2.
23
+
24
+ num_samples: Number of samples used by Dirac-3; default: 1.
25
+
26
+ upper_limit: Coefficient upper limit; a regularization parameter;
27
+ default: 1.0.
28
+
29
+ gamma: Gaussian kernel parameter; default: 1.0.
30
+
31
+ eta: A penalty multiplier; default: 1.0.
32
+
33
+ zeta: A penalty multiplier; default: 1.0.
34
+
35
+ Examples
36
+ -----------
37
+
38
+ >>> from sklearn import datasets
39
+ >>> from sklearn.preprocessing import MinMaxScaler
40
+ >>> from sklearn.model_selection import train_test_split
41
+ >>> iris = datasets.load_iris()
42
+ >>> X = iris.data
43
+ >>> y = iris.target
44
+ >>> scaler = MinMaxScaler()
45
+ >>> X = scaler.fit_transform(X)
46
+ >>> for i in range(len(y)):
47
+ ... if y[i] == 0:
48
+ ... y[i] = -1
49
+ ... elif y[i] == 2:
50
+ ... y[i] = 1
51
+ >>> X_train, X_test, y_train, y_test = train_test_split(
52
+ ... X,
53
+ ... y,
54
+ ... test_size=0.2,
55
+ ... random_state=42,
56
+ ... )
57
+ >>> from eqc_models.ml.classifierqsvm import QSVMClassifier
58
+ >>> obj = QSVMClassifier(
59
+ ... relaxation_schedule=2,
60
+ ... num_samples=1,
61
+ ... upper_limit=1.0,
62
+ ... gamma=1.0,
63
+ ... eta=1.0,
64
+ ... zeta=1.0,
65
+ ... )
66
+ >>> from contextlib import redirect_stdout
67
+ >>> import io
68
+ >>> f = io.StringIO()
69
+ >>> with redirect_stdout(f):
70
+ ... obj = obj.fit(X_train, y_train)
71
+ ... y_train_prd = obj.predict(X_train)
72
+ ... y_test_prd = obj.predict(X_test)
73
+
74
+ """
75
+
76
+ def __init__(
77
+ self,
78
+ relaxation_schedule=2,
79
+ num_samples=1,
80
+ upper_limit=1.0,
81
+ gamma=1.0,
82
+ eta=1.0,
83
+ zeta=1.0,
84
+ ):
85
+ super(QSVMClassifier).__init__()
86
+
87
+ self.relaxation_schedule = relaxation_schedule
88
+ self.num_samples = num_samples
89
+ self.upper_limit = upper_limit
90
+ self.gamma = gamma
91
+ self.eta = eta
92
+ self.zeta = zeta
93
+
94
+ def kernel(self, vec1, vec2):
95
+ return np.exp(-self.gamma * np.linalg.norm(vec1 - vec2) ** 2)
96
+
97
+ def fit(self, X, y):
98
+ """
99
+ Build a QSVM classifier from the training set (X, y).
100
+
101
+ Parameters
102
+ ----------
103
+ X : {array-like, sparse matrix} of shape (n_samples, n_features)
104
+ The training input samples.
105
+
106
+ y : array-like of shape (n_samples,)
107
+ The target values.
108
+
109
+ Returns
110
+ -------
111
+ Response of Dirac-3 in JSON format.
112
+ """
113
+
114
+ assert X.shape[0] == y.shape[0], "Inconsistent sizes!"
115
+
116
+ assert set(y) == {-1, 1}, "Target values should be in {-1, 1}"
117
+
118
+ J, C, sum_constraint = self.get_hamiltonian(X, y)
119
+
120
+ assert J.shape[0] == J.shape[1], "Inconsistent hamiltonian size!"
121
+ assert J.shape[0] == C.shape[0], "Inconsistent hamiltonian size!"
122
+
123
+ self.set_model(J, C, sum_constraint)
124
+
125
+ sol, response = self.solve()
126
+
127
+ assert len(sol) == C.shape[0], "Inconsistent solution size!"
128
+
129
+ self.params = self.convert_sol_to_params(sol)
130
+
131
+ self.X_train = X
132
+ self.y_train = y
133
+
134
+ n_records = X.shape[0]
135
+ self.kernel_mat_train = np.zeros(
136
+ shape=(n_records, n_records), dtype=np.float32
137
+ )
138
+ for m in range(n_records):
139
+ for n in range(n_records):
140
+ self.kernel_mat_train[m][n] = self.kernel(X[m], X[n])
141
+
142
+ return response
143
+
144
+ def predict(self, X: np.array):
145
+ """
146
+ Predict classes for X.
147
+
148
+ Parameters
149
+ ----------
150
+ X : {array-like, sparse matrix} of shape (n_samples, n_features)
151
+
152
+ Returns
153
+ -------
154
+ y : ndarray of shape (n_samples,)
155
+ The predicted classes.
156
+ """
157
+
158
+ assert self.X_train is not None, "Model not trained yet!"
159
+ assert self.y_train is not None, "Model not trained yet!"
160
+
161
+ assert (
162
+ X.shape[1] == self.X_train.shape[1]
163
+ ), "Inconsistent dimensions!"
164
+
165
+ n_records = X.shape[0]
166
+ n_records_train = self.X_train.shape[0]
167
+ kernel_mat = np.zeros(
168
+ shape=(n_records, n_records_train), dtype=np.float32
169
+ )
170
+ for m in range(n_records):
171
+ for n in range(n_records_train):
172
+ kernel_mat[m][n] = self.kernel(X[m], self.X_train[n])
173
+
174
+ intercept = 0
175
+ tmp_vec1 = np.tensordot(
176
+ self.params * self.y_train, self.kernel_mat_train, axes=(0, 0)
177
+ )
178
+ assert tmp_vec1.shape[0] == n_records_train, "Inconsistent size!"
179
+
180
+ tmp1 = np.sum(
181
+ self.params
182
+ * (self.upper_limit - self.params)
183
+ * (self.y_train - tmp_vec1)
184
+ )
185
+ tmp2 = np.sum(self.params * (self.upper_limit - self.params))
186
+
187
+ assert tmp2 != 0, "Something went wrong!"
188
+
189
+ intercept = tmp1 / tmp2
190
+
191
+ y = np.zeros(shape=(n_records), dtype=np.float32)
192
+ y += np.tensordot(
193
+ self.params * self.y_train, kernel_mat, axes=(0, 1)
194
+ )
195
+ y += intercept
196
+ y = np.sign(y)
197
+
198
+ return y
199
+
200
+ def get_hamiltonian(
201
+ self,
202
+ X: np.array,
203
+ y: np.array,
204
+ ):
205
+ n_records = X.shape[0]
206
+ n_dims = X.shape[1]
207
+
208
+ J = np.zeros(
209
+ shape=(2 * n_records, 2 * n_records), dtype=np.float32
210
+ )
211
+ C = np.zeros(shape=(2 * n_records,), dtype=np.float32)
212
+
213
+ for n in range(n_records):
214
+ for m in range(n_records):
215
+ J[n][m] = (
216
+ 0.5 * y[n] * y[m] * self.kernel(X[n], X[m])
217
+ + self.zeta * y[n] * y[m]
218
+ )
219
+ J[n][n] += self.eta
220
+ J[n][n + n_records] = self.eta
221
+ J[n + n_records][n] = self.eta
222
+ J[n + n_records][n + n_records] = self.eta
223
+
224
+ C[n] = -1.0 - 2.0 * self.eta * self.upper_limit
225
+ C[n + n_records] = -2.0 * self.eta * self.upper_limit
226
+
227
+ C = C.reshape((2 * n_records, 1))
228
+ J = 0.5 * (J + J.transpose())
229
+
230
+ return J, C, n_records * self.upper_limit
231
+
232
+ def convert_sol_to_params(self, sol):
233
+ assert len(sol) % 2 == 0, "Expected an even solution size!"
234
+
235
+ sol = sol[: int(len(sol) / 2)]
236
+
237
+ return np.array(sol)
@@ -0,0 +1,323 @@
1
+ import sys
2
+ import numpy as np
3
+ import networkx as nx
4
+
5
+ from eqc_models.ml.clusteringbase import ClusteringBase
6
+
7
+
8
+ class GraphClustering(ClusteringBase):
9
+ """
10
+ A clustering approach on a graph based on maximizing modularity.
11
+
12
+ Parameters
13
+ ----------
14
+
15
+ graph: A NetwrokX graph object representing the data.
16
+ num_clusters: Number of clusters.
17
+
18
+ alpha: A penalty term multilplier; default: 1.0.
19
+
20
+ relaxation_schedule: Relaxation schedule used by Dirac-3; default:
21
+ 2.
22
+
23
+ num_samples: Number of samples used by Dirac-3; default: 1.
24
+
25
+ device: The device used, dirac-1 or dirac-3; default: dirac-3.
26
+
27
+ Examples
28
+ ---------
29
+
30
+ >>> import networkx as nx
31
+ >>> G = nx.Graph()
32
+ >>> G.add_edge(1, 2, weight=5)
33
+ >>> G.add_edge(2, 3, weight=3)
34
+ >>> G.add_edge(1, 3, weight=4)
35
+ >>> G.add_edge(4, 5, weight=6)
36
+ >>> G.add_edge(5, 6, weight=2)
37
+ >>> G.add_edge(4, 6, weight=7)
38
+ >>> G.add_edge(7, 8, weight=8)
39
+ >>> G.add_edge(8, 9, weight=1)
40
+ >>> G.add_edge(7, 9, weight=5)
41
+ >>> G.add_edge(3, 4, weight=0.5)
42
+ >>> G.add_edge(6, 7, weight=0.5)
43
+ >>> from eqc_models.ml.clustering import GraphClustering
44
+ >>> from contextlib import redirect_stdout
45
+ >>> import io
46
+ >>> f = io.StringIO()
47
+ >>> with redirect_stdout(f):
48
+ ... obj = GraphClustering(
49
+ ... relaxation_schedule=2,
50
+ ... num_samples=1,
51
+ ... graph=G,
52
+ ... num_clusters=3,
53
+ ... alpha=10.0,
54
+ ... device="dirac-3",
55
+ ... )
56
+ ... labels = obj.fit_predict()
57
+ """
58
+
59
+ def __init__(
60
+ self,
61
+ graph: nx.Graph,
62
+ num_clusters: int,
63
+ alpha: float = 1.0,
64
+ relaxation_schedule=2,
65
+ num_samples=1,
66
+ device="dirac-3",
67
+ ):
68
+ super(GraphClustering).__init__()
69
+
70
+ assert device in ["dirac-1", "dirac-3"]
71
+
72
+ self.graph = graph
73
+ self.num_nodes = graph.number_of_nodes()
74
+ self.num_edges = graph.number_of_edges()
75
+ self.num_clusters = num_clusters
76
+ self.alpha = alpha
77
+ self.relaxation_schedule = relaxation_schedule
78
+ self.num_samples = num_samples
79
+ self.device = device
80
+ self.labels = None
81
+
82
+ def get_hamiltonian(self):
83
+ adj_mat_sparse = nx.adjacency_matrix(self.graph)
84
+ A = adj_mat_sparse.toarray()
85
+
86
+ assert A.shape[0] == A.shape[1], "Inconsistent size!"
87
+ assert A.shape[0] == self.num_nodes, "Inconsistent size!"
88
+
89
+ num_clusters = self.num_clusters
90
+ num_nodes = self.num_nodes
91
+
92
+ J = np.zeros(
93
+ shape=(num_nodes * num_clusters, num_nodes * num_clusters),
94
+ dtype=np.float64,
95
+ )
96
+ C = np.zeros(
97
+ shape=(num_nodes * num_clusters),
98
+ dtype=np.float64,
99
+ )
100
+
101
+ for i in range(num_nodes):
102
+ for j in range(num_nodes):
103
+ for c in range(num_clusters):
104
+ for d in range(num_clusters):
105
+ if c == d:
106
+ J[i * num_clusters + c][
107
+ j * num_clusters + d
108
+ ] += -(
109
+ A[i][j]
110
+ - np.sum(A[i]) * np.sum(A[j]) / np.sum(A)
111
+ )
112
+ if i == j:
113
+ J[i * num_clusters + c][
114
+ j * num_clusters + d
115
+ ] += self.alpha
116
+
117
+ for i in range(num_nodes):
118
+ for c in range(num_clusters):
119
+ C[i * num_clusters + c] += -2.0 * self.alpha
120
+
121
+ return J, C, num_nodes
122
+
123
+ def get_labels(self, sol: np.array):
124
+ labels = np.empty(shape=(self.num_nodes), dtype=np.int32)
125
+
126
+ for i in range(self.num_nodes):
127
+ vec = sol[i * self.num_clusters : (i + 1) * self.num_clusters]
128
+ labels[i] = np.argmax(vec) + 1
129
+
130
+ return labels
131
+
132
+ def fit(self):
133
+ """
134
+ Fit clustering.
135
+ """
136
+ J, C, sum_constraint = self.get_hamiltonian()
137
+
138
+ assert J.shape[0] == J.shape[1], "Inconsistent hamiltonian size!"
139
+ assert J.shape[0] == C.shape[0], "Inconsistent hamiltonian size!"
140
+
141
+ self.set_model(J, C, sum_constraint)
142
+
143
+ sol = self.solve()
144
+
145
+ assert len(sol) == C.shape[0], "Inconsistent solution size!"
146
+ assert len(sol) == self.num_clusters * self.num_nodes
147
+
148
+ self.labels = self.get_labels(sol)
149
+
150
+ return self
151
+
152
+ def fit_predict(self):
153
+ """
154
+ Fit clustering and return cluster labels for all nodes.
155
+ """
156
+
157
+ self.fit()
158
+
159
+ return self.labels
160
+
161
+ def get_modularity(self):
162
+ if self.labels is None:
163
+ return
164
+
165
+ clusters = [set() for i in range(self.num_clusters)]
166
+ nodes = list(self.graph.nodes())
167
+ for i in range(self.num_nodes):
168
+ clusters[self.labels[i] - 1].add(nodes[i])
169
+
170
+ return nx.community.modularity(self.graph, clusters)
171
+
172
+
173
+ class Clustering(ClusteringBase):
174
+ """A clustering approach based on QCi's Dirac machines.
175
+
176
+ Parameters
177
+ ----------
178
+
179
+ num_clusters: Number of clusters.
180
+
181
+ alpha: A penalty term multilplier; default: 1.0.
182
+
183
+ relaxation_schedule: Relaxation schedule used by Dirac-3; default:
184
+ 2.
185
+
186
+ num_samples: Number of samples used by Dirac-3; default: 1.
187
+
188
+ distance_func: Distance function used; default: squared_l2_norm.
189
+
190
+ device: The device used, dirac-1 or dirac-3; default: dirac-3.
191
+
192
+ Examples
193
+ ---------
194
+
195
+ >>> np.random.seed(42)
196
+ >>> cluster1 = np.random.randn(15, 2) * 0.5 + np.array([2, 2])
197
+ >>> cluster2 = np.random.randn(15, 2) * 0.5 + np.array([8, 3])
198
+ >>> cluster3 = np.random.randn(15, 2) * 0.5 + np.array([5, 8])
199
+ >>> X = np.vstack((cluster1, cluster2, cluster3))
200
+ >>> from eqc_models.ml.clustering import Clustering
201
+ >>> from contextlib import redirect_stdout
202
+ >>> import io
203
+ >>> f = io.StringIO()
204
+ >>> with redirect_stdout(f):
205
+ ... obj = Clustering(
206
+ ... num_clusters=3,
207
+ ... relaxation_schedule=1,
208
+ ... num_samples=1,
209
+ ... alpha=500.0,
210
+ ... distance_func="squared_l2_norm",
211
+ ... device="dirac-3",
212
+ ... )
213
+ ... labels = obj.fit_predict(X)
214
+
215
+ """
216
+
217
+ def __init__(
218
+ self,
219
+ num_clusters: int,
220
+ alpha: float = 1.0,
221
+ relaxation_schedule: int = 2,
222
+ num_samples: int = 1,
223
+ distance_func: str = "squared_l2_norm",
224
+ device: str = "dirac-3",
225
+ ):
226
+ super(Clustering).__init__()
227
+
228
+ assert device in ["dirac-1", "dirac-3"]
229
+
230
+ self.num_clusters = num_clusters
231
+ self.alpha = alpha
232
+ self.relaxation_schedule = relaxation_schedule
233
+ self.num_samples = num_samples
234
+ self.distance_func = distance_func
235
+ self.device = device
236
+ self.labels = None
237
+
238
+ assert distance_func in ["squared_l2_norm"], (
239
+ "Unknown distance function <%s>!" % distance_func
240
+ )
241
+
242
+ def get_hamiltonian(self, X: np.array):
243
+ num_items = X.shape[0]
244
+ num_clusters = self.num_clusters
245
+
246
+ if self.distance_func == "squared_l2_norm":
247
+ dist = lambda u, v: np.linalg.norm(u - v) ** 2
248
+
249
+ J = np.zeros(
250
+ shape=(num_items * num_clusters, num_items * num_clusters),
251
+ dtype=np.float64,
252
+ )
253
+ C = np.zeros(
254
+ shape=(num_items * num_clusters),
255
+ dtype=np.float64,
256
+ )
257
+
258
+ for i in range(num_items):
259
+ for j in range(num_items):
260
+ for c in range(num_clusters):
261
+ for d in range(num_clusters):
262
+ if c == d:
263
+ J[i * num_clusters + c][
264
+ j * num_clusters + d
265
+ ] += dist(X[i], X[j])
266
+
267
+ if i == j:
268
+ J[i * num_clusters + c][
269
+ j * num_clusters + d
270
+ ] += self.alpha
271
+
272
+ for i in range(num_items):
273
+ for c in range(num_clusters):
274
+ C[i * num_clusters + c] += -2.0 * self.alpha
275
+
276
+ return J, C, num_items
277
+
278
+ def get_labels(self, sol: np.array, num_items: int):
279
+ labels = np.empty(shape=(num_items), dtype=np.int32)
280
+
281
+ for i in range(num_items):
282
+ vec = sol[i * self.num_clusters : (i + 1) * self.num_clusters]
283
+ labels[i] = np.argmax(vec) + 1
284
+
285
+ return labels
286
+
287
+ def fit(self, X: np.array):
288
+ """
289
+ Fit clustering.
290
+
291
+ Parameters
292
+ ----------
293
+ X: Dataset; an array of shape (num_items, num_dims).
294
+ """
295
+ num_items = X.shape[0]
296
+ J, C, sum_constraint = self.get_hamiltonian(X)
297
+
298
+ assert J.shape[0] == J.shape[1], "Inconsistent hamiltonian size!"
299
+ assert J.shape[0] == C.shape[0], "Inconsistent hamiltonian size!"
300
+
301
+ self.set_model(J, C, sum_constraint)
302
+
303
+ sol = self.solve()
304
+
305
+ assert len(sol) == C.shape[0], "Inconsistent solution size!"
306
+ assert len(sol) == self.num_clusters * num_items
307
+
308
+ self.labels = self.get_labels(sol, num_items)
309
+
310
+ return self
311
+
312
+ def fit_predict(self, X: np.array):
313
+ """
314
+ Fit clustering and return cluster labels for all records.
315
+
316
+ Parameters
317
+ ----------
318
+ X: Dataset; an array of shape (num_items, num_dims).
319
+ """
320
+
321
+ self.fit(X)
322
+
323
+ return self.labels
@@ -0,0 +1,112 @@
1
+ # (C) Quantum Computing Inc., 2024.
2
+ import sys
3
+ import numpy as np
4
+
5
+ from eqc_models import QuadraticModel
6
+ from eqc_models.solvers.qciclient import (
7
+ Dirac1CloudSolver,
8
+ Dirac3CloudSolver,
9
+ )
10
+
11
+
12
+ class ClusteringBase(QuadraticModel):
13
+ """
14
+ A base class for clustering algorithms
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ relaxation_schedule=2,
20
+ num_samples=1,
21
+ device="dirac-3",
22
+ ):
23
+ super(self).__init__(None, None, None)
24
+
25
+ assert device in ["dirac-1", "dirac-3"]
26
+
27
+ self.relaxation_schedule = relaxation_schedule
28
+ self.num_samples = num_samples
29
+ self.device = device
30
+
31
+ def fit(self, X: np.array):
32
+ pass
33
+
34
+ def predict(self, X: np.array):
35
+ pass
36
+
37
+ def get_hamiltonian(
38
+ self,
39
+ X: np.array,
40
+ ):
41
+ pass
42
+
43
+ def set_model(self, J, C, sum_constraint):
44
+ # Set hamiltonians
45
+ self._C = C
46
+ self._J = J
47
+ self._H = C, J
48
+ self._sum_constraint = sum_constraint
49
+ num_variables = C.shape[0]
50
+
51
+ if self.device == "dirac-1":
52
+ self.upper_bound = np.ones((num_variables,))
53
+ elif self.device == "dirac-3":
54
+ self.upper_bound = sum_constraint * np.ones((num_variables,))
55
+
56
+ return
57
+
58
+ def solve(self):
59
+ if self.device == "dirac-1":
60
+ solver = Dirac1CloudSolver()
61
+ response = solver.solve(
62
+ self,
63
+ num_samples=self.num_samples,
64
+ )
65
+ elif self.device == "dirac-3":
66
+ solver = Dirac3CloudSolver()
67
+ response = solver.solve(
68
+ self,
69
+ sum_constraint=self._sum_constraint,
70
+ relaxation_schedule=self.relaxation_schedule,
71
+ solution_precision=1,
72
+ num_samples=self.num_samples,
73
+ )
74
+
75
+ min_id = np.argmin(response["results"]["energies"])
76
+
77
+ sol = response["results"]["solutions"][min_id]
78
+
79
+ print(response)
80
+
81
+ return sol
82
+
83
+ def get_labels(self, sol):
84
+ pass
85
+
86
+ def get_energy(self, sol: np.array):
87
+ C = self._C
88
+ J = self._J
89
+
90
+ return sol.transpose() @ J @ sol + sol.transpose @ C
91
+
92
+ def get_dynamic_range(self):
93
+ C = self._C
94
+ J = self._J
95
+
96
+ if C is None:
97
+ return
98
+
99
+ if J is None:
100
+ return
101
+
102
+ absc = np.abs(C)
103
+ absj = np.abs(J)
104
+ minc = np.min(absc[absc > 0])
105
+ maxc = np.max(absc)
106
+ minj = np.min(absj[absj > 0])
107
+ maxj = np.max(absj)
108
+
109
+ minval = min(minc, minj)
110
+ maxval = max(maxc, maxj)
111
+
112
+ return 10 * np.log10(maxval / minval)