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.
Files changed (46) hide show
  1. superquantx/__init__.py +321 -0
  2. superquantx/algorithms/__init__.py +55 -0
  3. superquantx/algorithms/base_algorithm.py +413 -0
  4. superquantx/algorithms/hybrid_classifier.py +628 -0
  5. superquantx/algorithms/qaoa.py +406 -0
  6. superquantx/algorithms/quantum_agents.py +1006 -0
  7. superquantx/algorithms/quantum_kmeans.py +575 -0
  8. superquantx/algorithms/quantum_nn.py +544 -0
  9. superquantx/algorithms/quantum_pca.py +499 -0
  10. superquantx/algorithms/quantum_svm.py +346 -0
  11. superquantx/algorithms/vqe.py +553 -0
  12. superquantx/algorithms.py +863 -0
  13. superquantx/backends/__init__.py +265 -0
  14. superquantx/backends/base_backend.py +321 -0
  15. superquantx/backends/braket_backend.py +420 -0
  16. superquantx/backends/cirq_backend.py +466 -0
  17. superquantx/backends/ocean_backend.py +491 -0
  18. superquantx/backends/pennylane_backend.py +419 -0
  19. superquantx/backends/qiskit_backend.py +451 -0
  20. superquantx/backends/simulator_backend.py +455 -0
  21. superquantx/backends/tket_backend.py +519 -0
  22. superquantx/circuits.py +447 -0
  23. superquantx/cli/__init__.py +28 -0
  24. superquantx/cli/commands.py +528 -0
  25. superquantx/cli/main.py +254 -0
  26. superquantx/client.py +298 -0
  27. superquantx/config.py +326 -0
  28. superquantx/exceptions.py +287 -0
  29. superquantx/gates.py +588 -0
  30. superquantx/logging_config.py +347 -0
  31. superquantx/measurements.py +702 -0
  32. superquantx/ml.py +936 -0
  33. superquantx/noise.py +760 -0
  34. superquantx/utils/__init__.py +83 -0
  35. superquantx/utils/benchmarking.py +523 -0
  36. superquantx/utils/classical_utils.py +575 -0
  37. superquantx/utils/feature_mapping.py +467 -0
  38. superquantx/utils/optimization.py +410 -0
  39. superquantx/utils/quantum_utils.py +456 -0
  40. superquantx/utils/visualization.py +654 -0
  41. superquantx/version.py +33 -0
  42. superquantx-0.1.0.dist-info/METADATA +365 -0
  43. superquantx-0.1.0.dist-info/RECORD +46 -0
  44. superquantx-0.1.0.dist-info/WHEEL +4 -0
  45. superquantx-0.1.0.dist-info/entry_points.txt +2 -0
  46. superquantx-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,499 @@
