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,251 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Approximate Optimization Algorithm (QAOA).
|
|
3
|
+
|
|
4
|
+
Solves combinatorial optimization problems like MaxCut, TSP, etc.
|
|
5
|
+
Hybrid quantum-classical algorithm using parameterized circuits.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import math
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Optional, Callable
|
|
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 QAOAResult:
|
|
19
|
+
"""Result from QAOA optimization."""
|
|
20
|
+
|
|
21
|
+
circuit: QuantumCircuit
|
|
22
|
+
optimal_params: np.ndarray
|
|
23
|
+
best_solution: str
|
|
24
|
+
best_cost: float
|
|
25
|
+
cost_history: list[float]
|
|
26
|
+
n_iterations: int
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class QAOA:
|
|
30
|
+
"""
|
|
31
|
+
Quantum Approximate Optimization Algorithm.
|
|
32
|
+
|
|
33
|
+
Solves combinatorial optimization by:
|
|
34
|
+
1. Encoding problem as cost Hamiltonian
|
|
35
|
+
2. Alternating cost and mixer layers
|
|
36
|
+
3. Classical optimization of parameters
|
|
37
|
+
|
|
38
|
+
Supported problems:
|
|
39
|
+
- MaxCut
|
|
40
|
+
- Weighted MaxCut
|
|
41
|
+
- Custom QUBO/Ising problems
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> qaoa = QAOA(p=2)
|
|
45
|
+
>>> edges = [(0, 1), (1, 2), (2, 0)]
|
|
46
|
+
>>> result = qaoa.maxcut(edges, n_nodes=3)
|
|
47
|
+
>>> print(f"Best cut: {result.best_solution}, Cost: {result.best_cost}")
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
backend: BackendType | str = BackendType.AUTO,
|
|
53
|
+
p: int = 1,
|
|
54
|
+
shots: int = 1024,
|
|
55
|
+
):
|
|
56
|
+
"""
|
|
57
|
+
Initialize QAOA.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
backend: Quantum backend
|
|
61
|
+
p: Number of QAOA layers (depth)
|
|
62
|
+
shots: Measurement shots per evaluation
|
|
63
|
+
"""
|
|
64
|
+
self.backend = get_backend(backend)
|
|
65
|
+
self.p = p
|
|
66
|
+
self.shots = shots
|
|
67
|
+
self._connected = False
|
|
68
|
+
|
|
69
|
+
def _ensure_connected(self):
|
|
70
|
+
if not self._connected:
|
|
71
|
+
self.backend.connect()
|
|
72
|
+
self._connected = True
|
|
73
|
+
|
|
74
|
+
def maxcut(
|
|
75
|
+
self,
|
|
76
|
+
edges: list[tuple[int, int]],
|
|
77
|
+
n_nodes: int,
|
|
78
|
+
weights: Optional[list[float]] = None,
|
|
79
|
+
max_iterations: int = 100,
|
|
80
|
+
) -> QAOAResult:
|
|
81
|
+
"""
|
|
82
|
+
Solve MaxCut problem.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
edges: List of (i, j) edges
|
|
86
|
+
n_nodes: Number of nodes
|
|
87
|
+
weights: Optional edge weights
|
|
88
|
+
max_iterations: Max optimization iterations
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
QAOAResult with optimal solution
|
|
92
|
+
"""
|
|
93
|
+
if weights is None:
|
|
94
|
+
weights = [1.0] * len(edges)
|
|
95
|
+
|
|
96
|
+
# Build cost function
|
|
97
|
+
def cost_fn(bitstring: str) -> float:
|
|
98
|
+
cost = 0
|
|
99
|
+
for (i, j), w in zip(edges, weights):
|
|
100
|
+
if bitstring[i] != bitstring[j]:
|
|
101
|
+
cost += w
|
|
102
|
+
return cost
|
|
103
|
+
|
|
104
|
+
return self._optimize(
|
|
105
|
+
n_qubits=n_nodes,
|
|
106
|
+
cost_hamiltonian=self._maxcut_hamiltonian(edges, weights, n_nodes),
|
|
107
|
+
cost_fn=cost_fn,
|
|
108
|
+
max_iterations=max_iterations,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def optimize_qubo(
|
|
112
|
+
self,
|
|
113
|
+
Q: np.ndarray,
|
|
114
|
+
max_iterations: int = 100,
|
|
115
|
+
) -> QAOAResult:
|
|
116
|
+
"""
|
|
117
|
+
Solve QUBO (Quadratic Unconstrained Binary Optimization).
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
Q: QUBO matrix (n x n)
|
|
121
|
+
max_iterations: Max optimization iterations
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
QAOAResult with optimal solution
|
|
125
|
+
"""
|
|
126
|
+
n_qubits = Q.shape[0]
|
|
127
|
+
|
|
128
|
+
def cost_fn(bitstring: str) -> float:
|
|
129
|
+
x = np.array([int(b) for b in bitstring])
|
|
130
|
+
return float(x @ Q @ x)
|
|
131
|
+
|
|
132
|
+
return self._optimize(
|
|
133
|
+
n_qubits=n_qubits,
|
|
134
|
+
cost_hamiltonian=self._qubo_hamiltonian(Q),
|
|
135
|
+
cost_fn=cost_fn,
|
|
136
|
+
max_iterations=max_iterations,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
def _optimize(
|
|
140
|
+
self,
|
|
141
|
+
n_qubits: int,
|
|
142
|
+
cost_hamiltonian: Callable[[QuantumCircuit, float], None],
|
|
143
|
+
cost_fn: Callable[[str], float],
|
|
144
|
+
max_iterations: int,
|
|
145
|
+
) -> QAOAResult:
|
|
146
|
+
"""Run QAOA optimization loop."""
|
|
147
|
+
self._ensure_connected()
|
|
148
|
+
|
|
149
|
+
# Initialize parameters: gamma (cost), beta (mixer)
|
|
150
|
+
params = np.random.uniform(0, np.pi, 2 * self.p)
|
|
151
|
+
cost_history = []
|
|
152
|
+
|
|
153
|
+
# Simple gradient-free optimization (COBYLA-style)
|
|
154
|
+
best_params = params.copy()
|
|
155
|
+
best_cost = float('inf')
|
|
156
|
+
|
|
157
|
+
for iteration in range(max_iterations):
|
|
158
|
+
# Evaluate current parameters
|
|
159
|
+
circuit = self._build_circuit(n_qubits, params, cost_hamiltonian)
|
|
160
|
+
result = self.backend.execute(circuit, shots=self.shots)
|
|
161
|
+
|
|
162
|
+
# Calculate expected cost
|
|
163
|
+
total_cost = 0
|
|
164
|
+
for bitstring, count in result.counts.items():
|
|
165
|
+
total_cost += cost_fn(bitstring) * count
|
|
166
|
+
avg_cost = total_cost / self.shots
|
|
167
|
+
|
|
168
|
+
cost_history.append(avg_cost)
|
|
169
|
+
|
|
170
|
+
if avg_cost < best_cost:
|
|
171
|
+
best_cost = avg_cost
|
|
172
|
+
best_params = params.copy()
|
|
173
|
+
|
|
174
|
+
# Simple parameter update (gradient approximation)
|
|
175
|
+
for i in range(len(params)):
|
|
176
|
+
delta = 0.1 * (np.random.random() - 0.5)
|
|
177
|
+
params[i] += delta
|
|
178
|
+
|
|
179
|
+
# Get best solution
|
|
180
|
+
final_circuit = self._build_circuit(n_qubits, best_params, cost_hamiltonian)
|
|
181
|
+
final_result = self.backend.execute(final_circuit, shots=self.shots)
|
|
182
|
+
best_solution = max(final_result.counts, key=final_result.counts.get)
|
|
183
|
+
|
|
184
|
+
return QAOAResult(
|
|
185
|
+
circuit=final_circuit,
|
|
186
|
+
optimal_params=best_params,
|
|
187
|
+
best_solution=best_solution,
|
|
188
|
+
best_cost=best_cost,
|
|
189
|
+
cost_history=cost_history,
|
|
190
|
+
n_iterations=max_iterations,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def _build_circuit(
|
|
194
|
+
self,
|
|
195
|
+
n_qubits: int,
|
|
196
|
+
params: np.ndarray,
|
|
197
|
+
cost_hamiltonian: Callable,
|
|
198
|
+
) -> QuantumCircuit:
|
|
199
|
+
"""Build QAOA circuit with given parameters."""
|
|
200
|
+
qc = QuantumCircuit(n_qubits, n_qubits, name="qaoa")
|
|
201
|
+
|
|
202
|
+
# Initial superposition
|
|
203
|
+
qc.h(range(n_qubits))
|
|
204
|
+
|
|
205
|
+
# QAOA layers
|
|
206
|
+
for layer in range(self.p):
|
|
207
|
+
gamma = params[2 * layer]
|
|
208
|
+
beta = params[2 * layer + 1]
|
|
209
|
+
|
|
210
|
+
# Cost layer
|
|
211
|
+
cost_hamiltonian(qc, gamma)
|
|
212
|
+
|
|
213
|
+
# Mixer layer (X rotations)
|
|
214
|
+
for i in range(n_qubits):
|
|
215
|
+
qc.rx(2 * beta, i)
|
|
216
|
+
|
|
217
|
+
# Measure
|
|
218
|
+
qc.measure(range(n_qubits), range(n_qubits))
|
|
219
|
+
|
|
220
|
+
return qc
|
|
221
|
+
|
|
222
|
+
def _maxcut_hamiltonian(
|
|
223
|
+
self,
|
|
224
|
+
edges: list[tuple[int, int]],
|
|
225
|
+
weights: list[float],
|
|
226
|
+
n_qubits: int,
|
|
227
|
+
) -> Callable:
|
|
228
|
+
"""Create MaxCut cost Hamiltonian."""
|
|
229
|
+
def apply_cost(qc: QuantumCircuit, gamma: float):
|
|
230
|
+
for (i, j), w in zip(edges, weights):
|
|
231
|
+
qc.rzz(w * gamma, i, j)
|
|
232
|
+
return apply_cost
|
|
233
|
+
|
|
234
|
+
def _qubo_hamiltonian(self, Q: np.ndarray) -> Callable:
|
|
235
|
+
"""Create QUBO cost Hamiltonian."""
|
|
236
|
+
n = Q.shape[0]
|
|
237
|
+
|
|
238
|
+
def apply_cost(qc: QuantumCircuit, gamma: float):
|
|
239
|
+
# Diagonal terms
|
|
240
|
+
for i in range(n):
|
|
241
|
+
if Q[i, i] != 0:
|
|
242
|
+
qc.rz(Q[i, i] * gamma, i)
|
|
243
|
+
|
|
244
|
+
# Off-diagonal terms
|
|
245
|
+
for i in range(n):
|
|
246
|
+
for j in range(i + 1, n):
|
|
247
|
+
if Q[i, j] != 0 or Q[j, i] != 0:
|
|
248
|
+
coeff = Q[i, j] + Q[j, i]
|
|
249
|
+
qc.rzz(coeff * gamma / 2, i, j)
|
|
250
|
+
|
|
251
|
+
return apply_cost
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Annealing Simulation.
|
|
3
|
+
|
|
4
|
+
Simulates quantum annealing for optimization problems.
|
|
5
|
+
Note: True quantum annealing requires specialized hardware (D-Wave).
|
|
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
|
+
|
|
14
|
+
from quantumflow.backends.base_backend import QuantumBackend, get_backend, BackendType
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class AnnealingResult:
|
|
19
|
+
"""Result from quantum annealing."""
|
|
20
|
+
|
|
21
|
+
circuit: QuantumCircuit
|
|
22
|
+
best_solution: str
|
|
23
|
+
best_energy: float
|
|
24
|
+
energy_history: list[float]
|
|
25
|
+
n_steps: int
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class QuantumAnnealing:
|
|
29
|
+
"""
|
|
30
|
+
Quantum Annealing Simulation using gate-based circuits.
|
|
31
|
+
|
|
32
|
+
Simulates the annealing process by:
|
|
33
|
+
1. Starting in ground state of transverse field
|
|
34
|
+
2. Slowly evolving to problem Hamiltonian
|
|
35
|
+
3. Measuring final state
|
|
36
|
+
|
|
37
|
+
Best for: Optimization problems expressible as Ising models.
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
>>> qa = QuantumAnnealing()
|
|
41
|
+
>>> J = {(0,1): -1, (1,2): -1} # Ferromagnetic coupling
|
|
42
|
+
>>> result = qa.anneal(J, h={}, n_qubits=3)
|
|
43
|
+
>>> print(f"Ground state: {result.best_solution}")
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
backend: BackendType | str = BackendType.AUTO,
|
|
49
|
+
n_steps: int = 20,
|
|
50
|
+
):
|
|
51
|
+
"""
|
|
52
|
+
Initialize quantum annealing.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
backend: Quantum backend
|
|
56
|
+
n_steps: Number of annealing steps (Trotter steps)
|
|
57
|
+
"""
|
|
58
|
+
self.backend = get_backend(backend)
|
|
59
|
+
self.n_steps = n_steps
|
|
60
|
+
self._connected = False
|
|
61
|
+
|
|
62
|
+
def _ensure_connected(self):
|
|
63
|
+
if not self._connected:
|
|
64
|
+
self.backend.connect()
|
|
65
|
+
self._connected = True
|
|
66
|
+
|
|
67
|
+
def anneal(
|
|
68
|
+
self,
|
|
69
|
+
J: dict[tuple[int, int], float],
|
|
70
|
+
h: dict[int, float],
|
|
71
|
+
n_qubits: int,
|
|
72
|
+
shots: int = 1024,
|
|
73
|
+
) -> AnnealingResult:
|
|
74
|
+
"""
|
|
75
|
+
Run quantum annealing for Ising problem.
|
|
76
|
+
|
|
77
|
+
Hamiltonian: H = -sum_ij J_ij * Z_i * Z_j - sum_i h_i * Z_i
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
J: Coupling strengths {(i,j): strength}
|
|
81
|
+
h: Local fields {i: field}
|
|
82
|
+
n_qubits: Number of qubits
|
|
83
|
+
shots: Measurement shots
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
AnnealingResult with ground state
|
|
87
|
+
"""
|
|
88
|
+
self._ensure_connected()
|
|
89
|
+
|
|
90
|
+
# Build annealing circuit
|
|
91
|
+
circuit = self._build_annealing_circuit(J, h, n_qubits)
|
|
92
|
+
|
|
93
|
+
# Execute
|
|
94
|
+
result = self.backend.execute(circuit, shots=shots)
|
|
95
|
+
|
|
96
|
+
# Find ground state (lowest energy)
|
|
97
|
+
best_solution = None
|
|
98
|
+
best_energy = float('inf')
|
|
99
|
+
|
|
100
|
+
for bitstring, count in result.counts.items():
|
|
101
|
+
energy = self._calculate_energy(bitstring, J, h)
|
|
102
|
+
if energy < best_energy:
|
|
103
|
+
best_energy = energy
|
|
104
|
+
best_solution = bitstring
|
|
105
|
+
|
|
106
|
+
# Simulate energy history (for visualization)
|
|
107
|
+
energy_history = self._simulate_energy_history(best_energy)
|
|
108
|
+
|
|
109
|
+
return AnnealingResult(
|
|
110
|
+
circuit=circuit,
|
|
111
|
+
best_solution=best_solution or "0" * n_qubits,
|
|
112
|
+
best_energy=best_energy,
|
|
113
|
+
energy_history=energy_history,
|
|
114
|
+
n_steps=self.n_steps,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def anneal_qubo(
|
|
118
|
+
self,
|
|
119
|
+
Q: np.ndarray,
|
|
120
|
+
shots: int = 1024,
|
|
121
|
+
) -> AnnealingResult:
|
|
122
|
+
"""
|
|
123
|
+
Run quantum annealing for QUBO problem.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
Q: QUBO matrix
|
|
127
|
+
shots: Measurement shots
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
AnnealingResult
|
|
131
|
+
"""
|
|
132
|
+
n_qubits = Q.shape[0]
|
|
133
|
+
|
|
134
|
+
# Convert QUBO to Ising
|
|
135
|
+
J, h, offset = self._qubo_to_ising(Q)
|
|
136
|
+
|
|
137
|
+
result = self.anneal(J, h, n_qubits, shots)
|
|
138
|
+
result.best_energy += offset
|
|
139
|
+
|
|
140
|
+
return result
|
|
141
|
+
|
|
142
|
+
def _build_annealing_circuit(
|
|
143
|
+
self,
|
|
144
|
+
J: dict[tuple[int, int], float],
|
|
145
|
+
h: dict[int, float],
|
|
146
|
+
n_qubits: int,
|
|
147
|
+
) -> QuantumCircuit:
|
|
148
|
+
"""Build circuit simulating quantum annealing."""
|
|
149
|
+
circuit = QuantumCircuit(n_qubits, n_qubits, name="annealing")
|
|
150
|
+
|
|
151
|
+
# Start in |+⟩^n (ground state of transverse field)
|
|
152
|
+
circuit.h(range(n_qubits))
|
|
153
|
+
|
|
154
|
+
# Annealing schedule: s goes from 0 to 1
|
|
155
|
+
dt = 1.0 / self.n_steps
|
|
156
|
+
|
|
157
|
+
for step in range(self.n_steps):
|
|
158
|
+
s = (step + 1) / self.n_steps # Annealing parameter
|
|
159
|
+
|
|
160
|
+
# Transverse field (decreasing)
|
|
161
|
+
gamma = (1 - s) * math.pi / 4
|
|
162
|
+
for i in range(n_qubits):
|
|
163
|
+
circuit.rx(2 * gamma * dt, i)
|
|
164
|
+
|
|
165
|
+
# Problem Hamiltonian (increasing)
|
|
166
|
+
beta = s * math.pi / 4
|
|
167
|
+
|
|
168
|
+
# ZZ interactions
|
|
169
|
+
for (i, j), strength in J.items():
|
|
170
|
+
circuit.rzz(2 * strength * beta * dt, i, j)
|
|
171
|
+
|
|
172
|
+
# Z terms
|
|
173
|
+
for i, field in h.items():
|
|
174
|
+
circuit.rz(2 * field * beta * dt, i)
|
|
175
|
+
|
|
176
|
+
# Measure
|
|
177
|
+
circuit.measure(range(n_qubits), range(n_qubits))
|
|
178
|
+
|
|
179
|
+
return circuit
|
|
180
|
+
|
|
181
|
+
def _calculate_energy(
|
|
182
|
+
self,
|
|
183
|
+
bitstring: str,
|
|
184
|
+
J: dict[tuple[int, int], float],
|
|
185
|
+
h: dict[int, float],
|
|
186
|
+
) -> float:
|
|
187
|
+
"""Calculate Ising energy for a bitstring."""
|
|
188
|
+
# Convert to spins (+1/-1)
|
|
189
|
+
spins = [1 if b == '0' else -1 for b in bitstring]
|
|
190
|
+
|
|
191
|
+
energy = 0.0
|
|
192
|
+
|
|
193
|
+
# Coupling terms
|
|
194
|
+
for (i, j), strength in J.items():
|
|
195
|
+
energy -= strength * spins[i] * spins[j]
|
|
196
|
+
|
|
197
|
+
# Field terms
|
|
198
|
+
for i, field in h.items():
|
|
199
|
+
energy -= field * spins[i]
|
|
200
|
+
|
|
201
|
+
return energy
|
|
202
|
+
|
|
203
|
+
def _qubo_to_ising(
|
|
204
|
+
self,
|
|
205
|
+
Q: np.ndarray,
|
|
206
|
+
) -> tuple[dict, dict, float]:
|
|
207
|
+
"""Convert QUBO to Ising formulation."""
|
|
208
|
+
n = Q.shape[0]
|
|
209
|
+
J = {}
|
|
210
|
+
h = {}
|
|
211
|
+
offset = 0.0
|
|
212
|
+
|
|
213
|
+
for i in range(n):
|
|
214
|
+
for j in range(i, n):
|
|
215
|
+
if i == j:
|
|
216
|
+
h[i] = Q[i, i] / 2
|
|
217
|
+
offset += Q[i, i] / 2
|
|
218
|
+
else:
|
|
219
|
+
coupling = (Q[i, j] + Q[j, i]) / 4
|
|
220
|
+
if coupling != 0:
|
|
221
|
+
J[(i, j)] = coupling
|
|
222
|
+
h[i] = h.get(i, 0) + (Q[i, j] + Q[j, i]) / 4
|
|
223
|
+
h[j] = h.get(j, 0) + (Q[i, j] + Q[j, i]) / 4
|
|
224
|
+
offset += (Q[i, j] + Q[j, i]) / 4
|
|
225
|
+
|
|
226
|
+
return J, h, offset
|
|
227
|
+
|
|
228
|
+
def _simulate_energy_history(self, final_energy: float) -> list[float]:
|
|
229
|
+
"""Simulate energy evolution during annealing."""
|
|
230
|
+
history = []
|
|
231
|
+
for step in range(self.n_steps):
|
|
232
|
+
s = step / self.n_steps
|
|
233
|
+
# Energy decreases as we anneal
|
|
234
|
+
energy = final_energy + (1 - s) * abs(final_energy) * 2
|
|
235
|
+
history.append(energy)
|
|
236
|
+
history.append(final_energy)
|
|
237
|
+
return history
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Quantum Circuit Optimizer.
|
|
3
|
+
|
|
4
|
+
Optimizes quantum circuits for better performance on real hardware.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from typing import Optional
|
|
9
|
+
import numpy as np
|
|
10
|
+
from qiskit import QuantumCircuit, transpile
|
|
11
|
+
|
|
12
|
+
from quantumflow.backends.base_backend import QuantumBackend, get_backend, BackendType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class OptimizationResult:
|
|
17
|
+
"""Result from circuit optimization."""
|
|
18
|
+
|
|
19
|
+
original_circuit: QuantumCircuit
|
|
20
|
+
optimized_circuit: QuantumCircuit
|
|
21
|
+
original_depth: int
|
|
22
|
+
optimized_depth: int
|
|
23
|
+
original_gates: int
|
|
24
|
+
optimized_gates: int
|
|
25
|
+
depth_reduction: float
|
|
26
|
+
gate_reduction: float
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CircuitOptimizer:
|
|
30
|
+
"""
|
|
31
|
+
Quantum Circuit Optimizer.
|
|
32
|
+
|
|
33
|
+
Applies various optimization techniques:
|
|
34
|
+
- Gate cancellation
|
|
35
|
+
- Gate fusion
|
|
36
|
+
- Commutation optimization
|
|
37
|
+
- Hardware-specific transpilation
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
>>> opt = CircuitOptimizer()
|
|
41
|
+
>>> result = opt.optimize(circuit)
|
|
42
|
+
>>> print(f"Depth reduced by {result.depth_reduction:.1%}")
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
backend: BackendType | str = BackendType.AUTO,
|
|
48
|
+
optimization_level: int = 2,
|
|
49
|
+
):
|
|
50
|
+
"""
|
|
51
|
+
Initialize optimizer.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
backend: Target backend for optimization
|
|
55
|
+
optimization_level: 0-3 (higher = more optimization)
|
|
56
|
+
"""
|
|
57
|
+
self.backend = get_backend(backend)
|
|
58
|
+
self.optimization_level = optimization_level
|
|
59
|
+
|
|
60
|
+
def optimize(
|
|
61
|
+
self,
|
|
62
|
+
circuit: QuantumCircuit,
|
|
63
|
+
target_basis: Optional[list[str]] = None,
|
|
64
|
+
) -> OptimizationResult:
|
|
65
|
+
"""
|
|
66
|
+
Optimize a quantum circuit.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
circuit: Circuit to optimize
|
|
70
|
+
target_basis: Target gate set (e.g., ['cx', 'u3'])
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
OptimizationResult with optimized circuit
|
|
74
|
+
"""
|
|
75
|
+
original_depth = circuit.depth()
|
|
76
|
+
original_gates = sum(circuit.count_ops().values())
|
|
77
|
+
|
|
78
|
+
# Apply optimizations
|
|
79
|
+
optimized = self._apply_optimizations(circuit)
|
|
80
|
+
|
|
81
|
+
# Transpile for target
|
|
82
|
+
if target_basis:
|
|
83
|
+
optimized = transpile(
|
|
84
|
+
optimized,
|
|
85
|
+
basis_gates=target_basis,
|
|
86
|
+
optimization_level=self.optimization_level,
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
optimized = transpile(
|
|
90
|
+
optimized,
|
|
91
|
+
optimization_level=self.optimization_level,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
optimized_depth = optimized.depth()
|
|
95
|
+
optimized_gates = sum(optimized.count_ops().values())
|
|
96
|
+
|
|
97
|
+
depth_reduction = 1 - (optimized_depth / original_depth) if original_depth > 0 else 0
|
|
98
|
+
gate_reduction = 1 - (optimized_gates / original_gates) if original_gates > 0 else 0
|
|
99
|
+
|
|
100
|
+
return OptimizationResult(
|
|
101
|
+
original_circuit=circuit,
|
|
102
|
+
optimized_circuit=optimized,
|
|
103
|
+
original_depth=original_depth,
|
|
104
|
+
optimized_depth=optimized_depth,
|
|
105
|
+
original_gates=original_gates,
|
|
106
|
+
optimized_gates=optimized_gates,
|
|
107
|
+
depth_reduction=depth_reduction,
|
|
108
|
+
gate_reduction=gate_reduction,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
def _apply_optimizations(self, circuit: QuantumCircuit) -> QuantumCircuit:
|
|
112
|
+
"""Apply custom optimization passes using transpile."""
|
|
113
|
+
# Use transpile with high optimization level
|
|
114
|
+
return transpile(circuit, optimization_level=3)
|
|
115
|
+
|
|
116
|
+
def cancel_redundant_gates(self, circuit: QuantumCircuit) -> QuantumCircuit:
|
|
117
|
+
"""Cancel adjacent inverse gates."""
|
|
118
|
+
return transpile(circuit, optimization_level=2)
|
|
119
|
+
|
|
120
|
+
def fuse_single_qubit_gates(self, circuit: QuantumCircuit) -> QuantumCircuit:
|
|
121
|
+
"""Fuse consecutive single-qubit gates."""
|
|
122
|
+
return transpile(circuit, optimization_level=2)
|
|
123
|
+
|
|
124
|
+
def optimize_for_hardware(
|
|
125
|
+
self,
|
|
126
|
+
circuit: QuantumCircuit,
|
|
127
|
+
coupling_map: Optional[list[list[int]]] = None,
|
|
128
|
+
) -> QuantumCircuit:
|
|
129
|
+
"""
|
|
130
|
+
Optimize circuit for specific hardware topology.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
circuit: Circuit to optimize
|
|
134
|
+
coupling_map: Hardware qubit connectivity
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Hardware-optimized circuit
|
|
138
|
+
"""
|
|
139
|
+
return transpile(
|
|
140
|
+
circuit,
|
|
141
|
+
coupling_map=coupling_map,
|
|
142
|
+
optimization_level=self.optimization_level,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def estimate_cost(self, circuit: QuantumCircuit) -> dict:
|
|
146
|
+
"""
|
|
147
|
+
Estimate circuit execution cost.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Dict with depth, gate counts, estimated time
|
|
151
|
+
"""
|
|
152
|
+
ops = circuit.count_ops()
|
|
153
|
+
|
|
154
|
+
# Rough gate time estimates (in microseconds)
|
|
155
|
+
gate_times = {
|
|
156
|
+
'x': 0.02, 'y': 0.02, 'z': 0.02,
|
|
157
|
+
'h': 0.02, 's': 0.02, 't': 0.02,
|
|
158
|
+
'rx': 0.03, 'ry': 0.03, 'rz': 0.03,
|
|
159
|
+
'cx': 0.3, 'cz': 0.3,
|
|
160
|
+
'ccx': 0.9, 'swap': 0.6,
|
|
161
|
+
'measure': 1.0,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
total_time = sum(
|
|
165
|
+
ops.get(gate, 0) * gate_times.get(gate, 0.1)
|
|
166
|
+
for gate in ops
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
two_qubit_gates = ops.get('cx', 0) + ops.get('cz', 0) + ops.get('swap', 0)
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
'depth': circuit.depth(),
|
|
173
|
+
'total_gates': sum(ops.values()),
|
|
174
|
+
'two_qubit_gates': two_qubit_gates,
|
|
175
|
+
'estimated_time_us': total_time,
|
|
176
|
+
'gate_counts': dict(ops),
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
def compare_circuits(
|
|
180
|
+
self,
|
|
181
|
+
circuit1: QuantumCircuit,
|
|
182
|
+
circuit2: QuantumCircuit,
|
|
183
|
+
) -> dict:
|
|
184
|
+
"""Compare two circuits."""
|
|
185
|
+
cost1 = self.estimate_cost(circuit1)
|
|
186
|
+
cost2 = self.estimate_cost(circuit2)
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
'circuit1': cost1,
|
|
190
|
+
'circuit2': cost2,
|
|
191
|
+
'depth_diff': cost1['depth'] - cost2['depth'],
|
|
192
|
+
'gate_diff': cost1['total_gates'] - cost2['total_gates'],
|
|
193
|
+
'time_diff_us': cost1['estimated_time_us'] - cost2['estimated_time_us'],
|
|
194
|
+
}
|