quantumflow-sdk 0.2.1__py3-none-any.whl → 0.4.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.
- api/main.py +34 -3
- api/models.py +41 -0
- api/routes/algorithm_routes.py +1029 -0
- api/routes/chat_routes.py +565 -0
- api/routes/pipeline_routes.py +578 -0
- db/models.py +357 -0
- quantumflow/algorithms/machine_learning/__init__.py +14 -2
- quantumflow/algorithms/machine_learning/vqe.py +355 -3
- quantumflow/core/__init__.py +10 -1
- quantumflow/core/quantum_compressor.py +379 -1
- quantumflow/integrations/domain_agents.py +617 -0
- quantumflow/pipeline/__init__.py +29 -0
- quantumflow/pipeline/anomaly_detector.py +521 -0
- quantumflow/pipeline/base_pipeline.py +602 -0
- quantumflow/pipeline/checkpoint_manager.py +587 -0
- quantumflow/pipeline/finance/__init__.py +5 -0
- quantumflow/pipeline/finance/portfolio_optimization.py +595 -0
- quantumflow/pipeline/healthcare/__init__.py +5 -0
- quantumflow/pipeline/healthcare/protein_folding.py +994 -0
- quantumflow/pipeline/temporal_memory.py +577 -0
- {quantumflow_sdk-0.2.1.dist-info → quantumflow_sdk-0.4.0.dist-info}/METADATA +3 -3
- {quantumflow_sdk-0.2.1.dist-info → quantumflow_sdk-0.4.0.dist-info}/RECORD +25 -12
- {quantumflow_sdk-0.2.1.dist-info → quantumflow_sdk-0.4.0.dist-info}/WHEEL +0 -0
- {quantumflow_sdk-0.2.1.dist-info → quantumflow_sdk-0.4.0.dist-info}/entry_points.txt +0 -0
- {quantumflow_sdk-0.2.1.dist-info → quantumflow_sdk-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -8,11 +8,16 @@ Key Results:
|
|
|
8
8
|
- Compression ratio: 2.1×
|
|
9
9
|
- Token reduction: 53.3%
|
|
10
10
|
- Memory: O(log n) vs O(n) classical
|
|
11
|
+
|
|
12
|
+
Enhanced with Gray-code measurement protocol from arXiv:2601.00247:
|
|
13
|
+
- Measurement settings reduced from O(N) to 2n+1
|
|
14
|
+
- Efficient amplitude and phase extraction
|
|
15
|
+
- Logarithmic qubit encoding preserved
|
|
11
16
|
"""
|
|
12
17
|
|
|
13
18
|
import math
|
|
14
19
|
from dataclasses import dataclass, field
|
|
15
|
-
from typing import Optional
|
|
20
|
+
from typing import Optional, Literal
|
|
16
21
|
import numpy as np
|
|
17
22
|
from qiskit import QuantumCircuit
|
|
18
23
|
|
|
@@ -81,6 +86,313 @@ class CompressedResult:
|
|
|
81
86
|
return reconstructed
|
|
82
87
|
|
|
83
88
|
|
|
89
|
+
def generate_gray_code(n_bits: int) -> list[int]:
|
|
90
|
+
"""
|
|
91
|
+
Generate Gray code sequence for n bits.
|
|
92
|
+
|
|
93
|
+
Gray code ensures adjacent codewords differ by exactly one bit,
|
|
94
|
+
enabling efficient phase extraction with minimal measurements.
|
|
95
|
+
|
|
96
|
+
Based on arXiv:2601.00247 Section IV.B
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
n_bits: Number of bits in the Gray code
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
List of integers in Gray code order
|
|
103
|
+
"""
|
|
104
|
+
if n_bits == 0:
|
|
105
|
+
return [0]
|
|
106
|
+
|
|
107
|
+
gray_sequence = []
|
|
108
|
+
for i in range(2 ** n_bits):
|
|
109
|
+
# Gray code: i XOR (i >> 1)
|
|
110
|
+
gray_sequence.append(i ^ (i >> 1))
|
|
111
|
+
return gray_sequence
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def gray_code_to_binary(gray: int) -> int:
|
|
115
|
+
"""Convert Gray code to binary."""
|
|
116
|
+
binary = gray
|
|
117
|
+
mask = gray >> 1
|
|
118
|
+
while mask:
|
|
119
|
+
binary ^= mask
|
|
120
|
+
mask >>= 1
|
|
121
|
+
return binary
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def binary_to_gray(binary: int) -> int:
|
|
125
|
+
"""Convert binary to Gray code."""
|
|
126
|
+
return binary ^ (binary >> 1)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@dataclass
|
|
130
|
+
class GrayCodeMeasurementResult:
|
|
131
|
+
"""
|
|
132
|
+
Result from Gray-code optimized measurement.
|
|
133
|
+
|
|
134
|
+
Based on arXiv:2601.00247 - reduces measurement settings
|
|
135
|
+
from O(N) to 2n+1 where n = log₂(N).
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
amplitudes: np.ndarray
|
|
139
|
+
phases: np.ndarray
|
|
140
|
+
n_qubits: int
|
|
141
|
+
n_measurement_settings: int
|
|
142
|
+
measurement_type: str # 'z', 'xx', 'xy'
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def complex_amplitudes(self) -> np.ndarray:
|
|
146
|
+
"""Get full complex amplitudes α_j = |α_j|e^(iθ_j)."""
|
|
147
|
+
return self.amplitudes * np.exp(1j * self.phases)
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def probabilities(self) -> np.ndarray:
|
|
151
|
+
"""Get measurement probabilities |α_j|²."""
|
|
152
|
+
return np.abs(self.amplitudes) ** 2
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class GrayCodeMeasurement:
|
|
156
|
+
"""
|
|
157
|
+
Gray-code optimized measurement protocol.
|
|
158
|
+
|
|
159
|
+
Implements efficient amplitude and phase extraction from
|
|
160
|
+
arXiv:2601.00247 Section IV.B.
|
|
161
|
+
|
|
162
|
+
Key insight: By arranging codewords along a cyclic Gray code path,
|
|
163
|
+
each measurement operator couples exactly one pair of basis states,
|
|
164
|
+
enabling unique phase reconstruction.
|
|
165
|
+
|
|
166
|
+
Measurement Protocol:
|
|
167
|
+
1. M_Z: One Z-basis measurement → amplitudes |α_j|
|
|
168
|
+
2. M_XX: n X-basis measurements → cos(θ_{j+1} - θ_j)
|
|
169
|
+
3. M_XY: n XY-basis measurements → sin(θ_{j+1} - θ_j)
|
|
170
|
+
|
|
171
|
+
Total: 2n+1 measurement settings (vs O(N) standard)
|
|
172
|
+
|
|
173
|
+
Example:
|
|
174
|
+
>>> gm = GrayCodeMeasurement(n_qubits=4)
|
|
175
|
+
>>> result = gm.extract_amplitudes_and_phases(execution_result)
|
|
176
|
+
>>> print(f"Used {result.n_measurement_settings} measurement settings")
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
def __init__(self, n_qubits: int):
|
|
180
|
+
"""
|
|
181
|
+
Initialize Gray-code measurement protocol.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
n_qubits: Number of qubits in the system
|
|
185
|
+
"""
|
|
186
|
+
self.n_qubits = n_qubits
|
|
187
|
+
self.n_states = 2 ** n_qubits
|
|
188
|
+
self.gray_sequence = generate_gray_code(n_qubits)
|
|
189
|
+
|
|
190
|
+
def get_measurement_settings_count(self) -> int:
|
|
191
|
+
"""
|
|
192
|
+
Get total number of measurement settings needed.
|
|
193
|
+
|
|
194
|
+
Returns 2n+1 where n is number of qubits.
|
|
195
|
+
"""
|
|
196
|
+
return 2 * self.n_qubits + 1
|
|
197
|
+
|
|
198
|
+
def extract_amplitudes(self, z_counts: dict[str, int], total_shots: int) -> np.ndarray:
|
|
199
|
+
"""
|
|
200
|
+
Extract amplitudes from Z-basis measurement.
|
|
201
|
+
|
|
202
|
+
|α_j| = sqrt((1 - <Z_j>) / 2)
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
z_counts: Measurement counts from Z-basis
|
|
206
|
+
total_shots: Total number of shots
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Array of amplitude magnitudes
|
|
210
|
+
"""
|
|
211
|
+
amplitudes = np.zeros(self.n_states)
|
|
212
|
+
|
|
213
|
+
for bitstring, count in z_counts.items():
|
|
214
|
+
# Convert bitstring to state index (Gray code order)
|
|
215
|
+
state_idx = int(bitstring, 2)
|
|
216
|
+
prob = count / total_shots
|
|
217
|
+
amplitudes[state_idx] = np.sqrt(prob)
|
|
218
|
+
|
|
219
|
+
# Normalize
|
|
220
|
+
norm = np.linalg.norm(amplitudes)
|
|
221
|
+
if norm > 0:
|
|
222
|
+
amplitudes /= norm
|
|
223
|
+
|
|
224
|
+
return amplitudes
|
|
225
|
+
|
|
226
|
+
def extract_phase_differences(
|
|
227
|
+
self,
|
|
228
|
+
xx_correlators: np.ndarray,
|
|
229
|
+
xy_correlators: np.ndarray,
|
|
230
|
+
amplitudes: np.ndarray,
|
|
231
|
+
) -> np.ndarray:
|
|
232
|
+
"""
|
|
233
|
+
Extract relative phases using Gray-code path.
|
|
234
|
+
|
|
235
|
+
θ_{j+1} - θ_j = arctan(<X_j Y_{j+1}> / <X_j X_{j+1}>)
|
|
236
|
+
|
|
237
|
+
Based on Eq. (13) and (22) from arXiv:2601.00247.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
xx_correlators: <X_j X_{j+1}> for neighboring pairs
|
|
241
|
+
xy_correlators: <X_j Y_{j+1}> for neighboring pairs
|
|
242
|
+
amplitudes: Previously extracted amplitudes
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Array of phases (relative to first state)
|
|
246
|
+
"""
|
|
247
|
+
phases = np.zeros(self.n_states)
|
|
248
|
+
|
|
249
|
+
# Walk along Gray code path to accumulate phases
|
|
250
|
+
for i in range(len(self.gray_sequence) - 1):
|
|
251
|
+
j = self.gray_sequence[i]
|
|
252
|
+
k = self.gray_sequence[i + 1]
|
|
253
|
+
|
|
254
|
+
# Get amplitude product
|
|
255
|
+
amp_product = amplitudes[j] * amplitudes[k]
|
|
256
|
+
|
|
257
|
+
if amp_product > 1e-10: # Avoid division by zero
|
|
258
|
+
# Normalize correlators
|
|
259
|
+
cos_diff = xx_correlators[i] / (2 * amp_product)
|
|
260
|
+
sin_diff = xy_correlators[i] / (2 * amp_product)
|
|
261
|
+
|
|
262
|
+
# Clamp to valid range
|
|
263
|
+
cos_diff = np.clip(cos_diff, -1, 1)
|
|
264
|
+
sin_diff = np.clip(sin_diff, -1, 1)
|
|
265
|
+
|
|
266
|
+
# Phase difference
|
|
267
|
+
phase_diff = np.arctan2(sin_diff, cos_diff)
|
|
268
|
+
else:
|
|
269
|
+
phase_diff = 0.0
|
|
270
|
+
|
|
271
|
+
# Accumulate phase along path
|
|
272
|
+
phases[k] = phases[j] + phase_diff
|
|
273
|
+
|
|
274
|
+
return phases
|
|
275
|
+
|
|
276
|
+
def build_xx_measurement_circuits(
|
|
277
|
+
self,
|
|
278
|
+
base_circuit: QuantumCircuit,
|
|
279
|
+
) -> list[QuantumCircuit]:
|
|
280
|
+
"""
|
|
281
|
+
Build circuits for XX correlator measurements.
|
|
282
|
+
|
|
283
|
+
For each neighboring pair in Gray code, measure in X basis
|
|
284
|
+
on qubits that differ, Z basis on qubits that match.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
base_circuit: The state preparation circuit
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
List of measurement circuits
|
|
291
|
+
"""
|
|
292
|
+
circuits = []
|
|
293
|
+
|
|
294
|
+
for i in range(self.n_qubits):
|
|
295
|
+
qc = base_circuit.copy()
|
|
296
|
+
qc.name = f"xx_measure_bit{i}"
|
|
297
|
+
|
|
298
|
+
# Apply H to qubit i (measure in X basis)
|
|
299
|
+
qc.h(i)
|
|
300
|
+
qc.measure_all()
|
|
301
|
+
circuits.append(qc)
|
|
302
|
+
|
|
303
|
+
return circuits
|
|
304
|
+
|
|
305
|
+
def build_xy_measurement_circuits(
|
|
306
|
+
self,
|
|
307
|
+
base_circuit: QuantumCircuit,
|
|
308
|
+
) -> list[QuantumCircuit]:
|
|
309
|
+
"""
|
|
310
|
+
Build circuits for XY correlator measurements.
|
|
311
|
+
|
|
312
|
+
Alternating X and Y basis for phase extraction.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
base_circuit: The state preparation circuit
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
List of measurement circuits
|
|
319
|
+
"""
|
|
320
|
+
circuits = []
|
|
321
|
+
|
|
322
|
+
for i in range(self.n_qubits):
|
|
323
|
+
qc = base_circuit.copy()
|
|
324
|
+
qc.name = f"xy_measure_bit{i}"
|
|
325
|
+
|
|
326
|
+
# Apply H to qubit i (X basis)
|
|
327
|
+
qc.h(i)
|
|
328
|
+
|
|
329
|
+
# Apply S†H to qubit (i+1) mod n (Y basis)
|
|
330
|
+
next_qubit = (i + 1) % self.n_qubits
|
|
331
|
+
qc.sdg(next_qubit)
|
|
332
|
+
qc.h(next_qubit)
|
|
333
|
+
|
|
334
|
+
qc.measure_all()
|
|
335
|
+
circuits.append(qc)
|
|
336
|
+
|
|
337
|
+
return circuits
|
|
338
|
+
|
|
339
|
+
def full_reconstruction(
|
|
340
|
+
self,
|
|
341
|
+
z_result: dict[str, int],
|
|
342
|
+
xx_results: list[dict[str, int]],
|
|
343
|
+
xy_results: list[dict[str, int]],
|
|
344
|
+
total_shots: int,
|
|
345
|
+
) -> GrayCodeMeasurementResult:
|
|
346
|
+
"""
|
|
347
|
+
Perform full amplitude and phase reconstruction.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
z_result: Z-basis measurement counts
|
|
351
|
+
xx_results: List of XX measurement counts
|
|
352
|
+
xy_results: List of XY measurement counts
|
|
353
|
+
total_shots: Shots per measurement
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
GrayCodeMeasurementResult with amplitudes and phases
|
|
357
|
+
"""
|
|
358
|
+
# Step 1: Extract amplitudes from Z measurement
|
|
359
|
+
amplitudes = self.extract_amplitudes(z_result, total_shots)
|
|
360
|
+
|
|
361
|
+
# Step 2: Compute XX correlators
|
|
362
|
+
xx_correlators = np.zeros(self.n_qubits)
|
|
363
|
+
for i, counts in enumerate(xx_results):
|
|
364
|
+
correlator = 0.0
|
|
365
|
+
for bitstring, count in counts.items():
|
|
366
|
+
# Parity of bits i and (i+1)
|
|
367
|
+
bits = [int(b) for b in bitstring]
|
|
368
|
+
parity = (-1) ** (bits[i] + bits[(i + 1) % self.n_qubits])
|
|
369
|
+
correlator += parity * count / total_shots
|
|
370
|
+
xx_correlators[i] = correlator
|
|
371
|
+
|
|
372
|
+
# Step 3: Compute XY correlators
|
|
373
|
+
xy_correlators = np.zeros(self.n_qubits)
|
|
374
|
+
for i, counts in enumerate(xy_results):
|
|
375
|
+
correlator = 0.0
|
|
376
|
+
for bitstring, count in counts.items():
|
|
377
|
+
bits = [int(b) for b in bitstring]
|
|
378
|
+
parity = (-1) ** (bits[i] + bits[(i + 1) % self.n_qubits])
|
|
379
|
+
correlator += parity * count / total_shots
|
|
380
|
+
xy_correlators[i] = correlator
|
|
381
|
+
|
|
382
|
+
# Step 4: Extract phases
|
|
383
|
+
phases = self.extract_phase_differences(
|
|
384
|
+
xx_correlators, xy_correlators, amplitudes
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
return GrayCodeMeasurementResult(
|
|
388
|
+
amplitudes=amplitudes,
|
|
389
|
+
phases=phases,
|
|
390
|
+
n_qubits=self.n_qubits,
|
|
391
|
+
n_measurement_settings=2 * self.n_qubits + 1,
|
|
392
|
+
measurement_type="full_reconstruction",
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
|
|
84
396
|
class QuantumCompressor:
|
|
85
397
|
"""
|
|
86
398
|
Quantum token compression using amplitude encoding.
|
|
@@ -288,6 +600,72 @@ class QuantumCompressor:
|
|
|
288
600
|
|
|
289
601
|
return float(fidelity)
|
|
290
602
|
|
|
603
|
+
def compress_with_gray_code(
|
|
604
|
+
self,
|
|
605
|
+
tokens: list[int],
|
|
606
|
+
n_qubits: Optional[int] = None,
|
|
607
|
+
compression_level: float = 1.0,
|
|
608
|
+
shots: int = 1024,
|
|
609
|
+
) -> tuple[CompressedResult, GrayCodeMeasurementResult]:
|
|
610
|
+
"""
|
|
611
|
+
Compress tokens and extract using Gray-code measurement protocol.
|
|
612
|
+
|
|
613
|
+
This method uses the efficient measurement protocol from
|
|
614
|
+
arXiv:2601.00247 which requires only 2n+1 measurement settings
|
|
615
|
+
instead of O(N) for full state tomography.
|
|
616
|
+
|
|
617
|
+
Args:
|
|
618
|
+
tokens: List of integer tokens to compress
|
|
619
|
+
n_qubits: Number of qubits (auto-calculated if None)
|
|
620
|
+
compression_level: 0.0-1.0, higher = more compression
|
|
621
|
+
shots: Measurement shots per setting
|
|
622
|
+
|
|
623
|
+
Returns:
|
|
624
|
+
Tuple of (CompressedResult, GrayCodeMeasurementResult)
|
|
625
|
+
|
|
626
|
+
Example:
|
|
627
|
+
>>> compressor = QuantumCompressor(backend="simulator")
|
|
628
|
+
>>> result, gray_result = compressor.compress_with_gray_code([100, 200, 150, 175])
|
|
629
|
+
>>> print(f"Measurement settings used: {gray_result.n_measurement_settings}")
|
|
630
|
+
>>> print(f"Reconstructed amplitudes: {gray_result.amplitudes}")
|
|
631
|
+
"""
|
|
632
|
+
# First compress normally
|
|
633
|
+
result = self.compress(tokens, n_qubits, compression_level)
|
|
634
|
+
|
|
635
|
+
# Initialize Gray-code measurement
|
|
636
|
+
gray_measure = GrayCodeMeasurement(result.n_qubits)
|
|
637
|
+
|
|
638
|
+
# Execute Z-basis measurement
|
|
639
|
+
z_circuit = result.compressed_circuit.copy()
|
|
640
|
+
z_circuit.measure_all()
|
|
641
|
+
z_result = self.backend.execute(z_circuit, shots=shots)
|
|
642
|
+
|
|
643
|
+
# Execute XX measurements
|
|
644
|
+
xx_circuits = gray_measure.build_xx_measurement_circuits(result.compressed_circuit)
|
|
645
|
+
xx_results = [
|
|
646
|
+
self.backend.execute(qc, shots=shots).counts
|
|
647
|
+
for qc in xx_circuits
|
|
648
|
+
]
|
|
649
|
+
|
|
650
|
+
# Execute XY measurements
|
|
651
|
+
xy_circuits = gray_measure.build_xy_measurement_circuits(result.compressed_circuit)
|
|
652
|
+
xy_results = [
|
|
653
|
+
self.backend.execute(qc, shots=shots).counts
|
|
654
|
+
for qc in xy_circuits
|
|
655
|
+
]
|
|
656
|
+
|
|
657
|
+
# Full reconstruction
|
|
658
|
+
gray_result = gray_measure.full_reconstruction(
|
|
659
|
+
z_result.counts, xx_results, xy_results, shots
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
# Update result with execution info
|
|
663
|
+
result.execution_result = z_result
|
|
664
|
+
result.metadata["gray_code_measurement"] = True
|
|
665
|
+
result.metadata["measurement_settings"] = gray_result.n_measurement_settings
|
|
666
|
+
|
|
667
|
+
return result, gray_result
|
|
668
|
+
|
|
291
669
|
|
|
292
670
|
def compress_tokens(
|
|
293
671
|
tokens: list[int],
|