1
+ """Quantum Principal Component Analysis (QPCA) implementation.
2
+
3
+ This module provides quantum algorithms for principal component analysis,
4
+ including quantum matrix diagonalization and dimensionality reduction.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict, Optional, Tuple, Union
9
+
10
+ import numpy as np
11
+ from sklearn.decomposition import PCA
12
+ from sklearn.preprocessing import StandardScaler
13
+
14
+ from .base_algorithm import UnsupervisedQuantumAlgorithm
15
+
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+ class QuantumPCA(UnsupervisedQuantumAlgorithm):
20
+ """Quantum Principal Component Analysis for dimensionality reduction.
21
+
22
+ This implementation uses quantum algorithms to perform PCA, potentially
23
+ offering exponential speedup for certain types of data matrices.
24
+
25
+ The algorithm can use different quantum approaches:
26
+ - Quantum Matrix Inversion: For density matrix diagonalization
27
+ - Variational Quantum Eigensolver: For finding principal eigenvectors
28
+ - Quantum Phase Estimation: For eigenvalue extraction
29
+ - Quantum Singular Value Decomposition: Direct SVD approach
30
+
31
+ Args:
32
+ backend: Quantum backend for circuit execution
33
+ n_components: Number of principal components to extract
34
+ method: Quantum method ('vqe', 'phase_estimation', 'matrix_inversion', 'qsvd')
35
+ encoding: Data encoding method ('amplitude', 'dense', 'sparse')
36
+ max_iterations: Maximum iterations for variational methods
37
+ tolerance: Convergence tolerance
38
+ shots: Number of measurement shots
39
+ classical_fallback: Use classical PCA if quantum fails
40
+ **kwargs: Additional parameters
41
+
42
+ Example:
43
+ >>> qpca = QuantumPCA(backend='pennylane', n_components=3, method='vqe')
44
+ >>> qpca.fit(X_train)
45
+ >>> X_reduced = qpca.transform(X_test)
46
+ >>> X_reconstructed = qpca.inverse_transform(X_reduced)
47
+
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ backend: Union[str, Any],
53
+ n_components: int = 2,
54
+ method: str = 'vqe',
55
+ encoding: str = 'amplitude',
56
+ max_iterations: int = 1000,
57
+ tolerance: float = 1e-6,
58
+ shots: int = 1024,
59
+ classical_fallback: bool = True,
60
+ normalize_data: bool = True,
61
+ **kwargs
62
+ ) -> None:
63
+ super().__init__(backend=backend, shots=shots, **kwargs)
64
+
65
+ self.n_components = n_components
66
+ self.method = method
67
+ self.encoding = encoding
68
+ self.max_iterations = max_iterations
69
+ self.tolerance = tolerance
70
+ self.classical_fallback = classical_fallback
71
+ self.normalize_data = normalize_data
72
+
73
+ # PCA components
74
+ self.components_ = None
75
+ self.eigenvalues_ = None
76
+ self.mean_ = None
77
+ self.explained_variance_ = None
78
+ self.explained_variance_ratio_ = None
79
+
80
+ # Quantum-specific attributes
81
+ self.density_matrix_ = None
82
+ self.quantum_state_ = None
83
+ self.n_qubits = None
84
+
85
+ # Classical components for fallback/comparison
86
+ self.scaler = StandardScaler() if normalize_data else None
87
+ self.classical_pca = PCA(n_components=n_components)
88
+
89
+ # Method-specific parameters
90
+ self.vqe_params = None
91
+ self.convergence_history = []
92
+
93
+ logger.info(f"Initialized QuantumPCA with method={method}, n_components={n_components}")
94
+
95
+ def _prepare_data_matrix(self, X: np.ndarray) -> np.ndarray:
96
+ """Prepare data matrix for quantum processing."""
97
+ if self.normalize_data:
98
+ X = self.scaler.fit_transform(X)
99
+
100
+ # Center the data
101
+ self.mean_ = np.mean(X, axis=0)
102
+ X_centered = X - self.mean_
103
+
104
+ return X_centered
105
+
106
+ def _create_density_matrix(self, X: np.ndarray) -> np.ndarray:
107
+ """Create density matrix from data."""
108
+ # Compute covariance matrix
109
+ n_samples = X.shape[0]
110
+ cov_matrix = (X.T @ X) / (n_samples - 1)
111
+
112
+ # Convert to density matrix (normalized)
113
+ trace = np.trace(cov_matrix)
114
+ if trace > 0:
115
+ density_matrix = cov_matrix / trace
116
+ else:
117
+ density_matrix = cov_matrix
118
+
119
+ self.density_matrix_ = density_matrix
120
+ return density_matrix
121
+
122
+ def _determine_qubits(self, n_features: int) -> int:
123
+ """Determine number of qubits needed."""
124
+ if self.encoding == 'amplitude':
125
+ return int(np.ceil(np.log2(n_features)))
126
+ elif self.encoding == 'dense':
127
+ return n_features
128
+ else:
129
+ return int(np.ceil(np.log2(n_features)))
130
+
131
+ def _encode_density_matrix(self, density_matrix: np.ndarray) -> Any:
132
+ """Encode density matrix into quantum state."""
133
+ try:
134
+ if hasattr(self.backend, 'encode_density_matrix'):
135
+ return self.backend.encode_density_matrix(
136
+ density_matrix=density_matrix,
137
+ encoding=self.encoding,
138
+ n_qubits=self.n_qubits
139
+ )
140
+ else:
141
+ return self._fallback_encoding(density_matrix)
142
+ except Exception as e:
143
+ logger.error(f"Failed to encode density matrix: {e}")
144
+ return self._fallback_encoding(density_matrix)
145
+
146
+ def _fallback_encoding(self, density_matrix: np.ndarray) -> Any:
147
+ """Fallback encoding implementation."""
148
+ logger.warning("Using fallback density matrix encoding")
149
+ return None
150
+
151
+ def _quantum_eigensolver_vqe(self, density_matrix: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
152
+ """Use VQE to find principal eigenvectors and eigenvalues."""
153
+ logger.info("Running VQE for quantum PCA")
154
+
155
+ try:
156
+ if hasattr(self.backend, 'run_pca_vqe'):
157
+ result = self.backend.run_pca_vqe(
158
+ density_matrix=density_matrix,
159
+ n_components=self.n_components,
160
+ max_iterations=self.max_iterations,
161
+ tolerance=self.tolerance,
162
+ shots=self.shots
163
+ )
164
+
165
+ eigenvalues = result.get('eigenvalues', np.zeros(self.n_components))
166
+ eigenvectors = result.get('eigenvectors', np.eye(density_matrix.shape[0], self.n_components))
167
+ self.convergence_history = result.get('convergence_history', [])
168
+
169
+ return eigenvalues, eigenvectors
170
+ else:
171
+ return self._fallback_vqe_eigensolver(density_matrix)
172
+
173
+ except Exception as e:
174
+ logger.error(f"VQE eigensolver failed: {e}")
175
+ return self._fallback_vqe_eigensolver(density_matrix)
176
+
177
+ def _fallback_vqe_eigensolver(self, density_matrix: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
178
+ """Fallback VQE implementation using classical eigensolver."""
179
+ logger.warning("Using classical fallback for VQE eigensolver")
180
+ eigenvals, eigenvecs = np.linalg.eigh(density_matrix)
181
+ # Sort by eigenvalue magnitude (descending)
182
+ idx = np.argsort(eigenvals)[::-1]
183
+ return eigenvals[idx][:self.n_components], eigenvecs[:, idx][:, :self.n_components]
184
+
185
+ def _quantum_phase_estimation(self, density_matrix: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
186
+ """Use quantum phase estimation for eigenvalue extraction."""
187
+ logger.info("Running quantum phase estimation for PCA")
188
+
189
+ try:
190
+ if hasattr(self.backend, 'run_phase_estimation_pca'):
191
+ result = self.backend.run_phase_estimation_pca(
192
+ density_matrix=density_matrix,
193
+ n_components=self.n_components,
194
+ precision_bits=8,
195
+ shots=self.shots
196
+ )
197
+
198
+ eigenvalues = result.get('eigenvalues', np.zeros(self.n_components))
199
+ eigenvectors = result.get('eigenvectors', np.eye(density_matrix.shape[0], self.n_components))
200
+
201
+ return eigenvalues, eigenvectors
202
+ else:
203
+ return self._fallback_phase_estimation(density_matrix)
204
+
205
+ except Exception as e:
206
+ logger.error(f"Quantum phase estimation failed: {e}")
207
+ return self._fallback_phase_estimation(density_matrix)
208
+
209
+ def _fallback_phase_estimation(self, density_matrix: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
210
+ """Fallback phase estimation using classical methods."""
211
+ logger.warning("Using classical fallback for phase estimation")
212
+ return self._fallback_vqe_eigensolver(density_matrix)
213
+
214
+ def _quantum_svd(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
215
+ """Quantum singular value decomposition approach."""
216
+ logger.info("Running quantum SVD for PCA")
217
+
218
+ try:
219
+ if hasattr(self.backend, 'run_quantum_svd'):
220
+ result = self.backend.run_quantum_svd(
221
+ data_matrix=X,
222
+ n_components=self.n_components,
223
+ shots=self.shots
224
+ )
225
+
226
+ singular_values = result.get('singular_values', np.zeros(self.n_components))
227
+ components = result.get('components', np.eye(X.shape[1], self.n_components))
228
+
229
+ # Convert singular values to eigenvalues
230
+ eigenvalues = singular_values ** 2 / (X.shape[0] - 1)
231
+
232
+ return eigenvalues, components.T
233
+ else:
234
+ return self._fallback_svd(X)
235
+
236
+ except Exception as e:
237
+ logger.error(f"Quantum SVD failed: {e}")
238
+ return self._fallback_svd(X)
239
+
240
+ def _fallback_svd(self, X: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
241
+ """Fallback SVD using classical methods."""
242
+ logger.warning("Using classical fallback for SVD")
243
+ U, s, Vt = np.linalg.svd(X, full_matrices=False)
244
+ eigenvalues = (s ** 2) / (X.shape[0] - 1)
245
+ return eigenvalues[:self.n_components], Vt[:self.n_components].T
246
+
247
+ def _quantum_matrix_inversion(self, density_matrix: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
248
+ """Quantum matrix inversion approach."""
249
+ logger.info("Running quantum matrix inversion for PCA")
250
+
251
+ try:
252
+ if hasattr(self.backend, 'quantum_matrix_inversion_pca'):
253
+ result = self.backend.quantum_matrix_inversion_pca(
254
+ density_matrix=density_matrix,
255
+ n_components=self.n_components,
256
+ condition_threshold=1e-6,
257
+ shots=self.shots
258
+ )
259
+
260
+ eigenvalues = result.get('eigenvalues', np.zeros(self.n_components))
261
+ eigenvectors = result.get('eigenvectors', np.eye(density_matrix.shape[0], self.n_components))
262
+
263
+ return eigenvalues, eigenvectors
264
+ else:
265
+ return self._fallback_matrix_inversion(density_matrix)
266
+
267
+ except Exception as e:
268
+ logger.error(f"Quantum matrix inversion failed: {e}")
269
+ return self._fallback_matrix_inversion(density_matrix)
270
+
271
+ def _fallback_matrix_inversion(self, density_matrix: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
272
+ """Fallback matrix inversion using classical methods."""
273
+ logger.warning("Using classical fallback for matrix inversion")
274
+ return self._fallback_vqe_eigensolver(density_matrix)
275
+
276
+ def fit(self, X: np.ndarray, y: Optional[np.ndarray] = None, **kwargs) -> 'QuantumPCA':
277
+ """Fit quantum PCA to the data.
278
+
279
+ Args:
280
+ X: Training data
281
+ y: Ignored (unsupervised learning)
282
+ **kwargs: Additional fitting parameters
283
+
284
+ Returns:
285
+ Self for method chaining
286
+
287
+ """
288
+ logger.info(f"Fitting QuantumPCA to data of shape {X.shape}")
289
+
290
+ # Validate and preprocess data
291
+ super().fit(X, y, **kwargs)
292
+
293
+ # Prepare data
294
+ X_processed = self._prepare_data_matrix(X)
295
+
296
+ # Determine quantum circuit size
297
+ self.n_qubits = self._determine_qubits(X.shape[1])
298
+
299
+ # Choose quantum method
300
+ if self.method == 'vqe':
301
+ density_matrix = self._create_density_matrix(X_processed)
302
+ eigenvalues, eigenvectors = self._quantum_eigensolver_vqe(density_matrix)
303
+ elif self.method == 'phase_estimation':
304
+ density_matrix = self._create_density_matrix(X_processed)
305
+ eigenvalues, eigenvectors = self._quantum_phase_estimation(density_matrix)
306
+ elif self.method == 'matrix_inversion':
307
+ density_matrix = self._create_density_matrix(X_processed)
308
+ eigenvalues, eigenvectors = self._quantum_matrix_inversion(density_matrix)
309
+ elif self.method == 'qsvd':
310
+ eigenvalues, eigenvectors = self._quantum_svd(X_processed)
311
+ else:
312
+ raise ValueError(f"Unknown quantum PCA method: {self.method}")
313
+
314
+ # Store results
315
+ self.eigenvalues_ = eigenvalues
316
+ self.components_ = eigenvectors.T # Store as rows
317
+ self.explained_variance_ = eigenvalues
318
+
319
+ # Calculate explained variance ratio
320
+ total_variance = np.sum(eigenvalues) if np.sum(eigenvalues) > 0 else 1.0
321
+ self.explained_variance_ratio_ = eigenvalues / total_variance
322
+
323
+ # Fit classical PCA for comparison/fallback
324
+ if self.classical_fallback:
325
+ try:
326
+ self.classical_pca.fit(X_processed)
327
+ except Exception as e:
328
+ logger.warning(f"Classical PCA fitting failed: {e}")
329
+
330
+ self.is_fitted = True
331
+
332
+ logger.info(f"Quantum PCA completed. Explained variance ratio: {self.explained_variance_ratio_}")
333
+
334
+ return self
335
+
336
+ def transform(self, X: np.ndarray) -> np.ndarray:
337
+ """Transform data to lower dimensional space.
338
+
339
+ Args:
340
+ X: Data to transform
341
+
342
+ Returns:
343
+ Transformed data
344
+
345
+ """
346
+ if not self.is_fitted:
347
+ raise ValueError("QuantumPCA must be fitted before transform")
348
+
349
+ # Preprocess data
350
+ if self.normalize_data:
351
+ X = self.scaler.transform(X)
352
+
353
+ # Center data
354
+ X_centered = X - self.mean_
355
+
356
+ # Project onto principal components
357
+ X_transformed = X_centered @ self.components_.T
358
+
359
+ return X_transformed
360
+
361
+ def inverse_transform(self, X_transformed: np.ndarray) -> np.ndarray:
362
+ """Reconstruct data from lower dimensional representation.
363
+
364
+ Args:
365
+ X_transformed: Transformed data
366
+
367
+ Returns:
368
+ Reconstructed data
369
+
370
+ """
371
+ if not self.is_fitted:
372
+ raise ValueError("QuantumPCA must be fitted before inverse_transform")
373
+
374
+ # Reconstruct in original space
375
+ X_reconstructed = X_transformed @ self.components_
376
+
377
+ # Add back the mean
378
+ X_reconstructed += self.mean_
379
+
380
+ # Inverse scaling if applied
381
+ if self.normalize_data:
382
+ X_reconstructed = self.scaler.inverse_transform(X_reconstructed)
383
+
384
+ return X_reconstructed
385
+
386
+ def fit_transform(self, X: np.ndarray, y: Optional[np.ndarray] = None) -> np.ndarray:
387
+ """Fit PCA and transform data in one step."""
388
+ return self.fit(X, y).transform(X)
389
+
390
+ def predict(self, X: np.ndarray, **kwargs) -> np.ndarray:
391
+ """Transform data (alias for transform method)."""
392
+ return self.transform(X)
393
+
394
+ def get_quantum_advantage_metrics(self) -> Dict[str, Any]:
395
+ """Analyze potential quantum advantage."""
396
+ if not self.is_fitted:
397
+ raise ValueError("Must fit model first")
398
+
399
+ n_features = self.components_.shape[1]
400
+
401
+ metrics = {
402
+ 'data_dimension': n_features,
403
+ 'reduced_dimension': self.n_components,
404
+ 'compression_ratio': n_features / self.n_components,
405
+ 'quantum_circuit_qubits': self.n_qubits,
406
+ 'quantum_vs_classical_qubits': self.n_qubits / int(np.ceil(np.log2(n_features))),
407
+ }
408
+
409
+ # Potential speedup estimates (theoretical)
410
+ classical_complexity = n_features ** 3 # O(d^3) for eigendecomposition
411
+ quantum_complexity = self.n_qubits ** 2 * np.log(n_features) # Estimated quantum complexity
412
+
413
+ metrics.update({
414
+ 'classical_complexity_estimate': classical_complexity,
415
+ 'quantum_complexity_estimate': quantum_complexity,
416
+ 'theoretical_speedup': classical_complexity / quantum_complexity if quantum_complexity > 0 else 1,
417
+ })
418
+
419
+ return metrics
420
+
421
+ def compare_with_classical(self, X: np.ndarray) -> Dict[str, Any]:
422
+ """Compare quantum PCA results with classical PCA."""
423
+ if not self.is_fitted or not hasattr(self.classical_pca, 'components_'):
424
+ raise ValueError("Both quantum and classical PCA must be fitted")
425
+
426
+ # Transform data with both methods
427
+ X_quantum = self.transform(X)
428
+ X_classical = self.classical_pca.transform(X - self.mean_)
429
+
430
+ # Compute reconstruction errors
431
+ X_quantum_reconstructed = self.inverse_transform(X_quantum)
432
+ X_classical_reconstructed = self.classical_pca.inverse_transform(X_classical) + self.mean_
433
+
434
+ quantum_error = np.mean((X - X_quantum_reconstructed) ** 2)
435
+ classical_error = np.mean((X - X_classical_reconstructed) ** 2)
436
+
437
+ # Compare explained variance
438
+ quantum_var_ratio = np.sum(self.explained_variance_ratio_)
439
+ classical_var_ratio = np.sum(self.classical_pca.explained_variance_ratio_)
440
+
441
+ # Component similarity (using absolute cosine similarity)
442
+ component_similarities = []
443
+ min_components = min(self.n_components, len(self.classical_pca.components_))
444
+
445
+ for i in range(min_components):
446
+ # Cosine similarity between components
447
+ cos_sim = np.abs(np.dot(self.components_[i], self.classical_pca.components_[i]))
448
+ cos_sim /= (np.linalg.norm(self.components_[i]) * np.linalg.norm(self.classical_pca.components_[i]))
449
+ component_similarities.append(cos_sim)
450
+
451
+ return {
452
+ 'quantum_reconstruction_error': quantum_error,
453
+ 'classical_reconstruction_error': classical_error,
454
+ 'error_ratio': quantum_error / classical_error if classical_error > 0 else float('inf'),
455
+ 'quantum_variance_explained': quantum_var_ratio,
456
+ 'classical_variance_explained': classical_var_ratio,
457
+ 'variance_explained_ratio': quantum_var_ratio / classical_var_ratio if classical_var_ratio > 0 else float('inf'),
458
+ 'component_similarities': component_similarities,
459
+ 'mean_component_similarity': np.mean(component_similarities) if component_similarities else 0,
460
+ }
461
+
462
+ def analyze_convergence(self) -> Dict[str, Any]:
463
+ """Analyze convergence properties of the quantum algorithm."""
464
+ if not self.convergence_history:
465
+ return {'message': 'No convergence history available'}
466
+
467
+ convergence_data = np.array(self.convergence_history)
468
+
469
+ return {
470
+ 'total_iterations': len(convergence_data),
471
+ 'final_cost': convergence_data[-1],
472
+ 'initial_cost': convergence_data[0],
473
+ 'cost_reduction': convergence_data[0] - convergence_data[-1],
474
+ 'converged': abs(convergence_data[-1] - convergence_data[-2]) < self.tolerance if len(convergence_data) > 1 else False,
475
+ 'convergence_rate': np.mean(np.diff(convergence_data)) if len(convergence_data) > 1 else 0,
476
+ }
477
+
478
+ def get_params(self, deep: bool = True) -> Dict[str, Any]:
479
+ """Get quantum PCA parameters."""
480
+ params = super().get_params(deep)
481
+ params.update({
482
+ 'n_components': self.n_components,
483
+ 'method': self.method,
484
+ 'encoding': self.encoding,
485
+ 'max_iterations': self.max_iterations,
486
+ 'tolerance': self.tolerance,
487
+ 'classical_fallback': self.classical_fallback,
488
+ 'normalize_data': self.normalize_data,
489
+ })
490
+ return params
491
+
492
+ def set_params(self, **params) -> 'QuantumPCA':
493
+ """Set quantum PCA parameters."""
494
+ if self.is_fitted and any(key in params for key in
495
+ ['n_components', 'method', 'encoding']):
496
+ logger.warning("Changing core parameters requires refitting the model")
497
+ self.is_fitted = False
498
+
499
+ return super().set_params(**params)