superquantx 0.1.0__py3-none-any.whl → 0.1.1__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 +24 -12
- superquantx/algorithms/__init__.py +1 -1
- superquantx/algorithms/base_algorithm.py +36 -36
- superquantx/algorithms/hybrid_classifier.py +22 -22
- superquantx/algorithms/qaoa.py +29 -28
- superquantx/algorithms/quantum_agents.py +57 -56
- superquantx/algorithms/quantum_kmeans.py +17 -17
- superquantx/algorithms/quantum_nn.py +18 -18
- superquantx/algorithms/quantum_pca.py +26 -26
- superquantx/algorithms/quantum_svm.py +26 -25
- superquantx/algorithms/vqe.py +40 -39
- superquantx/algorithms.py +56 -55
- superquantx/backends/__init__.py +12 -12
- superquantx/backends/base_backend.py +25 -24
- superquantx/backends/braket_backend.py +21 -21
- superquantx/backends/cirq_backend.py +26 -26
- superquantx/backends/ocean_backend.py +38 -38
- superquantx/backends/pennylane_backend.py +12 -11
- superquantx/backends/qiskit_backend.py +12 -12
- superquantx/backends/simulator_backend.py +31 -17
- superquantx/backends/tket_backend.py +23 -23
- superquantx/circuits.py +25 -25
- superquantx/cli/commands.py +6 -7
- superquantx/cli/main.py +5 -6
- superquantx/client.py +42 -42
- superquantx/config.py +14 -14
- superquantx/datasets/__init__.py +58 -0
- superquantx/datasets/molecular.py +307 -0
- superquantx/datasets/preprocessing.py +279 -0
- superquantx/datasets/quantum_datasets.py +277 -0
- superquantx/datasets/synthetic.py +300 -0
- superquantx/exceptions.py +29 -29
- superquantx/gates.py +26 -26
- superquantx/logging_config.py +29 -29
- superquantx/measurements.py +53 -54
- superquantx/ml.py +51 -52
- superquantx/noise.py +49 -49
- superquantx/utils/benchmarking.py +41 -36
- superquantx/utils/classical_utils.py +32 -32
- superquantx/utils/feature_mapping.py +40 -35
- superquantx/utils/optimization.py +28 -26
- superquantx/utils/quantum_utils.py +47 -48
- superquantx/utils/visualization.py +49 -49
- superquantx/version.py +3 -3
- {superquantx-0.1.0.dist-info → superquantx-0.1.1.dist-info}/METADATA +18 -16
- superquantx-0.1.1.dist-info/RECORD +51 -0
- superquantx-0.1.1.dist-info/licenses/LICENSE +180 -0
- superquantx-0.1.0.dist-info/RECORD +0 -46
- superquantx-0.1.0.dist-info/licenses/LICENSE +0 -21
- {superquantx-0.1.0.dist-info → superquantx-0.1.1.dist-info}/WHEEL +0 -0
- {superquantx-0.1.0.dist-info → superquantx-0.1.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,307 @@
|
|
1
|
+
"""Molecular datasets for quantum chemistry and simulation.
|
2
|
+
|
3
|
+
This module provides molecular datasets and utilities for quantum chemistry
|
4
|
+
applications, including common molecules used in quantum simulation benchmarks.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple
|
8
|
+
|
9
|
+
import numpy as np
|
10
|
+
|
11
|
+
|
12
|
+
class Molecule:
|
13
|
+
"""Represents a molecule for quantum simulation.
|
14
|
+
|
15
|
+
Attributes:
|
16
|
+
name: Molecule name
|
17
|
+
geometry: List of (atom, coordinates) tuples
|
18
|
+
charge: Molecular charge
|
19
|
+
multiplicity: Spin multiplicity
|
20
|
+
basis: Basis set for quantum chemistry calculations
|
21
|
+
hamiltonian: Molecular Hamiltonian (if computed)
|
22
|
+
n_qubits: Number of qubits needed for simulation
|
23
|
+
n_electrons: Number of electrons
|
24
|
+
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(
|
28
|
+
self,
|
29
|
+
name: str,
|
30
|
+
geometry: List[Tuple[str, Tuple[float, float, float]]],
|
31
|
+
charge: int = 0,
|
32
|
+
multiplicity: int = 1,
|
33
|
+
basis: str = 'sto-3g'
|
34
|
+
):
|
35
|
+
self.name = name
|
36
|
+
self.geometry = geometry
|
37
|
+
self.charge = charge
|
38
|
+
self.multiplicity = multiplicity
|
39
|
+
self.basis = basis
|
40
|
+
self.hamiltonian = None
|
41
|
+
self.n_qubits = None
|
42
|
+
self.n_electrons = None
|
43
|
+
|
44
|
+
def to_dict(self) -> Dict[str, Any]:
|
45
|
+
"""Convert molecule to dictionary representation."""
|
46
|
+
return {
|
47
|
+
'name': self.name,
|
48
|
+
'geometry': self.geometry,
|
49
|
+
'charge': self.charge,
|
50
|
+
'multiplicity': self.multiplicity,
|
51
|
+
'basis': self.basis,
|
52
|
+
'n_qubits': self.n_qubits,
|
53
|
+
'n_electrons': self.n_electrons
|
54
|
+
}
|
55
|
+
|
56
|
+
|
57
|
+
def load_molecule(
|
58
|
+
name: str,
|
59
|
+
bond_length: Optional[float] = None,
|
60
|
+
basis: str = 'sto-3g'
|
61
|
+
) -> Tuple[Molecule, Dict[str, Any]]:
|
62
|
+
"""Load a predefined molecule for quantum simulation.
|
63
|
+
|
64
|
+
Args:
|
65
|
+
name: Molecule name ('H2', 'LiH', 'BeH2', 'H2O', 'NH3', 'CH4')
|
66
|
+
bond_length: Custom bond length (if applicable)
|
67
|
+
basis: Basis set for quantum chemistry calculations
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
Tuple of (molecule, metadata)
|
71
|
+
|
72
|
+
"""
|
73
|
+
molecules = {
|
74
|
+
'H2': _create_h2_molecule,
|
75
|
+
'LiH': _create_lih_molecule,
|
76
|
+
'BeH2': _create_beh2_molecule,
|
77
|
+
'H2O': _create_h2o_molecule,
|
78
|
+
'NH3': _create_nh3_molecule,
|
79
|
+
'CH4': _create_ch4_molecule
|
80
|
+
}
|
81
|
+
|
82
|
+
if name not in molecules:
|
83
|
+
raise ValueError(f"Unknown molecule: {name}. Available: {list(molecules.keys())}")
|
84
|
+
|
85
|
+
molecule, metadata = molecules[name](bond_length, basis)
|
86
|
+
return molecule, metadata
|
87
|
+
|
88
|
+
|
89
|
+
def load_h2_molecule(
|
90
|
+
bond_length: float = 0.735,
|
91
|
+
basis: str = 'sto-3g'
|
92
|
+
) -> Tuple[Molecule, Dict[str, Any]]:
|
93
|
+
"""Load H2 molecule with specified bond length.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
bond_length: H-H bond length in Angstroms
|
97
|
+
basis: Basis set
|
98
|
+
|
99
|
+
Returns:
|
100
|
+
Tuple of (molecule, metadata)
|
101
|
+
|
102
|
+
"""
|
103
|
+
return _create_h2_molecule(bond_length, basis)
|
104
|
+
|
105
|
+
|
106
|
+
def load_lih_molecule(
|
107
|
+
bond_length: float = 1.595,
|
108
|
+
basis: str = 'sto-3g'
|
109
|
+
) -> Tuple[Molecule, Dict[str, Any]]:
|
110
|
+
"""Load LiH molecule with specified bond length.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
bond_length: Li-H bond length in Angstroms
|
114
|
+
basis: Basis set
|
115
|
+
|
116
|
+
Returns:
|
117
|
+
Tuple of (molecule, metadata)
|
118
|
+
|
119
|
+
"""
|
120
|
+
return _create_lih_molecule(bond_length, basis)
|
121
|
+
|
122
|
+
|
123
|
+
def load_beh2_molecule(
|
124
|
+
bond_length: float = 1.326,
|
125
|
+
basis: str = 'sto-3g'
|
126
|
+
) -> Tuple[Molecule, Dict[str, Any]]:
|
127
|
+
"""Load BeH2 molecule with specified bond length.
|
128
|
+
|
129
|
+
Args:
|
130
|
+
bond_length: Be-H bond length in Angstroms
|
131
|
+
basis: Basis set
|
132
|
+
|
133
|
+
Returns:
|
134
|
+
Tuple of (molecule, metadata)
|
135
|
+
|
136
|
+
"""
|
137
|
+
return _create_beh2_molecule(bond_length, basis)
|
138
|
+
|
139
|
+
|
140
|
+
def _create_h2_molecule(bond_length: Optional[float], basis: str) -> Tuple[Molecule, Dict[str, Any]]:
|
141
|
+
"""Create H2 molecule."""
|
142
|
+
if bond_length is None:
|
143
|
+
bond_length = 0.735
|
144
|
+
|
145
|
+
geometry = [
|
146
|
+
('H', (0.0, 0.0, 0.0)),
|
147
|
+
('H', (0.0, 0.0, bond_length))
|
148
|
+
]
|
149
|
+
|
150
|
+
molecule = Molecule('H2', geometry, charge=0, multiplicity=1, basis=basis)
|
151
|
+
molecule.n_electrons = 2
|
152
|
+
molecule.n_qubits = 4 # Minimal basis
|
153
|
+
|
154
|
+
metadata = {
|
155
|
+
'molecule_type': 'diatomic',
|
156
|
+
'bond_length': bond_length,
|
157
|
+
'ground_state_energy': -1.137, # Approximate
|
158
|
+
'typical_applications': ['VQE benchmark', 'quantum simulation'],
|
159
|
+
'difficulty': 'easy'
|
160
|
+
}
|
161
|
+
|
162
|
+
return molecule, metadata
|
163
|
+
|
164
|
+
|
165
|
+
def _create_lih_molecule(bond_length: Optional[float], basis: str) -> Tuple[Molecule, Dict[str, Any]]:
|
166
|
+
"""Create LiH molecule."""
|
167
|
+
if bond_length is None:
|
168
|
+
bond_length = 1.595
|
169
|
+
|
170
|
+
geometry = [
|
171
|
+
('Li', (0.0, 0.0, 0.0)),
|
172
|
+
('H', (0.0, 0.0, bond_length))
|
173
|
+
]
|
174
|
+
|
175
|
+
molecule = Molecule('LiH', geometry, charge=0, multiplicity=1, basis=basis)
|
176
|
+
molecule.n_electrons = 4
|
177
|
+
molecule.n_qubits = 12 # Typical for STO-3G
|
178
|
+
|
179
|
+
metadata = {
|
180
|
+
'molecule_type': 'diatomic',
|
181
|
+
'bond_length': bond_length,
|
182
|
+
'ground_state_energy': -8.027, # Approximate
|
183
|
+
'typical_applications': ['VQE', 'quantum chemistry'],
|
184
|
+
'difficulty': 'medium'
|
185
|
+
}
|
186
|
+
|
187
|
+
return molecule, metadata
|
188
|
+
|
189
|
+
|
190
|
+
def _create_beh2_molecule(bond_length: Optional[float], basis: str) -> Tuple[Molecule, Dict[str, Any]]:
|
191
|
+
"""Create BeH2 molecule."""
|
192
|
+
if bond_length is None:
|
193
|
+
bond_length = 1.326
|
194
|
+
|
195
|
+
geometry = [
|
196
|
+
('Be', (0.0, 0.0, 0.0)),
|
197
|
+
('H', (0.0, 0.0, bond_length)),
|
198
|
+
('H', (0.0, 0.0, -bond_length))
|
199
|
+
]
|
200
|
+
|
201
|
+
molecule = Molecule('BeH2', geometry, charge=0, multiplicity=1, basis=basis)
|
202
|
+
molecule.n_electrons = 6
|
203
|
+
molecule.n_qubits = 14 # Typical for STO-3G
|
204
|
+
|
205
|
+
metadata = {
|
206
|
+
'molecule_type': 'triatomic',
|
207
|
+
'bond_length': bond_length,
|
208
|
+
'ground_state_energy': -15.77, # Approximate
|
209
|
+
'typical_applications': ['quantum chemistry', 'benchmarking'],
|
210
|
+
'difficulty': 'medium'
|
211
|
+
}
|
212
|
+
|
213
|
+
return molecule, metadata
|
214
|
+
|
215
|
+
|
216
|
+
def _create_h2o_molecule(bond_length: Optional[float], basis: str) -> Tuple[Molecule, Dict[str, Any]]:
|
217
|
+
"""Create H2O molecule."""
|
218
|
+
if bond_length is None:
|
219
|
+
bond_length = 0.958
|
220
|
+
|
221
|
+
# Water geometry (bent structure)
|
222
|
+
angle = 104.5 * np.pi / 180 # HOH angle in radians
|
223
|
+
|
224
|
+
geometry = [
|
225
|
+
('O', (0.0, 0.0, 0.0)),
|
226
|
+
('H', (bond_length * np.sin(angle/2), 0.0, bond_length * np.cos(angle/2))),
|
227
|
+
('H', (-bond_length * np.sin(angle/2), 0.0, bond_length * np.cos(angle/2)))
|
228
|
+
]
|
229
|
+
|
230
|
+
molecule = Molecule('H2O', geometry, charge=0, multiplicity=1, basis=basis)
|
231
|
+
molecule.n_electrons = 10
|
232
|
+
molecule.n_qubits = 20 # Approximate
|
233
|
+
|
234
|
+
metadata = {
|
235
|
+
'molecule_type': 'triatomic',
|
236
|
+
'bond_length': bond_length,
|
237
|
+
'bond_angle': 104.5,
|
238
|
+
'ground_state_energy': -76.0, # Approximate
|
239
|
+
'typical_applications': ['quantum chemistry', 'environmental modeling'],
|
240
|
+
'difficulty': 'hard'
|
241
|
+
}
|
242
|
+
|
243
|
+
return molecule, metadata
|
244
|
+
|
245
|
+
|
246
|
+
def _create_nh3_molecule(bond_length: Optional[float], basis: str) -> Tuple[Molecule, Dict[str, Any]]:
|
247
|
+
"""Create NH3 molecule."""
|
248
|
+
if bond_length is None:
|
249
|
+
bond_length = 1.012
|
250
|
+
|
251
|
+
# Ammonia geometry (trigonal pyramid)
|
252
|
+
angle = 106.67 * np.pi / 180 # HNH angle
|
253
|
+
|
254
|
+
geometry = [
|
255
|
+
('N', (0.0, 0.0, 0.0)),
|
256
|
+
('H', (bond_length, 0.0, 0.0)),
|
257
|
+
('H', (-bond_length * np.cos(angle), bond_length * np.sin(angle), 0.0)),
|
258
|
+
('H', (-bond_length * np.cos(angle), -bond_length * np.sin(angle), 0.0))
|
259
|
+
]
|
260
|
+
|
261
|
+
molecule = Molecule('NH3', geometry, charge=0, multiplicity=1, basis=basis)
|
262
|
+
molecule.n_electrons = 10
|
263
|
+
molecule.n_qubits = 22 # Approximate
|
264
|
+
|
265
|
+
metadata = {
|
266
|
+
'molecule_type': 'tetrahedral',
|
267
|
+
'bond_length': bond_length,
|
268
|
+
'ground_state_energy': -56.2, # Approximate
|
269
|
+
'typical_applications': ['quantum chemistry', 'catalysis modeling'],
|
270
|
+
'difficulty': 'hard'
|
271
|
+
}
|
272
|
+
|
273
|
+
return molecule, metadata
|
274
|
+
|
275
|
+
|
276
|
+
def _create_ch4_molecule(bond_length: Optional[float], basis: str) -> Tuple[Molecule, Dict[str, Any]]:
|
277
|
+
"""Create CH4 molecule."""
|
278
|
+
if bond_length is None:
|
279
|
+
bond_length = 1.087
|
280
|
+
|
281
|
+
# Methane geometry (tetrahedral)
|
282
|
+
# Tetrahedral coordinates
|
283
|
+
tet_coords = [
|
284
|
+
(1, 1, 1),
|
285
|
+
(1, -1, -1),
|
286
|
+
(-1, 1, -1),
|
287
|
+
(-1, -1, 1)
|
288
|
+
]
|
289
|
+
|
290
|
+
geometry = [('C', (0.0, 0.0, 0.0))]
|
291
|
+
for x, y, z in tet_coords:
|
292
|
+
coord = np.array([x, y, z]) * bond_length / np.sqrt(3)
|
293
|
+
geometry.append(('H', tuple(coord)))
|
294
|
+
|
295
|
+
molecule = Molecule('CH4', geometry, charge=0, multiplicity=1, basis=basis)
|
296
|
+
molecule.n_electrons = 10
|
297
|
+
molecule.n_qubits = 24 # Approximate
|
298
|
+
|
299
|
+
metadata = {
|
300
|
+
'molecule_type': 'tetrahedral',
|
301
|
+
'bond_length': bond_length,
|
302
|
+
'ground_state_energy': -40.2, # Approximate
|
303
|
+
'typical_applications': ['quantum chemistry', 'combustion modeling'],
|
304
|
+
'difficulty': 'very_hard'
|
305
|
+
}
|
306
|
+
|
307
|
+
return molecule, metadata
|
@@ -0,0 +1,279 @@
|
|
1
|
+
"""Data preprocessing utilities for quantum machine learning.
|
2
|
+
|
3
|
+
This module provides preprocessing functions and classes to prepare
|
4
|
+
classical data for quantum machine learning algorithms.
|
5
|
+
"""
|
6
|
+
|
7
|
+
from typing import Literal, Optional
|
8
|
+
|
9
|
+
import numpy as np
|
10
|
+
from sklearn.preprocessing import MinMaxScaler, StandardScaler
|
11
|
+
|
12
|
+
|
13
|
+
class QuantumFeatureEncoder:
|
14
|
+
"""Base class for quantum feature encoding strategies.
|
15
|
+
|
16
|
+
Quantum feature encoding maps classical data to quantum states,
|
17
|
+
which is crucial for quantum machine learning algorithms.
|
18
|
+
"""
|
19
|
+
|
20
|
+
def __init__(self, encoding_type: str = 'amplitude'):
|
21
|
+
self.encoding_type = encoding_type
|
22
|
+
self.is_fitted = False
|
23
|
+
|
24
|
+
def fit(self, X: np.ndarray) -> 'QuantumFeatureEncoder':
|
25
|
+
"""Fit the encoder to training data."""
|
26
|
+
raise NotImplementedError
|
27
|
+
|
28
|
+
def transform(self, X: np.ndarray) -> np.ndarray:
|
29
|
+
"""Transform data using the fitted encoder."""
|
30
|
+
raise NotImplementedError
|
31
|
+
|
32
|
+
def fit_transform(self, X: np.ndarray) -> np.ndarray:
|
33
|
+
"""Fit encoder and transform data in one step."""
|
34
|
+
return self.fit(X).transform(X)
|
35
|
+
|
36
|
+
|
37
|
+
class AmplitudeEncoder(QuantumFeatureEncoder):
|
38
|
+
"""Amplitude encoding for quantum machine learning.
|
39
|
+
|
40
|
+
Encodes classical data as amplitudes of quantum states.
|
41
|
+
Each data point becomes a quantum state |ψ⟩ = Σᵢ xᵢ|i⟩.
|
42
|
+
|
43
|
+
The data is normalized so that ||x||₂ = 1 for proper quantum state encoding.
|
44
|
+
"""
|
45
|
+
|
46
|
+
def __init__(self, normalize_samples: bool = True, normalize_features: bool = False):
|
47
|
+
super().__init__('amplitude')
|
48
|
+
self.normalize_samples = normalize_samples
|
49
|
+
self.normalize_features = normalize_features
|
50
|
+
self.feature_scaler = None
|
51
|
+
|
52
|
+
def fit(self, X: np.ndarray) -> 'AmplitudeEncoder':
|
53
|
+
"""Fit the amplitude encoder.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
X: Training data of shape (n_samples, n_features)
|
57
|
+
|
58
|
+
Returns:
|
59
|
+
Self for method chaining
|
60
|
+
|
61
|
+
"""
|
62
|
+
if self.normalize_features:
|
63
|
+
self.feature_scaler = StandardScaler()
|
64
|
+
self.feature_scaler.fit(X)
|
65
|
+
|
66
|
+
self.is_fitted = True
|
67
|
+
return self
|
68
|
+
|
69
|
+
def transform(self, X: np.ndarray) -> np.ndarray:
|
70
|
+
"""Transform data using amplitude encoding.
|
71
|
+
|
72
|
+
Args:
|
73
|
+
X: Data to transform of shape (n_samples, n_features)
|
74
|
+
|
75
|
+
Returns:
|
76
|
+
Transformed data with proper normalization
|
77
|
+
|
78
|
+
"""
|
79
|
+
if not self.is_fitted:
|
80
|
+
raise ValueError("Encoder must be fitted before transform")
|
81
|
+
|
82
|
+
X_encoded = X.copy()
|
83
|
+
|
84
|
+
# Feature normalization
|
85
|
+
if self.normalize_features and self.feature_scaler is not None:
|
86
|
+
X_encoded = self.feature_scaler.transform(X_encoded)
|
87
|
+
|
88
|
+
# Sample normalization (L2 norm = 1 for each sample)
|
89
|
+
if self.normalize_samples:
|
90
|
+
norms = np.linalg.norm(X_encoded, axis=1, keepdims=True)
|
91
|
+
norms[norms == 0] = 1 # Avoid division by zero
|
92
|
+
X_encoded = X_encoded / norms
|
93
|
+
|
94
|
+
return X_encoded
|
95
|
+
|
96
|
+
|
97
|
+
class AngleEncoder(QuantumFeatureEncoder):
|
98
|
+
"""Angle encoding for quantum machine learning.
|
99
|
+
|
100
|
+
Encodes classical features as rotation angles in quantum circuits.
|
101
|
+
Each feature xᵢ becomes a rotation angle, typically in [0, 2π].
|
102
|
+
"""
|
103
|
+
|
104
|
+
def __init__(self, angle_range: tuple = (0, 2 * np.pi)):
|
105
|
+
super().__init__('angle')
|
106
|
+
self.angle_range = angle_range
|
107
|
+
self.scaler = None
|
108
|
+
|
109
|
+
def fit(self, X: np.ndarray) -> 'AngleEncoder':
|
110
|
+
"""Fit the angle encoder.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
X: Training data of shape (n_samples, n_features)
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
Self for method chaining
|
117
|
+
|
118
|
+
"""
|
119
|
+
self.scaler = MinMaxScaler(feature_range=self.angle_range)
|
120
|
+
self.scaler.fit(X)
|
121
|
+
self.is_fitted = True
|
122
|
+
return self
|
123
|
+
|
124
|
+
def transform(self, X: np.ndarray) -> np.ndarray:
|
125
|
+
"""Transform data using angle encoding.
|
126
|
+
|
127
|
+
Args:
|
128
|
+
X: Data to transform of shape (n_samples, n_features)
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
Data scaled to angle range
|
132
|
+
|
133
|
+
"""
|
134
|
+
if not self.is_fitted:
|
135
|
+
raise ValueError("Encoder must be fitted before transform")
|
136
|
+
|
137
|
+
return self.scaler.transform(X)
|
138
|
+
|
139
|
+
|
140
|
+
class BasisEncoder(QuantumFeatureEncoder):
|
141
|
+
"""Basis encoding for quantum machine learning.
|
142
|
+
|
143
|
+
Encodes classical data directly in the computational basis.
|
144
|
+
Each data point corresponds to a basis state |x⟩.
|
145
|
+
"""
|
146
|
+
|
147
|
+
def __init__(self, n_qubits: Optional[int] = None):
|
148
|
+
super().__init__('basis')
|
149
|
+
self.n_qubits = n_qubits
|
150
|
+
|
151
|
+
def fit(self, X: np.ndarray) -> 'BasisEncoder':
|
152
|
+
"""Fit the basis encoder."""
|
153
|
+
if self.n_qubits is None:
|
154
|
+
# Determine number of qubits needed
|
155
|
+
max_value = np.max(X)
|
156
|
+
self.n_qubits = int(np.ceil(np.log2(max_value + 1)))
|
157
|
+
|
158
|
+
self.is_fitted = True
|
159
|
+
return self
|
160
|
+
|
161
|
+
def transform(self, X: np.ndarray) -> np.ndarray:
|
162
|
+
"""Transform data for basis encoding."""
|
163
|
+
if not self.is_fitted:
|
164
|
+
raise ValueError("Encoder must be fitted before transform")
|
165
|
+
|
166
|
+
# Convert to integers for basis states
|
167
|
+
X_int = np.round(X).astype(int)
|
168
|
+
|
169
|
+
# Clip to valid range
|
170
|
+
max_state = 2 ** self.n_qubits - 1
|
171
|
+
X_int = np.clip(X_int, 0, max_state)
|
172
|
+
|
173
|
+
return X_int
|
174
|
+
|
175
|
+
|
176
|
+
def normalize_quantum_data(
|
177
|
+
X: np.ndarray,
|
178
|
+
method: Literal['l1', 'l2', 'max'] = 'l2',
|
179
|
+
axis: int = 1
|
180
|
+
) -> np.ndarray:
|
181
|
+
"""Normalize data for quantum machine learning.
|
182
|
+
|
183
|
+
Args:
|
184
|
+
X: Data to normalize of shape (n_samples, n_features)
|
185
|
+
method: Normalization method ('l1', 'l2', or 'max')
|
186
|
+
axis: Axis along which to normalize (1 for samples, 0 for features)
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
Normalized data
|
190
|
+
|
191
|
+
"""
|
192
|
+
if method == 'l1':
|
193
|
+
norms = np.sum(np.abs(X), axis=axis, keepdims=True)
|
194
|
+
elif method == 'l2':
|
195
|
+
norms = np.sqrt(np.sum(X ** 2, axis=axis, keepdims=True))
|
196
|
+
elif method == 'max':
|
197
|
+
norms = np.max(np.abs(X), axis=axis, keepdims=True)
|
198
|
+
else:
|
199
|
+
raise ValueError(f"Unknown normalization method: {method}")
|
200
|
+
|
201
|
+
# Avoid division by zero
|
202
|
+
norms[norms == 0] = 1
|
203
|
+
|
204
|
+
return X / norms
|
205
|
+
|
206
|
+
|
207
|
+
def pad_to_power_of_two(X: np.ndarray, pad_value: float = 0.0) -> np.ndarray:
|
208
|
+
"""Pad feature dimension to next power of two.
|
209
|
+
|
210
|
+
Many quantum algorithms work more efficiently when the number
|
211
|
+
of features is a power of two.
|
212
|
+
|
213
|
+
Args:
|
214
|
+
X: Input data of shape (n_samples, n_features)
|
215
|
+
pad_value: Value to use for padding
|
216
|
+
|
217
|
+
Returns:
|
218
|
+
Padded data with power-of-two feature dimension
|
219
|
+
|
220
|
+
"""
|
221
|
+
n_features = X.shape[1]
|
222
|
+
n_qubits = int(np.ceil(np.log2(n_features)))
|
223
|
+
n_padded = 2 ** n_qubits
|
224
|
+
|
225
|
+
if n_padded == n_features:
|
226
|
+
return X
|
227
|
+
|
228
|
+
# Create padding
|
229
|
+
n_samples = X.shape[0]
|
230
|
+
padding_shape = (n_samples, n_padded - n_features)
|
231
|
+
padding = np.full(padding_shape, pad_value)
|
232
|
+
|
233
|
+
return np.hstack([X, padding])
|
234
|
+
|
235
|
+
|
236
|
+
def quantum_feature_map(
|
237
|
+
X: np.ndarray,
|
238
|
+
feature_map_type: str = 'ZZFeatureMap',
|
239
|
+
reps: int = 1,
|
240
|
+
parameter_prefix: str = 'x'
|
241
|
+
) -> dict:
|
242
|
+
"""Generate quantum feature map parameters.
|
243
|
+
|
244
|
+
Args:
|
245
|
+
X: Input data
|
246
|
+
feature_map_type: Type of feature map
|
247
|
+
reps: Number of repetitions
|
248
|
+
parameter_prefix: Prefix for parameter names
|
249
|
+
|
250
|
+
Returns:
|
251
|
+
Dictionary with feature map configuration
|
252
|
+
|
253
|
+
"""
|
254
|
+
n_features = X.shape[1]
|
255
|
+
|
256
|
+
feature_map_config = {
|
257
|
+
'type': feature_map_type,
|
258
|
+
'n_qubits': n_features,
|
259
|
+
'n_features': n_features,
|
260
|
+
'reps': reps,
|
261
|
+
'parameter_prefix': parameter_prefix,
|
262
|
+
'data_shape': X.shape
|
263
|
+
}
|
264
|
+
|
265
|
+
if feature_map_type == 'ZZFeatureMap':
|
266
|
+
feature_map_config.update({
|
267
|
+
'entanglement': 'linear',
|
268
|
+
'alpha': 2.0
|
269
|
+
})
|
270
|
+
elif feature_map_type == 'ZFeatureMap':
|
271
|
+
feature_map_config.update({
|
272
|
+
'rotation': 'Y'
|
273
|
+
})
|
274
|
+
elif feature_map_type == 'PauliFeatureMap':
|
275
|
+
feature_map_config.update({
|
276
|
+
'paulis': ['Z', 'ZZ']
|
277
|
+
})
|
278
|
+
|
279
|
+
return feature_map_config
|