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,419 @@
1
+ """PennyLane backend implementation for SuperQuantX.
2
+
3
+ This module provides integration with PennyLane for quantum machine learning
4
+ and variational quantum algorithms.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Callable, Dict, List, Optional, Union
9
+
10
+ import numpy as np
11
+
12
+ from .base_backend import BaseBackend
13
+
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ # Try to import PennyLane
18
+ try:
19
+ import pennylane as qml
20
+ from pennylane import numpy as pnp
21
+ PENNYLANE_AVAILABLE = True
22
+ except ImportError:
23
+ PENNYLANE_AVAILABLE = False
24
+ qml = None
25
+ pnp = None
26
+
27
+ class PennyLaneBackend(BaseBackend):
28
+ """PennyLane backend for quantum computing operations.
29
+
30
+ This backend provides access to PennyLane's quantum devices and
31
+ automatic differentiation capabilities for variational quantum
32
+ algorithms.
33
+
34
+ Args:
35
+ device: PennyLane device name ('default.qubit', 'qiskit.aer', etc.)
36
+ wires: Number of qubits/wires
37
+ shots: Number of measurement shots
38
+ **kwargs: Additional device parameters
39
+
40
+ """
41
+
42
+ def __init__(self, device: str = 'default.qubit', wires: int = 4, shots: int = 1024, **kwargs):
43
+ if not PENNYLANE_AVAILABLE:
44
+ raise ImportError("PennyLane is required for PennyLaneBackend. Install with: pip install pennylane")
45
+
46
+ super().__init__(device=device, shots=shots, **kwargs)
47
+
48
+ self.wires = wires
49
+ self.dev = None
50
+ self.capabilities = {
51
+ 'supports_gradient': True,
52
+ 'supports_parameter_shift': True,
53
+ 'supports_finite_diff': True,
54
+ 'supports_backprop': device in ['default.qubit'],
55
+ 'supports_adjoint': device in ['default.qubit'],
56
+ }
57
+
58
+ def _initialize_backend(self) -> None:
59
+ """Initialize PennyLane device."""
60
+ try:
61
+ device_kwargs = self.config.copy()
62
+ device_kwargs.pop('wires', None) # Remove wires from kwargs if present
63
+
64
+ # Create PennyLane device
65
+ if self.shots is not None and self.shots > 0:
66
+ self.dev = qml.device(self.device, wires=self.wires, shots=self.shots, **device_kwargs)
67
+ else:
68
+ # Exact simulation (no shots)
69
+ self.dev = qml.device(self.device, wires=self.wires, **device_kwargs)
70
+
71
+ logger.info(f"Initialized PennyLane device: {self.device} with {self.wires} wires")
72
+
73
+ except Exception as e:
74
+ logger.error(f"Failed to initialize PennyLane device: {e}")
75
+ raise
76
+
77
+ def create_circuit(self, n_qubits: int) -> Callable:
78
+ """Create a PennyLane quantum function template."""
79
+ if n_qubits > self.wires:
80
+ logger.warning(f"Requested {n_qubits} qubits, but device only has {self.wires} wires")
81
+ n_qubits = self.wires
82
+
83
+ def circuit_template():
84
+ """Empty circuit template."""
85
+ return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)]
86
+
87
+ return circuit_template
88
+
89
+ def add_gate(self, circuit: Callable, gate: str, qubits: Union[int, List[int]],
90
+ params: Optional[List[float]] = None) -> Callable:
91
+ """Add a quantum gate to the circuit (conceptual - PennyLane uses functions)."""
92
+ # In PennyLane, gates are added within quantum functions
93
+ # This method is more for compatibility with the base interface
94
+ logger.debug(f"Gate {gate} would be added to qubits {qubits} with params {params}")
95
+ return circuit
96
+
97
+ def add_measurement(self, circuit: Callable, qubits: Optional[List[int]] = None) -> Callable:
98
+ """Add measurement operations (conceptual in PennyLane)."""
99
+ return circuit
100
+
101
+ def execute_circuit(self, circuit: Callable, shots: Optional[int] = None) -> Dict[str, Any]:
102
+ """Execute quantum circuit and return results."""
103
+ try:
104
+ # Create QNode
105
+ if shots is not None:
106
+ dev = qml.device(self.device, wires=self.wires, shots=shots)
107
+ qnode = qml.QNode(circuit, dev)
108
+ else:
109
+ qnode = qml.QNode(circuit, self.dev)
110
+
111
+ # Execute
112
+ result = qnode()
113
+
114
+ return {
115
+ 'result': result,
116
+ 'shots': shots or self.shots,
117
+ 'device': self.device,
118
+ }
119
+
120
+ except Exception as e:
121
+ logger.error(f"Circuit execution failed: {e}")
122
+ raise
123
+
124
+ def get_statevector(self, circuit: Callable) -> np.ndarray:
125
+ """Get the statevector from a quantum circuit."""
126
+ # Create a statevector device
127
+ dev_statevector = qml.device('default.qubit', wires=self.wires)
128
+
129
+ @qml.qnode(dev_statevector)
130
+ def statevector_circuit():
131
+ circuit()
132
+ return qml.state()
133
+
134
+ return np.array(statevector_circuit())
135
+
136
+ def _get_n_qubits(self, circuit: Any) -> int:
137
+ """Get number of qubits in circuit."""
138
+ return self.wires
139
+
140
+ # ========================================================================
141
+ # Enhanced PennyLane-specific implementations
142
+ # ========================================================================
143
+
144
+ def create_feature_map(self, n_features: int, feature_map: str, reps: int = 1) -> Callable:
145
+ """Create quantum feature map for data encoding."""
146
+ if feature_map == 'ZZFeatureMap':
147
+ return self._create_zz_feature_map(n_features, reps)
148
+ elif feature_map == 'PauliFeatureMap':
149
+ return self._create_pauli_feature_map(n_features, reps)
150
+ elif feature_map == 'AmplitudeMap':
151
+ return self._create_amplitude_map(n_features)
152
+ else:
153
+ logger.warning(f"Unknown feature map '{feature_map}', using angle encoding")
154
+ return self._create_angle_encoding_map(n_features)
155
+
156
+ def _create_zz_feature_map(self, n_features: int, reps: int) -> Callable:
157
+ """Create ZZ feature map circuit."""
158
+ def zz_feature_map(x):
159
+ for r in range(reps):
160
+ # Single-qubit rotations
161
+ for i in range(min(n_features, self.wires)):
162
+ qml.Hadamard(wires=i)
163
+ qml.RZ(x[i], wires=i)
164
+
165
+ # Two-qubit interactions
166
+ for i in range(min(n_features - 1, self.wires - 1)):
167
+ qml.CNOT(wires=[i, i + 1])
168
+ qml.RZ(x[i] * x[i + 1], wires=i + 1)
169
+ qml.CNOT(wires=[i, i + 1])
170
+
171
+ return [qml.expval(qml.PauliZ(i)) for i in range(min(n_features, self.wires))]
172
+
173
+ return zz_feature_map
174
+
175
+ def _create_pauli_feature_map(self, n_features: int, reps: int) -> Callable:
176
+ """Create Pauli feature map circuit."""
177
+ def pauli_feature_map(x):
178
+ for r in range(reps):
179
+ for i in range(min(n_features, self.wires)):
180
+ qml.RX(x[i], wires=i)
181
+ qml.RY(x[i], wires=i)
182
+ qml.RZ(x[i], wires=i)
183
+
184
+ # Entangling layer
185
+ for i in range(min(n_features - 1, self.wires - 1)):
186
+ qml.CNOT(wires=[i, i + 1])
187
+
188
+ return [qml.expval(qml.PauliZ(i)) for i in range(min(n_features, self.wires))]
189
+
190
+ return pauli_feature_map
191
+
192
+ def _create_amplitude_map(self, n_features: int) -> Callable:
193
+ """Create amplitude encoding map."""
194
+ def amplitude_map(x):
195
+ # Normalize input
196
+ norm = np.linalg.norm(x)
197
+ if norm > 0:
198
+ normalized_x = x / norm
199
+ else:
200
+ normalized_x = x
201
+
202
+ # Pad to power of 2
203
+ n_qubits = int(np.ceil(np.log2(len(normalized_x))))
204
+ padded_x = np.pad(normalized_x, (0, 2**n_qubits - len(normalized_x)))
205
+
206
+ # Amplitude encoding (simplified)
207
+ qml.QubitStateVector(padded_x, wires=range(min(n_qubits, self.wires)))
208
+
209
+ return [qml.expval(qml.PauliZ(i)) for i in range(min(n_qubits, self.wires))]
210
+
211
+ return amplitude_map
212
+
213
+ def _create_angle_encoding_map(self, n_features: int) -> Callable:
214
+ """Create angle encoding map."""
215
+ def angle_encoding_map(x):
216
+ for i in range(min(n_features, self.wires)):
217
+ qml.RY(x[i], wires=i)
218
+
219
+ return [qml.expval(qml.PauliZ(i)) for i in range(min(n_features, self.wires))]
220
+
221
+ return angle_encoding_map
222
+
223
+ def compute_kernel_matrix(self, X1: np.ndarray, X2: np.ndarray,
224
+ feature_map: Callable, shots: Optional[int] = None) -> np.ndarray:
225
+ """Compute quantum kernel matrix using PennyLane."""
226
+ n1, n2 = len(X1), len(X2)
227
+ kernel_matrix = np.zeros((n1, n2))
228
+
229
+ # Create kernel evaluation circuit
230
+ def kernel_circuit(x1, x2):
231
+ # Encode first data point
232
+ for i in range(min(len(x1), self.wires)):
233
+ qml.RY(x1[i], wires=i)
234
+
235
+ # Encode second data point with inverse
236
+ for i in range(min(len(x2), self.wires)):
237
+ qml.RY(-x2[i], wires=i)
238
+
239
+ # Measure probability of |00...0⟩ state
240
+ return qml.probs(wires=range(min(len(x1), self.wires)))
241
+
242
+ # Create QNode
243
+ dev = qml.device(self.device, wires=self.wires, shots=shots) if shots else self.dev
244
+ kernel_qnode = qml.QNode(kernel_circuit, dev)
245
+
246
+ # Compute kernel matrix
247
+ for i in range(n1):
248
+ for j in range(n2):
249
+ try:
250
+ probs = kernel_qnode(X1[i], X2[j])
251
+ kernel_matrix[i, j] = probs[0] # Probability of |00...0⟩
252
+ except Exception as e:
253
+ logger.warning(f"Kernel computation failed for ({i},{j}): {e}")
254
+ kernel_matrix[i, j] = 0.0
255
+
256
+ return kernel_matrix
257
+
258
+ def create_ansatz(self, ansatz_type: str, n_qubits: int, params: np.ndarray,
259
+ include_custom_gates: bool = False) -> Callable:
260
+ """Create parameterized ansatz circuit."""
261
+ if ansatz_type == 'RealAmplitudes':
262
+ return self._create_real_amplitudes_ansatz(n_qubits, params)
263
+ elif ansatz_type == 'EfficientSU2':
264
+ return self._create_efficient_su2_ansatz(n_qubits, params)
265
+ elif ansatz_type == 'TwoLocal':
266
+ return self._create_two_local_ansatz(n_qubits, params)
267
+ elif ansatz_type == 'UCCSD':
268
+ return self._create_uccsd_ansatz(n_qubits, params)
269
+ else:
270
+ logger.warning(f"Unknown ansatz '{ansatz_type}', using RealAmplitudes")
271
+ return self._create_real_amplitudes_ansatz(n_qubits, params)
272
+
273
+ def _create_real_amplitudes_ansatz(self, n_qubits: int, params: np.ndarray) -> Callable:
274
+ """Create RealAmplitudes ansatz."""
275
+ def real_amplitudes_circuit():
276
+ n_layers = len(params) // (n_qubits * 2)
277
+ param_idx = 0
278
+
279
+ for layer in range(n_layers):
280
+ # Rotation layer
281
+ for i in range(min(n_qubits, self.wires)):
282
+ if param_idx < len(params):
283
+ qml.RY(params[param_idx], wires=i)
284
+ param_idx += 1
285
+
286
+ # Entangling layer
287
+ for i in range(min(n_qubits - 1, self.wires - 1)):
288
+ qml.CNOT(wires=[i, i + 1])
289
+
290
+ # Second rotation layer
291
+ for i in range(min(n_qubits, self.wires)):
292
+ if param_idx < len(params):
293
+ qml.RZ(params[param_idx], wires=i)
294
+ param_idx += 1
295
+
296
+ return [qml.expval(qml.PauliZ(i)) for i in range(min(n_qubits, self.wires))]
297
+
298
+ return real_amplitudes_circuit
299
+
300
+ def _create_efficient_su2_ansatz(self, n_qubits: int, params: np.ndarray) -> Callable:
301
+ """Create EfficientSU2 ansatz."""
302
+ def efficient_su2_circuit():
303
+ n_layers = len(params) // (n_qubits * 3)
304
+ param_idx = 0
305
+
306
+ for layer in range(n_layers):
307
+ # SU(2) rotations
308
+ for i in range(min(n_qubits, self.wires)):
309
+ if param_idx + 2 < len(params):
310
+ qml.RY(params[param_idx], wires=i)
311
+ qml.RZ(params[param_idx + 1], wires=i)
312
+ qml.RY(params[param_idx + 2], wires=i)
313
+ param_idx += 3
314
+
315
+ # Entangling layer
316
+ for i in range(min(n_qubits - 1, self.wires - 1)):
317
+ qml.CNOT(wires=[i, i + 1])
318
+
319
+ return [qml.expval(qml.PauliZ(i)) for i in range(min(n_qubits, self.wires))]
320
+
321
+ return efficient_su2_circuit
322
+
323
+ def _create_two_local_ansatz(self, n_qubits: int, params: np.ndarray) -> Callable:
324
+ """Create TwoLocal ansatz."""
325
+ def two_local_circuit():
326
+ n_layers = len(params) // (n_qubits * 2)
327
+ param_idx = 0
328
+
329
+ for layer in range(n_layers):
330
+ # Local rotations
331
+ for i in range(min(n_qubits, self.wires)):
332
+ if param_idx + 1 < len(params):
333
+ qml.RY(params[param_idx], wires=i)
334
+ qml.RZ(params[param_idx + 1], wires=i)
335
+ param_idx += 2
336
+
337
+ # Two-qubit gates
338
+ for i in range(0, min(n_qubits - 1, self.wires - 1), 2):
339
+ qml.CNOT(wires=[i, i + 1])
340
+ for i in range(1, min(n_qubits - 1, self.wires - 1), 2):
341
+ qml.CNOT(wires=[i, i + 1])
342
+
343
+ return [qml.expval(qml.PauliZ(i)) for i in range(min(n_qubits, self.wires))]
344
+
345
+ return two_local_circuit
346
+
347
+ def _create_uccsd_ansatz(self, n_qubits: int, params: np.ndarray) -> Callable:
348
+ """Create UCCSD ansatz (simplified)."""
349
+ def uccsd_circuit():
350
+ # Simplified UCCSD - full implementation would require molecular geometry
351
+ param_idx = 0
352
+
353
+ # Singles excitations
354
+ for i in range(0, min(n_qubits, self.wires), 2):
355
+ for j in range(1, min(n_qubits, self.wires), 2):
356
+ if param_idx < len(params) and i != j:
357
+ # Simplified single excitation
358
+ qml.CNOT(wires=[i, j])
359
+ qml.RY(params[param_idx], wires=j)
360
+ qml.CNOT(wires=[i, j])
361
+ param_idx += 1
362
+
363
+ # Doubles excitations (simplified)
364
+ for i in range(0, min(n_qubits - 3, self.wires - 3), 2):
365
+ if param_idx < len(params):
366
+ # Simplified double excitation
367
+ qml.CNOT(wires=[i, i + 1])
368
+ qml.CNOT(wires=[i + 2, i + 3])
369
+ qml.RY(params[param_idx], wires=i + 1)
370
+ qml.CNOT(wires=[i, i + 1])
371
+ qml.CNOT(wires=[i + 2, i + 3])
372
+ param_idx += 1
373
+
374
+ return [qml.expval(qml.PauliZ(i)) for i in range(min(n_qubits, self.wires))]
375
+
376
+ return uccsd_circuit
377
+
378
+ def compute_expectation(self, circuit: Callable, hamiltonian: Any,
379
+ shots: Optional[int] = None) -> float:
380
+ """Compute expectation value of Hamiltonian."""
381
+ try:
382
+ # If hamiltonian is a PennyLane Hamiltonian
383
+ if hasattr(hamiltonian, 'coeffs') and hasattr(hamiltonian, 'ops'):
384
+ @qml.qnode(self.dev if shots is None else qml.device(self.device, wires=self.wires, shots=shots))
385
+ def expectation_circuit():
386
+ circuit()
387
+ return qml.expval(hamiltonian)
388
+
389
+ return float(expectation_circuit())
390
+
391
+ # If hamiltonian is a matrix, decompose it
392
+ elif isinstance(hamiltonian, np.ndarray):
393
+ return self._compute_matrix_expectation(circuit, hamiltonian, shots)
394
+
395
+ else:
396
+ logger.warning("Unknown Hamiltonian format, returning 0")
397
+ return 0.0
398
+
399
+ except Exception as e:
400
+ logger.error(f"Expectation computation failed: {e}")
401
+ return 0.0
402
+
403
+ def _compute_matrix_expectation(self, circuit: Callable, H: np.ndarray, shots: Optional[int]) -> float:
404
+ """Compute expectation value for matrix Hamiltonian."""
405
+ # Get statevector
406
+ statevector = self.get_statevector(circuit)
407
+
408
+ # Compute ⟨ψ|H|ψ⟩
409
+ expectation = np.real(np.conj(statevector) @ H @ statevector)
410
+ return float(expectation)
411
+
412
+ def get_version_info(self) -> Dict[str, Any]:
413
+ """Get PennyLane version information."""
414
+ info = super().get_version_info()
415
+ info.update({
416
+ 'pennylane_version': qml.version() if qml else 'Not available',
417
+ 'available_devices': qml.plugin_devices if qml else [],
418
+ })
419
+ return info