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,467 @@
|
|
1
|
+
"""Quantum feature mapping utilities.
|
2
|
+
|
3
|
+
This module provides functions and classes for creating quantum feature maps,
|
4
|
+
which encode classical data into quantum states for machine learning algorithms.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from abc import ABC, abstractmethod
|
8
|
+
from dataclasses import dataclass
|
9
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
10
|
+
|
11
|
+
import numpy as np
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass
|
15
|
+
class FeatureMapConfig:
|
16
|
+
"""Configuration for quantum feature maps."""
|
17
|
+
|
18
|
+
n_qubits: int
|
19
|
+
n_features: int
|
20
|
+
reps: int = 1
|
21
|
+
entanglement: str = 'full'
|
22
|
+
parameter_prefix: str = 'x'
|
23
|
+
insert_barriers: bool = False
|
24
|
+
data_map_func: Optional[Callable] = None
|
25
|
+
|
26
|
+
|
27
|
+
class QuantumFeatureMap(ABC):
|
28
|
+
"""Abstract base class for quantum feature maps.
|
29
|
+
|
30
|
+
Feature maps encode classical data into quantum states by applying
|
31
|
+
parameterized quantum gates based on the input features.
|
32
|
+
"""
|
33
|
+
|
34
|
+
def __init__(
|
35
|
+
self,
|
36
|
+
n_features: int,
|
37
|
+
reps: int = 1,
|
38
|
+
entanglement: str = 'full',
|
39
|
+
parameter_prefix: str = 'x'
|
40
|
+
):
|
41
|
+
self.n_features = n_features
|
42
|
+
self.reps = reps
|
43
|
+
self.entanglement = entanglement
|
44
|
+
self.parameter_prefix = parameter_prefix
|
45
|
+
self.n_qubits = n_features
|
46
|
+
self._parameters = []
|
47
|
+
|
48
|
+
@abstractmethod
|
49
|
+
def _build_circuit(self, parameters: np.ndarray) -> Dict[str, Any]:
|
50
|
+
"""Build the quantum circuit for the feature map."""
|
51
|
+
pass
|
52
|
+
|
53
|
+
def map_data_point(self, x: np.ndarray) -> Dict[str, Any]:
|
54
|
+
"""Map a single data point to quantum circuit parameters.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
x: Input data point of length n_features
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
Circuit representation with parameters
|
61
|
+
|
62
|
+
"""
|
63
|
+
if len(x) != self.n_features:
|
64
|
+
raise ValueError(f"Expected {self.n_features} features, got {len(x)}")
|
65
|
+
|
66
|
+
# Repeat the data point for each repetition
|
67
|
+
parameters = np.tile(x, self.reps)
|
68
|
+
|
69
|
+
return self._build_circuit(parameters)
|
70
|
+
|
71
|
+
def map_data(self, X: np.ndarray) -> List[Dict[str, Any]]:
|
72
|
+
"""Map multiple data points to quantum circuits.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
X: Input data of shape (n_samples, n_features)
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
List of circuit representations
|
79
|
+
|
80
|
+
"""
|
81
|
+
return [self.map_data_point(x) for x in X]
|
82
|
+
|
83
|
+
@property
|
84
|
+
def num_parameters(self) -> int:
|
85
|
+
"""Number of parameters in the feature map."""
|
86
|
+
return self.n_features * self.reps
|
87
|
+
|
88
|
+
|
89
|
+
class ZFeatureMap(QuantumFeatureMap):
|
90
|
+
"""Z-axis rotation feature map.
|
91
|
+
|
92
|
+
This feature map applies RZ rotations to encode features:
|
93
|
+
RZ(2 * x_i) for each feature x_i
|
94
|
+
"""
|
95
|
+
|
96
|
+
def __init__(
|
97
|
+
self,
|
98
|
+
n_features: int,
|
99
|
+
reps: int = 1,
|
100
|
+
parameter_prefix: str = 'x'
|
101
|
+
):
|
102
|
+
super().__init__(n_features, reps, 'none', parameter_prefix)
|
103
|
+
|
104
|
+
def _build_circuit(self, parameters: np.ndarray) -> Dict[str, Any]:
|
105
|
+
"""Build Z feature map circuit."""
|
106
|
+
gates = []
|
107
|
+
|
108
|
+
for rep in range(self.reps):
|
109
|
+
for i in range(self.n_features):
|
110
|
+
param_idx = rep * self.n_features + i
|
111
|
+
gates.append({
|
112
|
+
'type': 'RZ',
|
113
|
+
'qubit': i,
|
114
|
+
'parameter': 2.0 * parameters[param_idx],
|
115
|
+
'parameter_name': f'{self.parameter_prefix}_{param_idx}'
|
116
|
+
})
|
117
|
+
|
118
|
+
return {
|
119
|
+
'n_qubits': self.n_qubits,
|
120
|
+
'gates': gates,
|
121
|
+
'parameters': parameters.tolist(),
|
122
|
+
'feature_map_type': 'Z'
|
123
|
+
}
|
124
|
+
|
125
|
+
|
126
|
+
class ZZFeatureMap(QuantumFeatureMap):
|
127
|
+
"""ZZ entangling feature map.
|
128
|
+
|
129
|
+
This feature map uses both single-qubit Z rotations and two-qubit ZZ interactions:
|
130
|
+
- Single qubit: RZ(2 * x_i)
|
131
|
+
- Two qubit: RZZ(2 * x_i * x_j) for entangled qubits
|
132
|
+
"""
|
133
|
+
|
134
|
+
def __init__(
|
135
|
+
self,
|
136
|
+
n_features: int,
|
137
|
+
reps: int = 1,
|
138
|
+
entanglement: str = 'linear',
|
139
|
+
alpha: float = 2.0,
|
140
|
+
parameter_prefix: str = 'x'
|
141
|
+
):
|
142
|
+
super().__init__(n_features, reps, entanglement, parameter_prefix)
|
143
|
+
self.alpha = alpha
|
144
|
+
|
145
|
+
def _build_circuit(self, parameters: np.ndarray) -> Dict[str, Any]:
|
146
|
+
"""Build ZZ feature map circuit."""
|
147
|
+
gates = []
|
148
|
+
|
149
|
+
for rep in range(self.reps):
|
150
|
+
# Single qubit Z rotations
|
151
|
+
for i in range(self.n_features):
|
152
|
+
param_idx = rep * self.n_features + i
|
153
|
+
gates.append({
|
154
|
+
'type': 'RZ',
|
155
|
+
'qubit': i,
|
156
|
+
'parameter': self.alpha * parameters[param_idx],
|
157
|
+
'parameter_name': f'{self.parameter_prefix}_{param_idx}'
|
158
|
+
})
|
159
|
+
|
160
|
+
# Two-qubit ZZ entangling gates
|
161
|
+
entangling_pairs = self._get_entangling_pairs()
|
162
|
+
|
163
|
+
for i, j in entangling_pairs:
|
164
|
+
param_i = rep * self.n_features + i
|
165
|
+
param_j = rep * self.n_features + j
|
166
|
+
|
167
|
+
gates.append({
|
168
|
+
'type': 'RZZ',
|
169
|
+
'qubits': [i, j],
|
170
|
+
'parameter': self.alpha * parameters[param_i] * parameters[param_j],
|
171
|
+
'parameter_names': [f'{self.parameter_prefix}_{param_i}',
|
172
|
+
f'{self.parameter_prefix}_{param_j}']
|
173
|
+
})
|
174
|
+
|
175
|
+
return {
|
176
|
+
'n_qubits': self.n_qubits,
|
177
|
+
'gates': gates,
|
178
|
+
'parameters': parameters.tolist(),
|
179
|
+
'feature_map_type': 'ZZ',
|
180
|
+
'entanglement': self.entanglement
|
181
|
+
}
|
182
|
+
|
183
|
+
def _get_entangling_pairs(self) -> List[Tuple[int, int]]:
|
184
|
+
"""Get pairs of qubits for entangling gates."""
|
185
|
+
pairs = []
|
186
|
+
|
187
|
+
if self.entanglement == 'linear':
|
188
|
+
# Linear chain: (0,1), (1,2), (2,3), ...
|
189
|
+
for i in range(self.n_qubits - 1):
|
190
|
+
pairs.append((i, i + 1))
|
191
|
+
|
192
|
+
elif self.entanglement == 'circular':
|
193
|
+
# Circular: linear + (n-1, 0)
|
194
|
+
pairs = self._get_linear_pairs()
|
195
|
+
pairs.append((self.n_qubits - 1, 0))
|
196
|
+
|
197
|
+
elif self.entanglement == 'full':
|
198
|
+
# Full connectivity: all pairs
|
199
|
+
for i in range(self.n_qubits):
|
200
|
+
for j in range(i + 1, self.n_qubits):
|
201
|
+
pairs.append((i, j))
|
202
|
+
|
203
|
+
elif self.entanglement == 'sca':
|
204
|
+
# Strongly correlated ansatz
|
205
|
+
for i in range(0, self.n_qubits - 1, 2):
|
206
|
+
if i + 1 < self.n_qubits:
|
207
|
+
pairs.append((i, i + 1))
|
208
|
+
|
209
|
+
return pairs
|
210
|
+
|
211
|
+
|
212
|
+
class PauliFeatureMap(QuantumFeatureMap):
|
213
|
+
"""Pauli feature map with arbitrary Pauli strings.
|
214
|
+
|
215
|
+
This feature map applies rotations based on Pauli operators:
|
216
|
+
exp(i * alpha * phi * P) where P is a Pauli string and phi is the feature value.
|
217
|
+
"""
|
218
|
+
|
219
|
+
def __init__(
|
220
|
+
self,
|
221
|
+
n_features: int,
|
222
|
+
reps: int = 1,
|
223
|
+
paulis: List[str] = ['Z', 'ZZ'],
|
224
|
+
entanglement: str = 'full',
|
225
|
+
alpha: float = 2.0,
|
226
|
+
parameter_prefix: str = 'x'
|
227
|
+
):
|
228
|
+
super().__init__(n_features, reps, entanglement, parameter_prefix)
|
229
|
+
self.paulis = paulis
|
230
|
+
self.alpha = alpha
|
231
|
+
|
232
|
+
def _build_circuit(self, parameters: np.ndarray) -> Dict[str, Any]:
|
233
|
+
"""Build Pauli feature map circuit."""
|
234
|
+
gates = []
|
235
|
+
|
236
|
+
for rep in range(self.reps):
|
237
|
+
for pauli_string in self.paulis:
|
238
|
+
if len(pauli_string) == 1:
|
239
|
+
# Single qubit Pauli
|
240
|
+
self._add_single_pauli_gates(
|
241
|
+
gates, pauli_string, parameters, rep
|
242
|
+
)
|
243
|
+
else:
|
244
|
+
# Multi-qubit Pauli
|
245
|
+
self._add_multi_pauli_gates(
|
246
|
+
gates, pauli_string, parameters, rep
|
247
|
+
)
|
248
|
+
|
249
|
+
return {
|
250
|
+
'n_qubits': self.n_qubits,
|
251
|
+
'gates': gates,
|
252
|
+
'parameters': parameters.tolist(),
|
253
|
+
'feature_map_type': 'Pauli',
|
254
|
+
'paulis': self.paulis
|
255
|
+
}
|
256
|
+
|
257
|
+
def _add_single_pauli_gates(
|
258
|
+
self,
|
259
|
+
gates: List[Dict],
|
260
|
+
pauli: str,
|
261
|
+
parameters: np.ndarray,
|
262
|
+
rep: int
|
263
|
+
):
|
264
|
+
"""Add single-qubit Pauli rotation gates."""
|
265
|
+
for i in range(self.n_features):
|
266
|
+
param_idx = rep * self.n_features + i
|
267
|
+
|
268
|
+
if pauli == 'X':
|
269
|
+
gate_type = 'RX'
|
270
|
+
elif pauli == 'Y':
|
271
|
+
gate_type = 'RY'
|
272
|
+
elif pauli == 'Z':
|
273
|
+
gate_type = 'RZ'
|
274
|
+
else:
|
275
|
+
continue
|
276
|
+
|
277
|
+
gates.append({
|
278
|
+
'type': gate_type,
|
279
|
+
'qubit': i,
|
280
|
+
'parameter': self.alpha * parameters[param_idx],
|
281
|
+
'parameter_name': f'{self.parameter_prefix}_{param_idx}'
|
282
|
+
})
|
283
|
+
|
284
|
+
def _add_multi_pauli_gates(
|
285
|
+
self,
|
286
|
+
gates: List[Dict],
|
287
|
+
pauli_string: str,
|
288
|
+
parameters: np.ndarray,
|
289
|
+
rep: int
|
290
|
+
):
|
291
|
+
"""Add multi-qubit Pauli rotation gates."""
|
292
|
+
# For simplicity, handle ZZ case
|
293
|
+
if pauli_string == 'ZZ':
|
294
|
+
entangling_pairs = self._get_entangling_pairs()
|
295
|
+
|
296
|
+
for i, j in entangling_pairs:
|
297
|
+
param_i = rep * self.n_features + i
|
298
|
+
param_j = rep * self.n_features + j
|
299
|
+
|
300
|
+
gates.append({
|
301
|
+
'type': 'RZZ',
|
302
|
+
'qubits': [i, j],
|
303
|
+
'parameter': self.alpha * parameters[param_i] * parameters[param_j],
|
304
|
+
'parameter_names': [f'{self.parameter_prefix}_{param_i}',
|
305
|
+
f'{self.parameter_prefix}_{param_j}']
|
306
|
+
})
|
307
|
+
|
308
|
+
|
309
|
+
def create_feature_map(
|
310
|
+
feature_map_type: str,
|
311
|
+
n_features: int,
|
312
|
+
**kwargs
|
313
|
+
) -> QuantumFeatureMap:
|
314
|
+
"""Factory function to create quantum feature maps.
|
315
|
+
|
316
|
+
Args:
|
317
|
+
feature_map_type: Type of feature map ('Z', 'ZZ', 'Pauli')
|
318
|
+
n_features: Number of input features
|
319
|
+
**kwargs: Additional arguments for specific feature maps
|
320
|
+
|
321
|
+
Returns:
|
322
|
+
QuantumFeatureMap instance
|
323
|
+
|
324
|
+
"""
|
325
|
+
feature_map_type = feature_map_type.upper()
|
326
|
+
|
327
|
+
if feature_map_type == 'Z':
|
328
|
+
return ZFeatureMap(n_features, **kwargs)
|
329
|
+
elif feature_map_type == 'ZZ':
|
330
|
+
return ZZFeatureMap(n_features, **kwargs)
|
331
|
+
elif feature_map_type == 'PAULI':
|
332
|
+
return PauliFeatureMap(n_features, **kwargs)
|
333
|
+
else:
|
334
|
+
raise ValueError(f"Unknown feature map type: {feature_map_type}")
|
335
|
+
|
336
|
+
|
337
|
+
def pauli_feature_map(
|
338
|
+
n_features: int,
|
339
|
+
paulis: List[str] = ['Z', 'ZZ'],
|
340
|
+
reps: int = 1,
|
341
|
+
alpha: float = 2.0,
|
342
|
+
entanglement: str = 'full'
|
343
|
+
) -> PauliFeatureMap:
|
344
|
+
"""Create a Pauli feature map with specified Pauli strings.
|
345
|
+
|
346
|
+
Args:
|
347
|
+
n_features: Number of input features
|
348
|
+
paulis: List of Pauli strings to use
|
349
|
+
reps: Number of repetitions
|
350
|
+
alpha: Scaling factor
|
351
|
+
entanglement: Entanglement pattern
|
352
|
+
|
353
|
+
Returns:
|
354
|
+
PauliFeatureMap instance
|
355
|
+
|
356
|
+
"""
|
357
|
+
return PauliFeatureMap(
|
358
|
+
n_features=n_features,
|
359
|
+
paulis=paulis,
|
360
|
+
reps=reps,
|
361
|
+
alpha=alpha,
|
362
|
+
entanglement=entanglement
|
363
|
+
)
|
364
|
+
|
365
|
+
|
366
|
+
def zz_feature_map(
|
367
|
+
n_features: int,
|
368
|
+
reps: int = 1,
|
369
|
+
entanglement: str = 'linear',
|
370
|
+
alpha: float = 2.0
|
371
|
+
) -> ZZFeatureMap:
|
372
|
+
"""Create a ZZ feature map with specified parameters.
|
373
|
+
|
374
|
+
Args:
|
375
|
+
n_features: Number of input features
|
376
|
+
reps: Number of repetitions
|
377
|
+
entanglement: Entanglement pattern ('linear', 'circular', 'full')
|
378
|
+
alpha: Scaling factor
|
379
|
+
|
380
|
+
Returns:
|
381
|
+
ZZFeatureMap instance
|
382
|
+
|
383
|
+
"""
|
384
|
+
return ZZFeatureMap(
|
385
|
+
n_features=n_features,
|
386
|
+
reps=reps,
|
387
|
+
entanglement=entanglement,
|
388
|
+
alpha=alpha
|
389
|
+
)
|
390
|
+
|
391
|
+
|
392
|
+
def feature_map_from_config(config: FeatureMapConfig) -> QuantumFeatureMap:
|
393
|
+
"""Create feature map from configuration.
|
394
|
+
|
395
|
+
Args:
|
396
|
+
config: FeatureMapConfig instance
|
397
|
+
|
398
|
+
Returns:
|
399
|
+
QuantumFeatureMap instance
|
400
|
+
|
401
|
+
"""
|
402
|
+
# Determine feature map type based on config
|
403
|
+
# This is a simplified approach - in practice, you'd need more logic
|
404
|
+
if config.entanglement == 'none':
|
405
|
+
return ZFeatureMap(
|
406
|
+
n_features=config.n_features,
|
407
|
+
reps=config.reps,
|
408
|
+
parameter_prefix=config.parameter_prefix
|
409
|
+
)
|
410
|
+
else:
|
411
|
+
return ZZFeatureMap(
|
412
|
+
n_features=config.n_features,
|
413
|
+
reps=config.reps,
|
414
|
+
entanglement=config.entanglement,
|
415
|
+
parameter_prefix=config.parameter_prefix
|
416
|
+
)
|
417
|
+
|
418
|
+
|
419
|
+
def evaluate_feature_map_expressibility(
|
420
|
+
feature_map: QuantumFeatureMap,
|
421
|
+
n_samples: int = 1000,
|
422
|
+
random_state: Optional[int] = None
|
423
|
+
) -> Dict[str, float]:
|
424
|
+
"""Evaluate the expressibility of a quantum feature map.
|
425
|
+
|
426
|
+
Expressibility measures how well a feature map can generate
|
427
|
+
diverse quantum states across the Hilbert space.
|
428
|
+
|
429
|
+
Args:
|
430
|
+
feature_map: QuantumFeatureMap to evaluate
|
431
|
+
n_samples: Number of random data points to sample
|
432
|
+
random_state: Random seed
|
433
|
+
|
434
|
+
Returns:
|
435
|
+
Dictionary with expressibility metrics
|
436
|
+
|
437
|
+
"""
|
438
|
+
if random_state is not None:
|
439
|
+
np.random.seed(random_state)
|
440
|
+
|
441
|
+
# Generate random data points
|
442
|
+
X_random = np.random.uniform(-1, 1, (n_samples, feature_map.n_features))
|
443
|
+
|
444
|
+
# Map to quantum circuits
|
445
|
+
circuits = feature_map.map_data(X_random)
|
446
|
+
|
447
|
+
# Compute diversity metrics
|
448
|
+
# This is a simplified version - real implementation would need
|
449
|
+
# to simulate the circuits and compute state overlaps
|
450
|
+
|
451
|
+
parameter_diversity = []
|
452
|
+
for circuit in circuits:
|
453
|
+
params = circuit['parameters']
|
454
|
+
# Measure parameter diversity (standard deviation)
|
455
|
+
param_std = np.std(params)
|
456
|
+
parameter_diversity.append(param_std)
|
457
|
+
|
458
|
+
metrics = {
|
459
|
+
'parameter_diversity_mean': np.mean(parameter_diversity),
|
460
|
+
'parameter_diversity_std': np.std(parameter_diversity),
|
461
|
+
'n_samples': n_samples,
|
462
|
+
'n_features': feature_map.n_features,
|
463
|
+
'n_qubits': feature_map.n_qubits,
|
464
|
+
'reps': feature_map.reps
|
465
|
+
}
|
466
|
+
|
467
|
+
return metrics
|