superquantx 0.1.0__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.
- superquantx/__init__.py +321 -0
- superquantx/algorithms/__init__.py +55 -0
- superquantx/algorithms/base_algorithm.py +413 -0
- superquantx/algorithms/hybrid_classifier.py +628 -0
- superquantx/algorithms/qaoa.py +406 -0
- superquantx/algorithms/quantum_agents.py +1006 -0
- superquantx/algorithms/quantum_kmeans.py +575 -0
- superquantx/algorithms/quantum_nn.py +544 -0
- superquantx/algorithms/quantum_pca.py +499 -0
- superquantx/algorithms/quantum_svm.py +346 -0
- superquantx/algorithms/vqe.py +553 -0
- superquantx/algorithms.py +863 -0
- superquantx/backends/__init__.py +265 -0
- superquantx/backends/base_backend.py +321 -0
- superquantx/backends/braket_backend.py +420 -0
- superquantx/backends/cirq_backend.py +466 -0
- superquantx/backends/ocean_backend.py +491 -0
- superquantx/backends/pennylane_backend.py +419 -0
- superquantx/backends/qiskit_backend.py +451 -0
- superquantx/backends/simulator_backend.py +455 -0
- superquantx/backends/tket_backend.py +519 -0
- superquantx/circuits.py +447 -0
- superquantx/cli/__init__.py +28 -0
- superquantx/cli/commands.py +528 -0
- superquantx/cli/main.py +254 -0
- superquantx/client.py +298 -0
- superquantx/config.py +326 -0
- superquantx/exceptions.py +287 -0
- superquantx/gates.py +588 -0
- superquantx/logging_config.py +347 -0
- superquantx/measurements.py +702 -0
- superquantx/ml.py +936 -0
- superquantx/noise.py +760 -0
- superquantx/utils/__init__.py +83 -0
- superquantx/utils/benchmarking.py +523 -0
- superquantx/utils/classical_utils.py +575 -0
- superquantx/utils/feature_mapping.py +467 -0
- superquantx/utils/optimization.py +410 -0
- superquantx/utils/quantum_utils.py +456 -0
- superquantx/utils/visualization.py +654 -0
- superquantx/version.py +33 -0
- superquantx-0.1.0.dist-info/METADATA +365 -0
- superquantx-0.1.0.dist-info/RECORD +46 -0
- superquantx-0.1.0.dist-info/WHEEL +4 -0
- superquantx-0.1.0.dist-info/entry_points.txt +2 -0
- superquantx-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,346 @@
|
|
1
|
+
"""Quantum Support Vector Machine (QSVM) implementation.
|
2
|
+
|
3
|
+
This module provides a quantum support vector machine algorithm that can be used
|
4
|
+
for binary and multiclass classification tasks using quantum kernels.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import logging
|
8
|
+
from typing import Any, Callable, Dict, Optional, Union
|
9
|
+
|
10
|
+
import numpy as np
|
11
|
+
from sklearn.metrics import accuracy_score
|
12
|
+
from sklearn.preprocessing import StandardScaler
|
13
|
+
from sklearn.svm import SVC
|
14
|
+
|
15
|
+
from .base_algorithm import SupervisedQuantumAlgorithm
|
16
|
+
|
17
|
+
|
18
|
+
logger = logging.getLogger(__name__)
|
19
|
+
|
20
|
+
class QuantumSVM(SupervisedQuantumAlgorithm):
|
21
|
+
"""Quantum Support Vector Machine for classification.
|
22
|
+
|
23
|
+
This implementation uses quantum feature maps to transform data into
|
24
|
+
a high-dimensional Hilbert space where linear separation is possible.
|
25
|
+
The quantum kernel is computed using quantum circuits.
|
26
|
+
|
27
|
+
The algorithm works by:
|
28
|
+
1. Encoding classical data into quantum states using feature maps
|
29
|
+
2. Computing quantum kernels between data points
|
30
|
+
3. Training a classical SVM using the quantum kernel matrix
|
31
|
+
|
32
|
+
Args:
|
33
|
+
backend: Quantum backend for circuit execution
|
34
|
+
feature_map: Type of quantum feature map ('ZZFeatureMap', 'PauliFeatureMap', etc.)
|
35
|
+
feature_map_reps: Number of repetitions in the feature map
|
36
|
+
C: Regularization parameter for SVM
|
37
|
+
gamma: Kernel coefficient (for RBF-like quantum kernels)
|
38
|
+
quantum_kernel: Custom quantum kernel function
|
39
|
+
shots: Number of measurement shots
|
40
|
+
**kwargs: Additional parameters
|
41
|
+
|
42
|
+
Example:
|
43
|
+
>>> qsvm = QuantumSVM(backend='pennylane', feature_map='ZZFeatureMap')
|
44
|
+
>>> qsvm.fit(X_train, y_train)
|
45
|
+
>>> predictions = qsvm.predict(X_test)
|
46
|
+
>>> accuracy = qsvm.score(X_test, y_test)
|
47
|
+
|
48
|
+
"""
|
49
|
+
|
50
|
+
def __init__(
|
51
|
+
self,
|
52
|
+
backend: Union[str, Any],
|
53
|
+
feature_map: str = 'ZZFeatureMap',
|
54
|
+
feature_map_reps: int = 2,
|
55
|
+
C: float = 1.0,
|
56
|
+
gamma: Optional[float] = None,
|
57
|
+
quantum_kernel: Optional[Callable] = None,
|
58
|
+
shots: int = 1024,
|
59
|
+
normalize_data: bool = True,
|
60
|
+
**kwargs
|
61
|
+
) -> None:
|
62
|
+
super().__init__(backend=backend, shots=shots, **kwargs)
|
63
|
+
|
64
|
+
self.feature_map = feature_map
|
65
|
+
self.feature_map_reps = feature_map_reps
|
66
|
+
self.C = C
|
67
|
+
self.gamma = gamma
|
68
|
+
self.quantum_kernel = quantum_kernel
|
69
|
+
self.normalize_data = normalize_data
|
70
|
+
|
71
|
+
# Classical components
|
72
|
+
self.svm = None
|
73
|
+
self.scaler = StandardScaler() if normalize_data else None
|
74
|
+
|
75
|
+
# Quantum components
|
76
|
+
self.kernel_matrix_ = None
|
77
|
+
self.feature_map_circuit_ = None
|
78
|
+
|
79
|
+
# Training data storage (needed for kernel computation)
|
80
|
+
self.X_train_ = None
|
81
|
+
|
82
|
+
logger.info(f"Initialized QuantumSVM with feature_map={feature_map}, reps={feature_map_reps}")
|
83
|
+
|
84
|
+
def _create_feature_map(self, n_features: int) -> Any:
|
85
|
+
"""Create quantum feature map circuit."""
|
86
|
+
try:
|
87
|
+
if hasattr(self.backend, 'create_feature_map'):
|
88
|
+
return self.backend.create_feature_map(
|
89
|
+
n_features=n_features,
|
90
|
+
feature_map=self.feature_map,
|
91
|
+
reps=self.feature_map_reps
|
92
|
+
)
|
93
|
+
else:
|
94
|
+
# Fallback implementation
|
95
|
+
return self._default_feature_map(n_features)
|
96
|
+
except Exception as e:
|
97
|
+
logger.error(f"Failed to create feature map: {e}")
|
98
|
+
return self._default_feature_map(n_features)
|
99
|
+
|
100
|
+
def _default_feature_map(self, n_features: int) -> Any:
|
101
|
+
"""Create default feature map when backend doesn't provide one."""
|
102
|
+
# This would be implemented based on the backend
|
103
|
+
logger.warning("Using default feature map - results may vary")
|
104
|
+
return None
|
105
|
+
|
106
|
+
def _compute_quantum_kernel(self, X1: np.ndarray, X2: Optional[np.ndarray] = None) -> np.ndarray:
|
107
|
+
"""Compute quantum kernel matrix between data points.
|
108
|
+
|
109
|
+
Args:
|
110
|
+
X1: First set of data points
|
111
|
+
X2: Second set of data points (if None, use X1)
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Quantum kernel matrix
|
115
|
+
|
116
|
+
"""
|
117
|
+
if X2 is None:
|
118
|
+
X2 = X1
|
119
|
+
|
120
|
+
n1, n2 = len(X1), len(X2)
|
121
|
+
kernel_matrix = np.zeros((n1, n2))
|
122
|
+
|
123
|
+
if self.quantum_kernel:
|
124
|
+
# Use custom quantum kernel
|
125
|
+
for i in range(n1):
|
126
|
+
for j in range(n2):
|
127
|
+
kernel_matrix[i, j] = self.quantum_kernel(X1[i], X2[j])
|
128
|
+
else:
|
129
|
+
# Use backend's kernel computation
|
130
|
+
try:
|
131
|
+
kernel_matrix = self.backend.compute_kernel_matrix(
|
132
|
+
X1, X2,
|
133
|
+
feature_map=self.feature_map_circuit_,
|
134
|
+
shots=self.shots
|
135
|
+
)
|
136
|
+
except Exception as e:
|
137
|
+
logger.error(f"Quantum kernel computation failed: {e}")
|
138
|
+
# Fallback to classical RBF kernel
|
139
|
+
from sklearn.metrics.pairwise import rbf_kernel
|
140
|
+
gamma = self.gamma if self.gamma else 1.0 / X1.shape[1]
|
141
|
+
kernel_matrix = rbf_kernel(X1, X2, gamma=gamma)
|
142
|
+
logger.warning("Using classical RBF kernel as fallback")
|
143
|
+
|
144
|
+
return kernel_matrix
|
145
|
+
|
146
|
+
def fit(self, X: np.ndarray, y: np.ndarray, **kwargs) -> 'QuantumSVM':
|
147
|
+
"""Train the quantum SVM.
|
148
|
+
|
149
|
+
Args:
|
150
|
+
X: Training data features
|
151
|
+
y: Training data labels
|
152
|
+
**kwargs: Additional training parameters
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
Self for method chaining
|
156
|
+
|
157
|
+
"""
|
158
|
+
logger.info(f"Training QuantumSVM on {X.shape[0]} samples with {X.shape[1]} features")
|
159
|
+
|
160
|
+
# Validate and preprocess data
|
161
|
+
super().fit(X, y, **kwargs)
|
162
|
+
|
163
|
+
if self.normalize_data:
|
164
|
+
X = self.scaler.fit_transform(X)
|
165
|
+
|
166
|
+
self.X_train_ = X.copy()
|
167
|
+
|
168
|
+
# Create quantum feature map
|
169
|
+
self.feature_map_circuit_ = self._create_feature_map(X.shape[1])
|
170
|
+
|
171
|
+
# Compute quantum kernel matrix
|
172
|
+
logger.info("Computing quantum kernel matrix...")
|
173
|
+
self.kernel_matrix_ = self._compute_quantum_kernel(X)
|
174
|
+
|
175
|
+
# Train classical SVM with quantum kernel
|
176
|
+
self.svm = SVC(
|
177
|
+
kernel='precomputed',
|
178
|
+
C=self.C,
|
179
|
+
)
|
180
|
+
|
181
|
+
self.svm.fit(self.kernel_matrix_, y)
|
182
|
+
self.is_fitted = True
|
183
|
+
|
184
|
+
# Compute training accuracy
|
185
|
+
train_predictions = self.predict(X)
|
186
|
+
train_accuracy = accuracy_score(y, train_predictions)
|
187
|
+
|
188
|
+
self.training_history.append({
|
189
|
+
'train_accuracy': train_accuracy,
|
190
|
+
'n_support_vectors': self.svm.n_support_,
|
191
|
+
'kernel_matrix_shape': self.kernel_matrix_.shape,
|
192
|
+
})
|
193
|
+
|
194
|
+
logger.info(f"Training completed. Accuracy: {train_accuracy:.3f}, "
|
195
|
+
f"Support vectors: {sum(self.svm.n_support_)}")
|
196
|
+
|
197
|
+
return self
|
198
|
+
|
199
|
+
def predict(self, X: np.ndarray, **kwargs) -> np.ndarray:
|
200
|
+
"""Make predictions using the trained quantum SVM.
|
201
|
+
|
202
|
+
Args:
|
203
|
+
X: Input data for prediction
|
204
|
+
**kwargs: Additional prediction parameters
|
205
|
+
|
206
|
+
Returns:
|
207
|
+
Predicted labels
|
208
|
+
|
209
|
+
"""
|
210
|
+
if not self.is_fitted:
|
211
|
+
raise ValueError("Model must be fitted before making predictions")
|
212
|
+
|
213
|
+
if self.normalize_data:
|
214
|
+
X = self.scaler.transform(X)
|
215
|
+
|
216
|
+
# Compute kernel matrix between test data and training data
|
217
|
+
test_kernel = self._compute_quantum_kernel(X, self.X_train_)
|
218
|
+
|
219
|
+
# Make predictions using the trained SVM
|
220
|
+
predictions = self.svm.predict(test_kernel)
|
221
|
+
|
222
|
+
return predictions
|
223
|
+
|
224
|
+
def predict_proba(self, X: np.ndarray, **kwargs) -> np.ndarray:
|
225
|
+
"""Predict class probabilities.
|
226
|
+
|
227
|
+
Args:
|
228
|
+
X: Input data for prediction
|
229
|
+
**kwargs: Additional parameters
|
230
|
+
|
231
|
+
Returns:
|
232
|
+
Predicted class probabilities
|
233
|
+
|
234
|
+
"""
|
235
|
+
if not self.is_fitted:
|
236
|
+
raise ValueError("Model must be fitted before making predictions")
|
237
|
+
|
238
|
+
if self.normalize_data:
|
239
|
+
X = self.scaler.transform(X)
|
240
|
+
|
241
|
+
test_kernel = self._compute_quantum_kernel(X, self.X_train_)
|
242
|
+
|
243
|
+
# Need to recreate SVM with probability=True for probabilities
|
244
|
+
if not hasattr(self.svm, 'predict_proba'):
|
245
|
+
logger.warning("Probability prediction not available, returning decision scores")
|
246
|
+
return self.decision_function(X)
|
247
|
+
|
248
|
+
return self.svm.predict_proba(test_kernel)
|
249
|
+
|
250
|
+
def decision_function(self, X: np.ndarray) -> np.ndarray:
|
251
|
+
"""Compute decision function values.
|
252
|
+
|
253
|
+
Args:
|
254
|
+
X: Input data
|
255
|
+
|
256
|
+
Returns:
|
257
|
+
Decision function values
|
258
|
+
|
259
|
+
"""
|
260
|
+
if not self.is_fitted:
|
261
|
+
raise ValueError("Model must be fitted before computing decision function")
|
262
|
+
|
263
|
+
if self.normalize_data:
|
264
|
+
X = self.scaler.transform(X)
|
265
|
+
|
266
|
+
test_kernel = self._compute_quantum_kernel(X, self.X_train_)
|
267
|
+
return self.svm.decision_function(test_kernel)
|
268
|
+
|
269
|
+
def get_support_vectors(self) -> np.ndarray:
|
270
|
+
"""Get support vectors from the trained model."""
|
271
|
+
if not self.is_fitted:
|
272
|
+
raise ValueError("Model must be fitted to get support vectors")
|
273
|
+
|
274
|
+
return self.X_train_[self.svm.support_]
|
275
|
+
|
276
|
+
def get_quantum_kernel_matrix(self, X: Optional[np.ndarray] = None) -> np.ndarray:
|
277
|
+
"""Get the quantum kernel matrix.
|
278
|
+
|
279
|
+
Args:
|
280
|
+
X: Data to compute kernel matrix for (default: training data)
|
281
|
+
|
282
|
+
Returns:
|
283
|
+
Quantum kernel matrix
|
284
|
+
|
285
|
+
"""
|
286
|
+
if X is None:
|
287
|
+
if self.kernel_matrix_ is None:
|
288
|
+
raise ValueError("No kernel matrix available")
|
289
|
+
return self.kernel_matrix_
|
290
|
+
else:
|
291
|
+
if self.normalize_data:
|
292
|
+
X = self.scaler.transform(X)
|
293
|
+
return self._compute_quantum_kernel(X)
|
294
|
+
|
295
|
+
def analyze_kernel(self) -> Dict[str, Any]:
|
296
|
+
"""Analyze properties of the quantum kernel.
|
297
|
+
|
298
|
+
Returns:
|
299
|
+
Dictionary with kernel analysis results
|
300
|
+
|
301
|
+
"""
|
302
|
+
if self.kernel_matrix_ is None:
|
303
|
+
raise ValueError("Model must be fitted to analyze kernel")
|
304
|
+
|
305
|
+
K = self.kernel_matrix_
|
306
|
+
|
307
|
+
# Compute kernel properties
|
308
|
+
eigenvalues = np.linalg.eigvals(K)
|
309
|
+
|
310
|
+
analysis = {
|
311
|
+
'kernel_shape': K.shape,
|
312
|
+
'kernel_rank': np.linalg.matrix_rank(K),
|
313
|
+
'condition_number': np.linalg.cond(K),
|
314
|
+
'trace': np.trace(K),
|
315
|
+
'frobenius_norm': np.linalg.norm(K, 'fro'),
|
316
|
+
'eigenvalue_stats': {
|
317
|
+
'mean': np.mean(eigenvalues),
|
318
|
+
'std': np.std(eigenvalues),
|
319
|
+
'min': np.min(eigenvalues),
|
320
|
+
'max': np.max(eigenvalues),
|
321
|
+
},
|
322
|
+
'is_positive_definite': np.all(eigenvalues > 0),
|
323
|
+
}
|
324
|
+
|
325
|
+
return analysis
|
326
|
+
|
327
|
+
def get_params(self, deep: bool = True) -> Dict[str, Any]:
|
328
|
+
"""Get algorithm parameters."""
|
329
|
+
params = super().get_params(deep)
|
330
|
+
params.update({
|
331
|
+
'feature_map': self.feature_map,
|
332
|
+
'feature_map_reps': self.feature_map_reps,
|
333
|
+
'C': self.C,
|
334
|
+
'gamma': self.gamma,
|
335
|
+
'normalize_data': self.normalize_data,
|
336
|
+
})
|
337
|
+
return params
|
338
|
+
|
339
|
+
def set_params(self, **params) -> 'QuantumSVM':
|
340
|
+
"""Set algorithm parameters."""
|
341
|
+
if self.is_fitted and any(key in params for key in
|
342
|
+
['feature_map', 'feature_map_reps', 'C', 'gamma']):
|
343
|
+
logger.warning("Changing core parameters requires refitting the model")
|
344
|
+
self.is_fitted = False
|
345
|
+
|
346
|
+
return super().set_params(**params)
|