quantumflow-sdk 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.
- api/__init__.py +1 -0
- api/auth.py +208 -0
- api/main.py +403 -0
- api/models.py +137 -0
- api/routes/__init__.py +1 -0
- api/routes/auth_routes.py +234 -0
- api/routes/teleport_routes.py +415 -0
- db/__init__.py +15 -0
- db/crud.py +319 -0
- db/database.py +93 -0
- db/models.py +197 -0
- quantumflow/__init__.py +47 -0
- quantumflow/algorithms/__init__.py +48 -0
- quantumflow/algorithms/compression/__init__.py +7 -0
- quantumflow/algorithms/compression/amplitude_amplification.py +189 -0
- quantumflow/algorithms/compression/qft_compression.py +133 -0
- quantumflow/algorithms/compression/token_compression.py +261 -0
- quantumflow/algorithms/cryptography/__init__.py +6 -0
- quantumflow/algorithms/cryptography/qkd.py +205 -0
- quantumflow/algorithms/cryptography/qrng.py +231 -0
- quantumflow/algorithms/machine_learning/__init__.py +7 -0
- quantumflow/algorithms/machine_learning/qnn.py +276 -0
- quantumflow/algorithms/machine_learning/qsvm.py +249 -0
- quantumflow/algorithms/machine_learning/vqe.py +229 -0
- quantumflow/algorithms/optimization/__init__.py +7 -0
- quantumflow/algorithms/optimization/grover.py +223 -0
- quantumflow/algorithms/optimization/qaoa.py +251 -0
- quantumflow/algorithms/optimization/quantum_annealing.py +237 -0
- quantumflow/algorithms/utility/__init__.py +6 -0
- quantumflow/algorithms/utility/circuit_optimizer.py +194 -0
- quantumflow/algorithms/utility/error_correction.py +330 -0
- quantumflow/api/__init__.py +1 -0
- quantumflow/api/routes/__init__.py +4 -0
- quantumflow/api/routes/billing_routes.py +520 -0
- quantumflow/backends/__init__.py +33 -0
- quantumflow/backends/base_backend.py +184 -0
- quantumflow/backends/braket_backend.py +345 -0
- quantumflow/backends/ibm_backend.py +112 -0
- quantumflow/backends/simulator_backend.py +86 -0
- quantumflow/billing/__init__.py +25 -0
- quantumflow/billing/models.py +126 -0
- quantumflow/billing/stripe_service.py +619 -0
- quantumflow/core/__init__.py +12 -0
- quantumflow/core/entanglement.py +164 -0
- quantumflow/core/memory.py +147 -0
- quantumflow/core/quantum_backprop.py +394 -0
- quantumflow/core/quantum_compressor.py +309 -0
- quantumflow/core/teleportation.py +386 -0
- quantumflow/integrations/__init__.py +107 -0
- quantumflow/integrations/autogen_tools.py +501 -0
- quantumflow/integrations/crewai_agents.py +425 -0
- quantumflow/integrations/crewai_tools.py +407 -0
- quantumflow/integrations/langchain_memory.py +385 -0
- quantumflow/integrations/langchain_tools.py +366 -0
- quantumflow/integrations/mcp_server.py +575 -0
- quantumflow_sdk-0.1.0.dist-info/METADATA +190 -0
- quantumflow_sdk-0.1.0.dist-info/RECORD +60 -0
- quantumflow_sdk-0.1.0.dist-info/WHEEL +5 -0
- quantumflow_sdk-0.1.0.dist-info/entry_points.txt +2 -0
- quantumflow_sdk-0.1.0.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Amplitude Amplification Algorithm.
|
|
3
|
+
|
|
4
|
+
Generalization of Grover's algorithm for amplifying the probability
|
|
5
|
+
of marked states. Used in compression for selective retrieval.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import math
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Callable, Optional
|
|
11
|
+
import numpy as np
|
|
12
|
+
from qiskit import QuantumCircuit
|
|
13
|
+
|
|
14
|
+
from quantumflow.backends.base_backend import QuantumBackend, get_backend, BackendType
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class AmplificationResult:
|
|
19
|
+
"""Result from amplitude amplification."""
|
|
20
|
+
|
|
21
|
+
circuit: QuantumCircuit
|
|
22
|
+
n_qubits: int
|
|
23
|
+
iterations: int
|
|
24
|
+
target_probability: float
|
|
25
|
+
amplified_probability: float
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AmplitudeAmplification:
|
|
29
|
+
"""
|
|
30
|
+
Quantum Amplitude Amplification.
|
|
31
|
+
|
|
32
|
+
Amplifies the amplitude of marked states, increasing their
|
|
33
|
+
measurement probability from ~1/N to near certainty.
|
|
34
|
+
|
|
35
|
+
Applications:
|
|
36
|
+
- Selective token retrieval from compressed state
|
|
37
|
+
- Search optimization
|
|
38
|
+
- Probability boosting
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
>>> aa = AmplitudeAmplification()
|
|
42
|
+
>>> result = aa.amplify(initial_circuit, oracle, n_qubits=4)
|
|
43
|
+
>>> print(f"Amplified probability: {result.amplified_probability:.2%}")
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, backend: BackendType | str = BackendType.AUTO):
|
|
47
|
+
self.backend = get_backend(backend)
|
|
48
|
+
|
|
49
|
+
def amplify(
|
|
50
|
+
self,
|
|
51
|
+
initial_state: QuantumCircuit,
|
|
52
|
+
oracle: QuantumCircuit,
|
|
53
|
+
n_qubits: int,
|
|
54
|
+
iterations: Optional[int] = None,
|
|
55
|
+
target_count: int = 1,
|
|
56
|
+
) -> AmplificationResult:
|
|
57
|
+
"""
|
|
58
|
+
Apply amplitude amplification.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
initial_state: Circuit preparing initial superposition
|
|
62
|
+
oracle: Oracle marking target states (applies phase flip)
|
|
63
|
+
n_qubits: Number of qubits
|
|
64
|
+
iterations: Grover iterations (optimal if None)
|
|
65
|
+
target_count: Number of marked states
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
AmplificationResult with amplified circuit
|
|
69
|
+
"""
|
|
70
|
+
N = 2 ** n_qubits
|
|
71
|
+
|
|
72
|
+
# Calculate optimal iterations
|
|
73
|
+
if iterations is None:
|
|
74
|
+
if target_count > 0:
|
|
75
|
+
theta = math.asin(math.sqrt(target_count / N))
|
|
76
|
+
iterations = max(1, int(round(math.pi / (4 * theta) - 0.5)))
|
|
77
|
+
else:
|
|
78
|
+
iterations = 1
|
|
79
|
+
|
|
80
|
+
# Initial probability
|
|
81
|
+
initial_prob = target_count / N
|
|
82
|
+
|
|
83
|
+
# Build amplification circuit
|
|
84
|
+
circuit = self._build_circuit(
|
|
85
|
+
initial_state, oracle, n_qubits, iterations
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Calculate amplified probability
|
|
89
|
+
theta = math.asin(math.sqrt(target_count / N))
|
|
90
|
+
amplified_prob = math.sin((2 * iterations + 1) * theta) ** 2
|
|
91
|
+
|
|
92
|
+
return AmplificationResult(
|
|
93
|
+
circuit=circuit,
|
|
94
|
+
n_qubits=n_qubits,
|
|
95
|
+
iterations=iterations,
|
|
96
|
+
target_probability=initial_prob,
|
|
97
|
+
amplified_probability=amplified_prob,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def create_oracle(
|
|
101
|
+
self,
|
|
102
|
+
n_qubits: int,
|
|
103
|
+
marked_states: list[int],
|
|
104
|
+
) -> QuantumCircuit:
|
|
105
|
+
"""
|
|
106
|
+
Create an oracle that marks specified states.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
n_qubits: Number of qubits
|
|
110
|
+
marked_states: List of state indices to mark
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Oracle circuit applying phase flip to marked states
|
|
114
|
+
"""
|
|
115
|
+
oracle = QuantumCircuit(n_qubits, name="oracle")
|
|
116
|
+
|
|
117
|
+
for state in marked_states:
|
|
118
|
+
# Convert state to binary and apply X gates for 0s
|
|
119
|
+
binary = format(state, f'0{n_qubits}b')
|
|
120
|
+
|
|
121
|
+
# Apply X to qubits that should be |0⟩
|
|
122
|
+
for i, bit in enumerate(reversed(binary)):
|
|
123
|
+
if bit == '0':
|
|
124
|
+
oracle.x(i)
|
|
125
|
+
|
|
126
|
+
# Multi-controlled Z
|
|
127
|
+
if n_qubits == 1:
|
|
128
|
+
oracle.z(0)
|
|
129
|
+
elif n_qubits == 2:
|
|
130
|
+
oracle.cz(0, 1)
|
|
131
|
+
else:
|
|
132
|
+
oracle.h(n_qubits - 1)
|
|
133
|
+
oracle.mcx(list(range(n_qubits - 1)), n_qubits - 1)
|
|
134
|
+
oracle.h(n_qubits - 1)
|
|
135
|
+
|
|
136
|
+
# Undo X gates
|
|
137
|
+
for i, bit in enumerate(reversed(binary)):
|
|
138
|
+
if bit == '0':
|
|
139
|
+
oracle.x(i)
|
|
140
|
+
|
|
141
|
+
return oracle
|
|
142
|
+
|
|
143
|
+
def _build_circuit(
|
|
144
|
+
self,
|
|
145
|
+
initial_state: QuantumCircuit,
|
|
146
|
+
oracle: QuantumCircuit,
|
|
147
|
+
n_qubits: int,
|
|
148
|
+
iterations: int,
|
|
149
|
+
) -> QuantumCircuit:
|
|
150
|
+
"""Build the full amplitude amplification circuit."""
|
|
151
|
+
circuit = QuantumCircuit(n_qubits, name="amplitude_amp")
|
|
152
|
+
|
|
153
|
+
# Apply initial state preparation
|
|
154
|
+
circuit.compose(initial_state, inplace=True)
|
|
155
|
+
|
|
156
|
+
# Grover iterations
|
|
157
|
+
diffuser = self._create_diffuser(n_qubits)
|
|
158
|
+
|
|
159
|
+
for _ in range(iterations):
|
|
160
|
+
# Oracle
|
|
161
|
+
circuit.compose(oracle, inplace=True)
|
|
162
|
+
# Diffuser
|
|
163
|
+
circuit.compose(diffuser, inplace=True)
|
|
164
|
+
|
|
165
|
+
return circuit
|
|
166
|
+
|
|
167
|
+
def _create_diffuser(self, n_qubits: int) -> QuantumCircuit:
|
|
168
|
+
"""Create the Grover diffusion operator."""
|
|
169
|
+
diffuser = QuantumCircuit(n_qubits, name="diffuser")
|
|
170
|
+
|
|
171
|
+
# H gates
|
|
172
|
+
diffuser.h(range(n_qubits))
|
|
173
|
+
|
|
174
|
+
# X gates
|
|
175
|
+
diffuser.x(range(n_qubits))
|
|
176
|
+
|
|
177
|
+
# Multi-controlled Z
|
|
178
|
+
diffuser.h(n_qubits - 1)
|
|
179
|
+
if n_qubits > 1:
|
|
180
|
+
diffuser.mcx(list(range(n_qubits - 1)), n_qubits - 1)
|
|
181
|
+
diffuser.h(n_qubits - 1)
|
|
182
|
+
|
|
183
|
+
# X gates
|
|
184
|
+
diffuser.x(range(n_qubits))
|
|
185
|
+
|
|
186
|
+
# H gates
|
|
187
|
+
diffuser.h(range(n_qubits))
|
|
188
|
+
|
|
189
|
+
return diffuser
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Fourier Transform Compression.
|
|
3
|
+
|
|
4
|
+
Uses QFT for frequency-domain compression of token sequences,
|
|
5
|
+
similar to classical DCT/FFT compression but with quantum speedup.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import math
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Optional
|
|
11
|
+
import numpy as np
|
|
12
|
+
from qiskit import QuantumCircuit
|
|
13
|
+
from qiskit.circuit.library import QFT
|
|
14
|
+
|
|
15
|
+
from quantumflow.backends.base_backend import QuantumBackend, get_backend, BackendType
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class QFTResult:
|
|
20
|
+
"""Result from QFT compression."""
|
|
21
|
+
|
|
22
|
+
circuit: QuantumCircuit
|
|
23
|
+
n_qubits: int
|
|
24
|
+
kept_coefficients: int
|
|
25
|
+
total_coefficients: int
|
|
26
|
+
compression_ratio: float
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class QFTCompression:
|
|
30
|
+
"""
|
|
31
|
+
Quantum Fourier Transform based compression.
|
|
32
|
+
|
|
33
|
+
Compresses data by:
|
|
34
|
+
1. Encoding data as quantum amplitudes
|
|
35
|
+
2. Applying QFT to transform to frequency domain
|
|
36
|
+
3. Truncating high-frequency components
|
|
37
|
+
4. Storing only significant coefficients
|
|
38
|
+
|
|
39
|
+
Best for: Periodic or smoothly varying data.
|
|
40
|
+
|
|
41
|
+
Example:
|
|
42
|
+
>>> qft = QFTCompression()
|
|
43
|
+
>>> result = qft.compress([100, 110, 105, 115, 100, 108])
|
|
44
|
+
>>> print(f"Kept {result.kept_coefficients}/{result.total_coefficients}")
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
backend: BackendType | str = BackendType.AUTO,
|
|
50
|
+
keep_ratio: float = 0.5,
|
|
51
|
+
):
|
|
52
|
+
"""
|
|
53
|
+
Initialize QFT compression.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
backend: Quantum backend to use
|
|
57
|
+
keep_ratio: Fraction of frequency components to keep (0.0-1.0)
|
|
58
|
+
"""
|
|
59
|
+
self.backend = get_backend(backend)
|
|
60
|
+
self.keep_ratio = keep_ratio
|
|
61
|
+
|
|
62
|
+
def compress(
|
|
63
|
+
self,
|
|
64
|
+
data: list[float],
|
|
65
|
+
n_qubits: Optional[int] = None,
|
|
66
|
+
) -> QFTResult:
|
|
67
|
+
"""
|
|
68
|
+
Compress data using QFT.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
data: Input data to compress
|
|
72
|
+
n_qubits: Number of qubits (auto if None)
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
QFTResult with circuit and metadata
|
|
76
|
+
"""
|
|
77
|
+
if not data:
|
|
78
|
+
raise ValueError("Cannot compress empty data")
|
|
79
|
+
|
|
80
|
+
# Calculate qubits needed
|
|
81
|
+
if n_qubits is None:
|
|
82
|
+
n_qubits = max(2, math.ceil(math.log2(len(data))))
|
|
83
|
+
|
|
84
|
+
target_size = 2 ** n_qubits
|
|
85
|
+
|
|
86
|
+
# Normalize data to amplitudes
|
|
87
|
+
data_arr = np.array(data, dtype=float)
|
|
88
|
+
data_arr = np.pad(data_arr, (0, max(0, target_size - len(data_arr))))
|
|
89
|
+
data_arr = data_arr[:target_size]
|
|
90
|
+
|
|
91
|
+
norm = np.linalg.norm(data_arr)
|
|
92
|
+
if norm > 0:
|
|
93
|
+
amplitudes = data_arr / norm
|
|
94
|
+
else:
|
|
95
|
+
amplitudes = np.ones(target_size) / np.sqrt(target_size)
|
|
96
|
+
|
|
97
|
+
# Build circuit
|
|
98
|
+
circuit = self._build_qft_circuit(amplitudes, n_qubits)
|
|
99
|
+
|
|
100
|
+
kept = int(target_size * self.keep_ratio)
|
|
101
|
+
ratio = target_size / max(kept, 1)
|
|
102
|
+
|
|
103
|
+
return QFTResult(
|
|
104
|
+
circuit=circuit,
|
|
105
|
+
n_qubits=n_qubits,
|
|
106
|
+
kept_coefficients=kept,
|
|
107
|
+
total_coefficients=target_size,
|
|
108
|
+
compression_ratio=ratio,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def _build_qft_circuit(
|
|
112
|
+
self,
|
|
113
|
+
amplitudes: np.ndarray,
|
|
114
|
+
n_qubits: int,
|
|
115
|
+
) -> QuantumCircuit:
|
|
116
|
+
"""Build QFT compression circuit."""
|
|
117
|
+
qc = QuantumCircuit(n_qubits, name="qft_compress")
|
|
118
|
+
|
|
119
|
+
# Initialize with data
|
|
120
|
+
qc.initialize(amplitudes, range(n_qubits))
|
|
121
|
+
|
|
122
|
+
# Apply QFT
|
|
123
|
+
qft = QFT(n_qubits, do_swaps=True)
|
|
124
|
+
qc.append(qft, range(n_qubits))
|
|
125
|
+
|
|
126
|
+
return qc
|
|
127
|
+
|
|
128
|
+
def inverse_qft_circuit(self, n_qubits: int) -> QuantumCircuit:
|
|
129
|
+
"""Get inverse QFT circuit for decompression."""
|
|
130
|
+
qc = QuantumCircuit(n_qubits, name="iqft")
|
|
131
|
+
qft_inv = QFT(n_qubits, do_swaps=True, inverse=True)
|
|
132
|
+
qc.append(qft_inv, range(n_qubits))
|
|
133
|
+
return qc
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Token Compression Algorithm.
|
|
3
|
+
|
|
4
|
+
Enhanced version of Paper 1 compression with additional features:
|
|
5
|
+
- Adaptive qubit allocation
|
|
6
|
+
- Batch compression
|
|
7
|
+
- Streaming support
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import math
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Optional, Iterator
|
|
13
|
+
import numpy as np
|
|
14
|
+
from qiskit import QuantumCircuit
|
|
15
|
+
|
|
16
|
+
from quantumflow.backends.base_backend import QuantumBackend, get_backend, BackendType
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class CompressionConfig:
|
|
21
|
+
"""Configuration for token compression."""
|
|
22
|
+
|
|
23
|
+
min_compression_ratio: float = 1.5
|
|
24
|
+
max_qubits: int = 20
|
|
25
|
+
adaptive: bool = True
|
|
26
|
+
error_mitigation: bool = False
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class CompressedBatch:
|
|
31
|
+
"""A batch of compressed tokens."""
|
|
32
|
+
|
|
33
|
+
circuit: QuantumCircuit
|
|
34
|
+
original_size: int
|
|
35
|
+
compressed_size: int
|
|
36
|
+
batch_index: int
|
|
37
|
+
amplitudes: np.ndarray
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TokenCompression:
|
|
41
|
+
"""
|
|
42
|
+
Advanced token compression using quantum amplitude encoding.
|
|
43
|
+
|
|
44
|
+
Features:
|
|
45
|
+
- Adaptive qubit allocation based on token distribution
|
|
46
|
+
- Batch processing for large token sequences
|
|
47
|
+
- Streaming compression for real-time applications
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> tc = TokenCompression()
|
|
51
|
+
>>> result = tc.compress([100, 200, 300, 400])
|
|
52
|
+
>>> print(f"Ratio: {result.compression_ratio}x")
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
backend: BackendType | str = BackendType.AUTO,
|
|
58
|
+
config: Optional[CompressionConfig] = None,
|
|
59
|
+
):
|
|
60
|
+
self.backend = get_backend(backend)
|
|
61
|
+
self.config = config or CompressionConfig()
|
|
62
|
+
self._connected = False
|
|
63
|
+
|
|
64
|
+
def _ensure_connected(self):
|
|
65
|
+
if not self._connected:
|
|
66
|
+
self.backend.connect()
|
|
67
|
+
self._connected = True
|
|
68
|
+
|
|
69
|
+
def compress(
|
|
70
|
+
self,
|
|
71
|
+
tokens: list[int],
|
|
72
|
+
n_qubits: Optional[int] = None,
|
|
73
|
+
) -> CompressedBatch:
|
|
74
|
+
"""
|
|
75
|
+
Compress a list of tokens.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
tokens: Integer tokens to compress
|
|
79
|
+
n_qubits: Override automatic qubit calculation
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
CompressedBatch with circuit and metadata
|
|
83
|
+
"""
|
|
84
|
+
if not tokens:
|
|
85
|
+
raise ValueError("Cannot compress empty token list")
|
|
86
|
+
|
|
87
|
+
# Calculate optimal qubits
|
|
88
|
+
if n_qubits is None:
|
|
89
|
+
n_qubits = self._calculate_qubits(tokens)
|
|
90
|
+
|
|
91
|
+
n_qubits = min(n_qubits, self.config.max_qubits)
|
|
92
|
+
|
|
93
|
+
# Normalize to amplitudes
|
|
94
|
+
amplitudes = self._tokens_to_amplitudes(tokens, n_qubits)
|
|
95
|
+
|
|
96
|
+
# Build circuit
|
|
97
|
+
circuit = self._build_circuit(amplitudes, n_qubits)
|
|
98
|
+
|
|
99
|
+
return CompressedBatch(
|
|
100
|
+
circuit=circuit,
|
|
101
|
+
original_size=len(tokens),
|
|
102
|
+
compressed_size=n_qubits,
|
|
103
|
+
batch_index=0,
|
|
104
|
+
amplitudes=amplitudes,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def compress_batch(
|
|
108
|
+
self,
|
|
109
|
+
tokens: list[int],
|
|
110
|
+
batch_size: int = 256,
|
|
111
|
+
) -> list[CompressedBatch]:
|
|
112
|
+
"""
|
|
113
|
+
Compress tokens in batches.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
tokens: All tokens to compress
|
|
117
|
+
batch_size: Max tokens per batch (must be power of 2)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
List of CompressedBatch objects
|
|
121
|
+
"""
|
|
122
|
+
# Ensure batch_size is power of 2
|
|
123
|
+
batch_size = 2 ** int(math.log2(batch_size))
|
|
124
|
+
|
|
125
|
+
batches = []
|
|
126
|
+
for i in range(0, len(tokens), batch_size):
|
|
127
|
+
batch_tokens = tokens[i:i + batch_size]
|
|
128
|
+
batch = self.compress(batch_tokens)
|
|
129
|
+
batch.batch_index = i // batch_size
|
|
130
|
+
batches.append(batch)
|
|
131
|
+
|
|
132
|
+
return batches
|
|
133
|
+
|
|
134
|
+
def compress_stream(
|
|
135
|
+
self,
|
|
136
|
+
token_iterator: Iterator[int],
|
|
137
|
+
buffer_size: int = 64,
|
|
138
|
+
) -> Iterator[CompressedBatch]:
|
|
139
|
+
"""
|
|
140
|
+
Stream compress tokens as they arrive.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
token_iterator: Iterator yielding tokens
|
|
144
|
+
buffer_size: Tokens to buffer before compression
|
|
145
|
+
|
|
146
|
+
Yields:
|
|
147
|
+
CompressedBatch objects
|
|
148
|
+
"""
|
|
149
|
+
buffer = []
|
|
150
|
+
batch_idx = 0
|
|
151
|
+
|
|
152
|
+
for token in token_iterator:
|
|
153
|
+
buffer.append(token)
|
|
154
|
+
|
|
155
|
+
if len(buffer) >= buffer_size:
|
|
156
|
+
batch = self.compress(buffer)
|
|
157
|
+
batch.batch_index = batch_idx
|
|
158
|
+
yield batch
|
|
159
|
+
buffer = []
|
|
160
|
+
batch_idx += 1
|
|
161
|
+
|
|
162
|
+
# Final batch
|
|
163
|
+
if buffer:
|
|
164
|
+
batch = self.compress(buffer)
|
|
165
|
+
batch.batch_index = batch_idx
|
|
166
|
+
yield batch
|
|
167
|
+
|
|
168
|
+
def decompress(
|
|
169
|
+
self,
|
|
170
|
+
batch: CompressedBatch,
|
|
171
|
+
shots: int = 1024,
|
|
172
|
+
) -> list[int]:
|
|
173
|
+
"""
|
|
174
|
+
Decompress a batch back to tokens.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
batch: CompressedBatch to decompress
|
|
178
|
+
shots: Measurement shots for reconstruction
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Reconstructed token list
|
|
182
|
+
"""
|
|
183
|
+
self._ensure_connected()
|
|
184
|
+
|
|
185
|
+
result = self.backend.execute(batch.circuit, shots=shots)
|
|
186
|
+
|
|
187
|
+
# Reconstruct from statevector if available
|
|
188
|
+
if result.statevector is not None:
|
|
189
|
+
probs = np.abs(result.statevector) ** 2
|
|
190
|
+
else:
|
|
191
|
+
# Reconstruct from measurement counts
|
|
192
|
+
probs = np.zeros(2 ** batch.compressed_size)
|
|
193
|
+
for state, count in result.counts.items():
|
|
194
|
+
idx = int(state, 2)
|
|
195
|
+
probs[idx] = count / shots
|
|
196
|
+
|
|
197
|
+
# Scale back to token range
|
|
198
|
+
# This is approximate - exact reconstruction requires statevector
|
|
199
|
+
max_prob = np.max(probs)
|
|
200
|
+
if max_prob > 0:
|
|
201
|
+
tokens = (probs[:batch.original_size] / max_prob * 255).astype(int)
|
|
202
|
+
else:
|
|
203
|
+
tokens = np.zeros(batch.original_size, dtype=int)
|
|
204
|
+
|
|
205
|
+
return tokens.tolist()
|
|
206
|
+
|
|
207
|
+
def _calculate_qubits(self, tokens: list[int]) -> int:
|
|
208
|
+
"""Calculate optimal qubit count based on token distribution."""
|
|
209
|
+
n = len(tokens)
|
|
210
|
+
base_qubits = max(1, math.ceil(math.log2(n)))
|
|
211
|
+
|
|
212
|
+
if self.config.adaptive:
|
|
213
|
+
# Analyze token distribution
|
|
214
|
+
tokens_arr = np.array(tokens)
|
|
215
|
+
variance = np.var(tokens_arr)
|
|
216
|
+
|
|
217
|
+
# High variance = need more precision = more qubits
|
|
218
|
+
if variance > 1000:
|
|
219
|
+
base_qubits += 1
|
|
220
|
+
|
|
221
|
+
return base_qubits
|
|
222
|
+
|
|
223
|
+
def _tokens_to_amplitudes(
|
|
224
|
+
self,
|
|
225
|
+
tokens: list[int],
|
|
226
|
+
n_qubits: int,
|
|
227
|
+
) -> np.ndarray:
|
|
228
|
+
"""Convert tokens to normalized quantum amplitudes."""
|
|
229
|
+
target_size = 2 ** n_qubits
|
|
230
|
+
|
|
231
|
+
tokens_arr = np.array(tokens, dtype=float)
|
|
232
|
+
tokens_arr = np.maximum(tokens_arr, 1e-10)
|
|
233
|
+
|
|
234
|
+
# Pad to target size
|
|
235
|
+
if len(tokens_arr) < target_size:
|
|
236
|
+
tokens_arr = np.pad(
|
|
237
|
+
tokens_arr,
|
|
238
|
+
(0, target_size - len(tokens_arr)),
|
|
239
|
+
constant_values=1e-10
|
|
240
|
+
)
|
|
241
|
+
else:
|
|
242
|
+
tokens_arr = tokens_arr[:target_size]
|
|
243
|
+
|
|
244
|
+
# Normalize
|
|
245
|
+
norm = np.linalg.norm(tokens_arr)
|
|
246
|
+
return tokens_arr / norm if norm > 0 else tokens_arr
|
|
247
|
+
|
|
248
|
+
def _build_circuit(
|
|
249
|
+
self,
|
|
250
|
+
amplitudes: np.ndarray,
|
|
251
|
+
n_qubits: int,
|
|
252
|
+
) -> QuantumCircuit:
|
|
253
|
+
"""Build quantum circuit for amplitude encoding."""
|
|
254
|
+
qc = QuantumCircuit(n_qubits, name="token_compress")
|
|
255
|
+
qc.initialize(amplitudes, range(n_qubits))
|
|
256
|
+
return qc
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def compression_ratio(self) -> float:
|
|
260
|
+
"""Expected compression ratio based on config."""
|
|
261
|
+
return self.config.min_compression_ratio
|