autoq-qec 0.2.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.
- autoq_qec/__init__.py +24 -0
- autoq_qec/algorithm_estimator.py +101 -0
- autoq_qec/qec_estimator.py +338 -0
- autoq_qec/real_hardware.py +212 -0
- autoq_qec/recommender.py +169 -0
- autoq_qec-0.2.1.dist-info/METADATA +141 -0
- autoq_qec-0.2.1.dist-info/RECORD +10 -0
- autoq_qec-0.2.1.dist-info/WHEEL +5 -0
- autoq_qec-0.2.1.dist-info/licenses/LICENSE +21 -0
- autoq_qec-0.2.1.dist-info/top_level.txt +1 -0
autoq_qec/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AutoQ QEC Estimator
|
|
3
|
+
Multi-code fault-tolerant quantum error correction estimator.
|
|
4
|
+
"""
|
|
5
|
+
from .qec_estimator import (
|
|
6
|
+
CircuitProfile,
|
|
7
|
+
HardwareProfile,
|
|
8
|
+
CodeResult,
|
|
9
|
+
extract_circuit_profile,
|
|
10
|
+
estimate,
|
|
11
|
+
compare,
|
|
12
|
+
)
|
|
13
|
+
from .real_hardware import CalibratedHardware, HARDWARE_PROFILES
|
|
14
|
+
from .recommender import rank, Recommendation
|
|
15
|
+
from .algorithm_estimator import AlgorithmEstimator, AlgorithmEstimate
|
|
16
|
+
|
|
17
|
+
__version__ = "0.2.1"
|
|
18
|
+
__all__ = [
|
|
19
|
+
"CircuitProfile", "HardwareProfile", "CodeResult",
|
|
20
|
+
"CalibratedHardware", "HARDWARE_PROFILES",
|
|
21
|
+
"Recommendation",
|
|
22
|
+
"AlgorithmEstimator", "AlgorithmEstimate",
|
|
23
|
+
"extract_circuit_profile", "estimate", "compare", "rank",
|
|
24
|
+
]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AutoQ QEC — Algorithm Estimator
|
|
3
|
+
Estimativas analíticas de T-count para algoritmos quânticos conhecidos.
|
|
4
|
+
ATENÇÃO: estimativas de ordem de grandeza. Usar circuito real quando possível.
|
|
5
|
+
"""
|
|
6
|
+
import math
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class AlgorithmEstimate:
|
|
12
|
+
algorithm: str
|
|
13
|
+
n_logical_qubits: int
|
|
14
|
+
t_count_estimate: int
|
|
15
|
+
t_count_uncertainty: str # "±5×" ou "±2×"
|
|
16
|
+
source: str
|
|
17
|
+
notes: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AlgorithmEstimator:
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def shor(N: int) -> AlgorithmEstimate:
|
|
24
|
+
"""
|
|
25
|
+
Shor factoring de N.
|
|
26
|
+
T-count ≈ 20n³ onde n = ⌈log₂N⌉.
|
|
27
|
+
Fonte: Beauregard (2003) + síntese de portas fault-tolerant.
|
|
28
|
+
Incerteza: ±5× dependendo da implementação de aritmética modular
|
|
29
|
+
(verificado contra Shor N=15 real: fórmula subestima ~4,7×).
|
|
30
|
+
"""
|
|
31
|
+
if N <= 2:
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"Shor requer N > 2 (N={N} não tem fatoração não-trivial a estimar)"
|
|
34
|
+
)
|
|
35
|
+
n = math.ceil(math.log2(N))
|
|
36
|
+
t_count = 20 * n**3
|
|
37
|
+
return AlgorithmEstimate(
|
|
38
|
+
algorithm=f"Shor N={N}",
|
|
39
|
+
n_logical_qubits=2*n + 3,
|
|
40
|
+
t_count_estimate=t_count,
|
|
41
|
+
t_count_uncertainty="±5×",
|
|
42
|
+
source="Beauregard, QIC 3:175 (2003); Fowler gate synthesis",
|
|
43
|
+
notes=f"n={n} bits. Implementações simples podem ter até 5× mais T-gates.",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def grover(N: int) -> AlgorithmEstimate:
|
|
48
|
+
"""
|
|
49
|
+
Grover search em N itens.
|
|
50
|
+
T-count ≈ 7n por iteração × ⌈π/4·√N⌉ iterações, com n=⌈log₂N⌉.
|
|
51
|
+
Nota: como n cresce com log₂N, o T-count total escala com √N·log₂N,
|
|
52
|
+
não apenas √N — o fator log₂N vem do custo do oracle genérico por
|
|
53
|
+
iteração, não só do número de iterações.
|
|
54
|
+
Fonte: estimativa com oracle genérico.
|
|
55
|
+
"""
|
|
56
|
+
if N <= 1:
|
|
57
|
+
raise ValueError(f"Grover requer N > 1 itens de busca (N={N})")
|
|
58
|
+
n = math.ceil(math.log2(N))
|
|
59
|
+
iterations = math.ceil(math.pi/4 * math.sqrt(N))
|
|
60
|
+
t_per_iter = 7 * n
|
|
61
|
+
t_count = t_per_iter * iterations
|
|
62
|
+
return AlgorithmEstimate(
|
|
63
|
+
algorithm=f"Grover N={N}",
|
|
64
|
+
n_logical_qubits=n + 1,
|
|
65
|
+
t_count_estimate=t_count,
|
|
66
|
+
t_count_uncertainty="±10×",
|
|
67
|
+
source="Estimativa com oracle genérico de n T-gates",
|
|
68
|
+
notes="Depende fortemente do oracle. Incerteza alta sem oracle específico.",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def qft(n: int) -> AlgorithmEstimate:
|
|
73
|
+
"""
|
|
74
|
+
QFT em n qubits.
|
|
75
|
+
T-count ≈ n²/2 (rotações CP com síntese Solovay-Kitaev).
|
|
76
|
+
"""
|
|
77
|
+
t_count = n*n // 2
|
|
78
|
+
return AlgorithmEstimate(
|
|
79
|
+
algorithm=f"QFT n={n}",
|
|
80
|
+
n_logical_qubits=n,
|
|
81
|
+
t_count_estimate=t_count,
|
|
82
|
+
t_count_uncertainty="±2×",
|
|
83
|
+
source="Rotações CP decompostas em Clifford+T",
|
|
84
|
+
notes="CP(π/2^k) para k>2 requer síntese com T-gates.",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def vqe(n_qubits: int, depth: int) -> AlgorithmEstimate:
|
|
89
|
+
"""
|
|
90
|
+
VQE com n_qubits e profundidade depth.
|
|
91
|
+
T-count ≈ 4 · n · depth (Trotterizado).
|
|
92
|
+
"""
|
|
93
|
+
t_count = 4 * n_qubits * depth
|
|
94
|
+
return AlgorithmEstimate(
|
|
95
|
+
algorithm=f"VQE n={n_qubits} d={depth}",
|
|
96
|
+
n_logical_qubits=n_qubits,
|
|
97
|
+
t_count_estimate=t_count,
|
|
98
|
+
t_count_uncertainty="±3×",
|
|
99
|
+
source="Hamiltoniano Trotterizado com RZZ gates",
|
|
100
|
+
notes="Depende do Hamiltoniano. Incerteza moderada.",
|
|
101
|
+
)
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AutoQ QEC Estimator — módulo de estimativa multi-código fault-tolerant
|
|
3
|
+
Referências:
|
|
4
|
+
Surface Code: Fowler et al., PRA 86, 032324 (2012)
|
|
5
|
+
Bacon-Shor: Bacon, PRA 73, 012340 (2006); Aliferis & Cross (2007)
|
|
6
|
+
Steane [[7,1,3]]: Steane, PRL 77, 793 (1996)
|
|
7
|
+
"""
|
|
8
|
+
import math
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
# ── Tipos ────────────────────────────────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class CircuitProfile:
|
|
16
|
+
"""Métricas extraídas de um circuito Qiskit."""
|
|
17
|
+
n_logical_qubits: int
|
|
18
|
+
n_logical_gates: int # portas lógicas (nível Qiskit)
|
|
19
|
+
n_physical_gates: int # portas físicas após transpile CX+U
|
|
20
|
+
depth_physical: int # profundidade na base física
|
|
21
|
+
t_count: int # número de portas T (dominam custo FT)
|
|
22
|
+
cx_count: int
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class HardwareProfile:
|
|
26
|
+
name: str
|
|
27
|
+
t_gate_ns: float # tempo de porta em nanossegundos
|
|
28
|
+
p_phys: float # taxa de erro físico por porta
|
|
29
|
+
topology: str # "heavy-hex", "all-to-all", "linear", "grid"
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class CodeResult:
|
|
33
|
+
code_name: str
|
|
34
|
+
distance: Optional[int]
|
|
35
|
+
qubits_per_logical: Optional[int]
|
|
36
|
+
total_physical_qubits: Optional[int]
|
|
37
|
+
gate_overhead_per_logical: Optional[float]
|
|
38
|
+
total_physical_gates: Optional[float]
|
|
39
|
+
execution_time_us: Optional[float]
|
|
40
|
+
p_logical_achieved: Optional[float]
|
|
41
|
+
fidelity_circuit: Optional[float]
|
|
42
|
+
feasible: bool
|
|
43
|
+
reason: str # motivo de inviabilidade ou sumário
|
|
44
|
+
|
|
45
|
+
# ── Modelos QEC ──────────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
def _surface_code_model(p_phys: float, p_L_target: float,
|
|
48
|
+
p_th: float = 0.01, A: float = 0.1):
|
|
49
|
+
"""
|
|
50
|
+
Fowler et al. 2012: p_L ≈ A*(p/p_th)^((d+1)/2)
|
|
51
|
+
Retorna (d, qubits_per_logical, cycles_per_gate, p_L_achieved) ou raises
|
|
52
|
+
"""
|
|
53
|
+
if p_phys >= p_th:
|
|
54
|
+
raise ValueError(f"p_phys={p_phys:.4f} ≥ threshold={p_th}: não converge")
|
|
55
|
+
# Resolver d mínimo
|
|
56
|
+
ratio = math.log(p_L_target / A) / math.log(p_phys / p_th)
|
|
57
|
+
d = max(3, math.ceil(2 * ratio - 1 + 1e-9))
|
|
58
|
+
# Safeguard contra erro de ponto flutuante na fronteira
|
|
59
|
+
p_L_check = A * (p_phys / p_th) ** ((d + 1) / 2)
|
|
60
|
+
if p_L_check > p_L_target * (1 + 1e-12):
|
|
61
|
+
d += 2 # próximo ímpar
|
|
62
|
+
if d % 2 == 0:
|
|
63
|
+
d += 1 # d deve ser ímpar no Surface Code rotacionado
|
|
64
|
+
p_L = A * (p_phys / p_th) ** ((d + 1) / 2)
|
|
65
|
+
q_per_logical = 2 * d**2 - 1
|
|
66
|
+
# Ciclos de síndrome por porta lógica fault-tolerant: d (conservative)
|
|
67
|
+
cycles_per_gate = d
|
|
68
|
+
return d, q_per_logical, cycles_per_gate, p_L
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _bacon_shor_model(p_phys: float, p_L_target: float,
|
|
72
|
+
p_th: float = 0.008):
|
|
73
|
+
"""
|
|
74
|
+
Aliferis & Cross 2007: p_L ≈ (p/p_th)^d para [[d²,1,d]]
|
|
75
|
+
threshold ~0.7-1.1% dependendo do modelo; usamos 0.8% (conservador)
|
|
76
|
+
"""
|
|
77
|
+
if p_phys >= p_th:
|
|
78
|
+
raise ValueError(f"p_phys={p_phys:.4f} ≥ threshold_BS={p_th}: não converge")
|
|
79
|
+
d = max(2, math.ceil(math.log(p_L_target) / math.log(p_phys / p_th)))
|
|
80
|
+
p_L = (p_phys / p_th) ** d
|
|
81
|
+
q_per_logical = d**2
|
|
82
|
+
cycles_per_gate = 2 * (d - 1) # medições de gauge por ciclo
|
|
83
|
+
return d, q_per_logical, cycles_per_gate, p_L
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _steane_model(p_phys: float, p_L_target: float,
|
|
87
|
+
p_th: float = 0.007):
|
|
88
|
+
"""
|
|
89
|
+
Steane [[7,1,3]]: código CSS fixo, d=3, p_L ≈ 21*p²
|
|
90
|
+
Só é viável se p < p_th E p_L_target > 21*p²
|
|
91
|
+
"""
|
|
92
|
+
if p_phys >= p_th:
|
|
93
|
+
raise ValueError(f"p_phys={p_phys:.4f} ≥ threshold_Steane={p_th}")
|
|
94
|
+
p_L = 21 * p_phys**2
|
|
95
|
+
if p_L > p_L_target:
|
|
96
|
+
raise ValueError(
|
|
97
|
+
f"Steane d=3 insuficiente: p_L={p_L:.2e} > alvo={p_L_target:.2e}. "
|
|
98
|
+
f"Necessário concatenação ou código de distância maior."
|
|
99
|
+
)
|
|
100
|
+
return 3, 13, 6, p_L # 7 data + 6 ancilla, 6 síndrome por ciclo
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _floquet_code_model(p_phys: float, p_L_target: float,
|
|
104
|
+
p_th: float = 0.01, A: float = 0.07):
|
|
105
|
+
"""
|
|
106
|
+
Floquet Code planar (4.8.8) — Gidney & Fowler, arXiv:2202.11829
|
|
107
|
+
Vantagem vs Surface Code: overhead de tempo menor (d//2 vs d³ rodadas).
|
|
108
|
+
Custo: ~2× mais qubits por lógico (4d²+8(d-1) vs 2d²-1).
|
|
109
|
+
"""
|
|
110
|
+
if p_phys >= p_th:
|
|
111
|
+
raise ValueError(f"p_phys={p_phys:.4f} ≥ threshold_Floquet={p_th}: não converge")
|
|
112
|
+
ratio = math.log(p_L_target / A) / math.log(p_phys / p_th)
|
|
113
|
+
d = max(3, math.ceil(2 * ratio - 1 + 1e-9))
|
|
114
|
+
if d % 2 == 0:
|
|
115
|
+
d += 1
|
|
116
|
+
p_L = A * (p_phys / p_th) ** ((d + 1) / 2)
|
|
117
|
+
if p_L > p_L_target * (1 + 1e-9):
|
|
118
|
+
d += 2
|
|
119
|
+
p_L = A * (p_phys / p_th) ** ((d + 1) / 2)
|
|
120
|
+
q_per_logical = 4 * d**2 + 8 * (d - 1)
|
|
121
|
+
gate_overhead = d // 2
|
|
122
|
+
return d, q_per_logical, gate_overhead, p_L
|
|
123
|
+
|
|
124
|
+
# ── Extrator de perfil de circuito ───────────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
def _count_t_gates(circuit) -> int:
|
|
127
|
+
"""
|
|
128
|
+
Conta T e T† gates após decomposição na base Clifford+T.
|
|
129
|
+
Portas Clifford (H, S, CX) têm custo zero em QEC fault-tolerant.
|
|
130
|
+
optimization_level=2 simplifica ângulos redundantes antes da contagem
|
|
131
|
+
(ex.: T·T·T otimiza para S·T — 1 T-gate real, não 3).
|
|
132
|
+
"""
|
|
133
|
+
from qiskit import transpile
|
|
134
|
+
t_basis = transpile(
|
|
135
|
+
circuit,
|
|
136
|
+
basis_gates=['t', 'tdg', 's', 'sdg', 'h', 'x', 'y', 'z', 'cx'],
|
|
137
|
+
optimization_level=2,
|
|
138
|
+
seed_transpiler=42
|
|
139
|
+
)
|
|
140
|
+
ops = t_basis.count_ops()
|
|
141
|
+
return ops.get('t', 0) + ops.get('tdg', 0)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def extract_circuit_profile(circuit) -> CircuitProfile:
|
|
145
|
+
"""
|
|
146
|
+
Extrai métricas reais de um QuantumCircuit Qiskit.
|
|
147
|
+
Transpila para base {CX, U} para obter contagem física real.
|
|
148
|
+
"""
|
|
149
|
+
from qiskit import transpile
|
|
150
|
+
|
|
151
|
+
# Perfil lógico
|
|
152
|
+
n_q = circuit.num_qubits
|
|
153
|
+
ops_logical = {k: v for k, v in circuit.count_ops().items()
|
|
154
|
+
if k not in ('measure', 'barrier', 'reset')}
|
|
155
|
+
n_logical = sum(ops_logical.values())
|
|
156
|
+
|
|
157
|
+
# Perfil físico — transpile para base universal sem backend específico
|
|
158
|
+
phys = transpile(circuit, basis_gates=['cx', 'u'],
|
|
159
|
+
optimization_level=3, seed_transpiler=42)
|
|
160
|
+
ops_phys = {k: v for k, v in phys.count_ops().items()
|
|
161
|
+
if k not in ('measure', 'barrier', 'reset')}
|
|
162
|
+
n_physical = sum(ops_phys.values())
|
|
163
|
+
depth_phys = phys.depth()
|
|
164
|
+
|
|
165
|
+
# T-gates: contagem exata via decomposição Clifford+T (ver _count_t_gates).
|
|
166
|
+
# Portas Clifford (H, S, CX) têm custo zero em QEC fault-tolerant.
|
|
167
|
+
t_count = _count_t_gates(circuit)
|
|
168
|
+
cx_count = ops_phys.get('cx', 0)
|
|
169
|
+
|
|
170
|
+
return CircuitProfile(
|
|
171
|
+
n_logical_qubits=n_q,
|
|
172
|
+
n_logical_gates=n_logical,
|
|
173
|
+
n_physical_gates=n_physical,
|
|
174
|
+
depth_physical=depth_phys,
|
|
175
|
+
t_count=t_count,
|
|
176
|
+
cx_count=cx_count,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# ── Estimador principal ───────────────────────────────────────────────────────
|
|
180
|
+
|
|
181
|
+
def estimate(circuit_profile: CircuitProfile,
|
|
182
|
+
hardware: HardwareProfile,
|
|
183
|
+
fidelity_target: float = 0.99) -> list[CodeResult]:
|
|
184
|
+
"""
|
|
185
|
+
Para um dado CircuitProfile + HardwareProfile + alvo de fidelidade,
|
|
186
|
+
retorna lista de CodeResult para cada código QEC analisado.
|
|
187
|
+
|
|
188
|
+
p_L_per_gate = (1 - fidelity_target) / n_physical_gates
|
|
189
|
+
"""
|
|
190
|
+
if not (0 < fidelity_target < 1):
|
|
191
|
+
raise ValueError("fidelity_target deve estar em (0, 1)")
|
|
192
|
+
|
|
193
|
+
N = circuit_profile.n_physical_gates
|
|
194
|
+
if N == 0:
|
|
195
|
+
raise ValueError("Circuito tem 0 portas físicas — foi destruído pelo transpile?")
|
|
196
|
+
|
|
197
|
+
p_L_target = (1 - fidelity_target) / N
|
|
198
|
+
t_ns = hardware.t_gate_ns
|
|
199
|
+
n_L = circuit_profile.n_logical_qubits
|
|
200
|
+
p = hardware.p_phys
|
|
201
|
+
|
|
202
|
+
results = []
|
|
203
|
+
|
|
204
|
+
# ── Surface Code ──────────────────────────────────────────────────────────
|
|
205
|
+
try:
|
|
206
|
+
d, q_per_L, cycles, p_L = _surface_code_model(p, p_L_target)
|
|
207
|
+
total_q = q_per_L * n_L
|
|
208
|
+
# cycles = d rodadas de síndrome por porta lógica (Fowler et al. PRA 86,
|
|
209
|
+
# 032324, Sec. IV). Cada rodada tem d² medições de estabilizador (CX por
|
|
210
|
+
# ciclo). Overhead total por porta lógica: d rodadas × d² medições = d³.
|
|
211
|
+
gate_overhead = cycles * d**2 # = d³
|
|
212
|
+
total_phys_gates = N * gate_overhead
|
|
213
|
+
time_us = total_phys_gates * t_ns / 1000
|
|
214
|
+
fid = (1 - p_L) ** N
|
|
215
|
+
results.append(CodeResult(
|
|
216
|
+
code_name="Surface Code",
|
|
217
|
+
distance=d,
|
|
218
|
+
qubits_per_logical=q_per_L,
|
|
219
|
+
total_physical_qubits=total_q,
|
|
220
|
+
gate_overhead_per_logical=gate_overhead,
|
|
221
|
+
total_physical_gates=total_phys_gates,
|
|
222
|
+
execution_time_us=time_us,
|
|
223
|
+
p_logical_achieved=p_L,
|
|
224
|
+
fidelity_circuit=fid,
|
|
225
|
+
feasible=True,
|
|
226
|
+
reason=f"d={d}, p_L={p_L:.2e}",
|
|
227
|
+
))
|
|
228
|
+
except ValueError as e:
|
|
229
|
+
results.append(CodeResult(
|
|
230
|
+
code_name="Surface Code", distance=None, qubits_per_logical=None,
|
|
231
|
+
total_physical_qubits=None, gate_overhead_per_logical=None,
|
|
232
|
+
total_physical_gates=None, execution_time_us=None,
|
|
233
|
+
p_logical_achieved=None, fidelity_circuit=None,
|
|
234
|
+
feasible=False, reason=str(e),
|
|
235
|
+
))
|
|
236
|
+
|
|
237
|
+
# ── Bacon-Shor ────────────────────────────────────────────────────────────
|
|
238
|
+
try:
|
|
239
|
+
d, q_per_L, cycles, p_L = _bacon_shor_model(p, p_L_target)
|
|
240
|
+
total_q = q_per_L * n_L
|
|
241
|
+
total_phys_gates = N * cycles
|
|
242
|
+
time_us = total_phys_gates * t_ns / 1000
|
|
243
|
+
fid = (1 - p_L) ** N
|
|
244
|
+
results.append(CodeResult(
|
|
245
|
+
code_name="Bacon-Shor",
|
|
246
|
+
distance=d,
|
|
247
|
+
qubits_per_logical=q_per_L,
|
|
248
|
+
total_physical_qubits=total_q,
|
|
249
|
+
gate_overhead_per_logical=cycles,
|
|
250
|
+
total_physical_gates=total_phys_gates,
|
|
251
|
+
execution_time_us=time_us,
|
|
252
|
+
p_logical_achieved=p_L,
|
|
253
|
+
fidelity_circuit=fid,
|
|
254
|
+
feasible=True,
|
|
255
|
+
reason=f"d={d}, [[{d**2},1,{d}]], p_L={p_L:.2e}",
|
|
256
|
+
))
|
|
257
|
+
except ValueError as e:
|
|
258
|
+
results.append(CodeResult(
|
|
259
|
+
code_name="Bacon-Shor", distance=None, qubits_per_logical=None,
|
|
260
|
+
total_physical_qubits=None, gate_overhead_per_logical=None,
|
|
261
|
+
total_physical_gates=None, execution_time_us=None,
|
|
262
|
+
p_logical_achieved=None, fidelity_circuit=None,
|
|
263
|
+
feasible=False, reason=str(e),
|
|
264
|
+
))
|
|
265
|
+
|
|
266
|
+
# ── Steane [[7,1,3]] ──────────────────────────────────────────────────────
|
|
267
|
+
try:
|
|
268
|
+
d, q_per_L, cycles, p_L = _steane_model(p, p_L_target)
|
|
269
|
+
total_q = q_per_L * n_L
|
|
270
|
+
total_phys_gates = N * cycles
|
|
271
|
+
time_us = total_phys_gates * t_ns / 1000
|
|
272
|
+
fid = (1 - p_L) ** N
|
|
273
|
+
results.append(CodeResult(
|
|
274
|
+
code_name="Steane [[7,1,3]]",
|
|
275
|
+
distance=3,
|
|
276
|
+
qubits_per_logical=q_per_L,
|
|
277
|
+
total_physical_qubits=total_q,
|
|
278
|
+
gate_overhead_per_logical=cycles,
|
|
279
|
+
total_physical_gates=total_phys_gates,
|
|
280
|
+
execution_time_us=time_us,
|
|
281
|
+
p_logical_achieved=p_L,
|
|
282
|
+
fidelity_circuit=fid,
|
|
283
|
+
feasible=True,
|
|
284
|
+
reason=f"d=3 fixo, p_L={p_L:.2e}",
|
|
285
|
+
))
|
|
286
|
+
except ValueError as e:
|
|
287
|
+
results.append(CodeResult(
|
|
288
|
+
code_name="Steane [[7,1,3]]", distance=None, qubits_per_logical=None,
|
|
289
|
+
total_physical_qubits=None, gate_overhead_per_logical=None,
|
|
290
|
+
total_physical_gates=None, execution_time_us=None,
|
|
291
|
+
p_logical_achieved=None, fidelity_circuit=None,
|
|
292
|
+
feasible=False, reason=str(e),
|
|
293
|
+
))
|
|
294
|
+
|
|
295
|
+
# ── Floquet Code ──────────────────────────────────────────────────────────
|
|
296
|
+
try:
|
|
297
|
+
d, q_per_L, cycles, p_L = _floquet_code_model(p, p_L_target)
|
|
298
|
+
total_q = q_per_L * n_L
|
|
299
|
+
total_phys_gates = N * cycles
|
|
300
|
+
time_us = total_phys_gates * t_ns / 1000
|
|
301
|
+
fid = (1 - p_L) ** N
|
|
302
|
+
results.append(CodeResult(
|
|
303
|
+
code_name="Floquet Code",
|
|
304
|
+
distance=d,
|
|
305
|
+
qubits_per_logical=q_per_L,
|
|
306
|
+
total_physical_qubits=total_q,
|
|
307
|
+
gate_overhead_per_logical=cycles,
|
|
308
|
+
total_physical_gates=total_phys_gates,
|
|
309
|
+
execution_time_us=time_us,
|
|
310
|
+
p_logical_achieved=p_L,
|
|
311
|
+
fidelity_circuit=fid,
|
|
312
|
+
feasible=True,
|
|
313
|
+
reason=f"d={d} (4.8.8 planar), p_L={p_L:.2e}",
|
|
314
|
+
))
|
|
315
|
+
except ValueError as e:
|
|
316
|
+
results.append(CodeResult(
|
|
317
|
+
code_name="Floquet Code", distance=None, qubits_per_logical=None,
|
|
318
|
+
total_physical_qubits=None, gate_overhead_per_logical=None,
|
|
319
|
+
total_physical_gates=None, execution_time_us=None,
|
|
320
|
+
p_logical_achieved=None, fidelity_circuit=None,
|
|
321
|
+
feasible=False, reason=str(e),
|
|
322
|
+
))
|
|
323
|
+
|
|
324
|
+
return results
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def compare(circuit, hardware_list: list[HardwareProfile],
|
|
328
|
+
fidelity_target: float = 0.99):
|
|
329
|
+
"""
|
|
330
|
+
API principal: recebe circuito Qiskit + lista de hardwares,
|
|
331
|
+
devolve dicionário hardware_name → [CodeResult].
|
|
332
|
+
"""
|
|
333
|
+
profile = extract_circuit_profile(circuit)
|
|
334
|
+
output = {"circuit_profile": profile, "results": {}, "hardware_profiles": {}}
|
|
335
|
+
for hw in hardware_list:
|
|
336
|
+
output["results"][hw.name] = estimate(profile, hw, fidelity_target)
|
|
337
|
+
output["hardware_profiles"][hw.name] = hw
|
|
338
|
+
return output
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AutoQ Real Hardware Integration
|
|
3
|
+
Três modos de operação:
|
|
4
|
+
1. IBM real — puxa backend.properties() com token
|
|
5
|
+
2. IBM noise — AerSimulator com noise model do backend real
|
|
6
|
+
3. Publicado — dados de papers/specs para fabricantes sem API aberta
|
|
7
|
+
"""
|
|
8
|
+
import math
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class CalibratedHardware:
|
|
14
|
+
"""Perfil de hardware com dados reais de calibração."""
|
|
15
|
+
name: str
|
|
16
|
+
n_qubits: int
|
|
17
|
+
p_1q_mean: float # erro médio porta 1q
|
|
18
|
+
p_2q_mean: float # erro médio porta 2q (CX/CZ/ZZ)
|
|
19
|
+
p_2q_worst: float # erro máximo — par de qubits mais ruidoso
|
|
20
|
+
t_1q_ns: float # duração porta 1q em ns
|
|
21
|
+
t_2q_ns: float # duração porta 2q em ns
|
|
22
|
+
T1_us: float # tempo de relaxação médio
|
|
23
|
+
T2_us: float # tempo de decoerência médio
|
|
24
|
+
readout_error: float # erro de leitura médio
|
|
25
|
+
topology: str
|
|
26
|
+
source: str # referência dos dados
|
|
27
|
+
# p_phys efetivo para QEC: geralmente dominado por 2q gates
|
|
28
|
+
@property
|
|
29
|
+
def p_phys(self) -> float:
|
|
30
|
+
return self.p_2q_mean
|
|
31
|
+
|
|
32
|
+
def t1_constraint(self, n_physical_gates: int, gate_overhead: float) -> dict:
|
|
33
|
+
"""Verifica se o circuito QEC cabe dentro do T1."""
|
|
34
|
+
t_circuit_us = n_physical_gates * gate_overhead * self.t_2q_ns / 1000
|
|
35
|
+
t1_us = self.T1_us
|
|
36
|
+
ratio = t_circuit_us / t1_us
|
|
37
|
+
return {
|
|
38
|
+
"t_circuit_us": t_circuit_us,
|
|
39
|
+
"T1_us": t1_us,
|
|
40
|
+
"ratio": ratio,
|
|
41
|
+
"feasible": ratio < 0.5, # conservador: <50% de T1
|
|
42
|
+
"warning": ratio > 0.1,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ── Dados publicados por fabricante ───────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
HARDWARE_PROFILES = {
|
|
49
|
+
|
|
50
|
+
"IBM_Eagle_r3": CalibratedHardware(
|
|
51
|
+
name="IBM Eagle r3 (ibm_brisbane)",
|
|
52
|
+
n_qubits=127,
|
|
53
|
+
p_1q_mean=2.8e-4,
|
|
54
|
+
p_2q_mean=6.2e-3,
|
|
55
|
+
p_2q_worst=2.1e-2,
|
|
56
|
+
t_1q_ns=56.0,
|
|
57
|
+
t_2q_ns=391.0,
|
|
58
|
+
T1_us=198.4,
|
|
59
|
+
T2_us=143.2,
|
|
60
|
+
readout_error=0.0142,
|
|
61
|
+
topology="heavy-hex",
|
|
62
|
+
source="IBM Quantum Network calibration data, Eagle r3, 2024",
|
|
63
|
+
),
|
|
64
|
+
|
|
65
|
+
"IBM_Heron_r2": CalibratedHardware(
|
|
66
|
+
name="IBM Heron r2 (ibm_torino)",
|
|
67
|
+
n_qubits=133,
|
|
68
|
+
p_1q_mean=1.9e-4,
|
|
69
|
+
p_2q_mean=3.0e-3, # CZ gate no Heron
|
|
70
|
+
p_2q_worst=8.5e-3,
|
|
71
|
+
t_1q_ns=56.0,
|
|
72
|
+
t_2q_ns=100.0, # CZ mais rápido que CNOT no Eagle
|
|
73
|
+
T1_us=242.0,
|
|
74
|
+
T2_us=186.0,
|
|
75
|
+
readout_error=0.0089,
|
|
76
|
+
topology="heavy-hex",
|
|
77
|
+
source="IBM Quantum: Heron r2 specs 2024; arXiv:2404.07471",
|
|
78
|
+
),
|
|
79
|
+
|
|
80
|
+
"Quantinuum_H2": CalibratedHardware(
|
|
81
|
+
name="Quantinuum H2-1",
|
|
82
|
+
n_qubits=56,
|
|
83
|
+
p_1q_mean=3.8e-5,
|
|
84
|
+
p_2q_mean=2.9e-4, # ZZ gate
|
|
85
|
+
p_2q_worst=5.0e-4,
|
|
86
|
+
t_1q_ns=10e3, # 10 µs
|
|
87
|
+
t_2q_ns=100e3, # 100 µs (íon aprisionado)
|
|
88
|
+
T1_us=1e7, # horas — sem limite prático de T1
|
|
89
|
+
T2_us=1e5, # ~0.1 s
|
|
90
|
+
readout_error=0.0015,
|
|
91
|
+
topology="all-to-all",
|
|
92
|
+
source="Quantinuum H-Series specs; PRX Quantum 4, 020312 (2023)",
|
|
93
|
+
),
|
|
94
|
+
|
|
95
|
+
"IonQ_Aria": CalibratedHardware(
|
|
96
|
+
name="IonQ Aria-1",
|
|
97
|
+
n_qubits=25,
|
|
98
|
+
p_1q_mean=4.0e-4,
|
|
99
|
+
p_2q_mean=5.5e-3, # MS gate (Mølmer-Sørensen)
|
|
100
|
+
p_2q_worst=8.0e-3,
|
|
101
|
+
t_1q_ns=135e3, # 135 µs
|
|
102
|
+
t_2q_ns=600e3, # 600 µs
|
|
103
|
+
T1_us=1e7, # >10 s
|
|
104
|
+
T2_us=1e5, # ~0.1 s
|
|
105
|
+
readout_error=0.005,
|
|
106
|
+
topology="all-to-all",
|
|
107
|
+
source="IonQ Aria specs; arXiv:2307.01765 (2023)",
|
|
108
|
+
),
|
|
109
|
+
|
|
110
|
+
"Google_Sycamore": CalibratedHardware(
|
|
111
|
+
name="Google Sycamore (53q)",
|
|
112
|
+
n_qubits=53,
|
|
113
|
+
p_1q_mean=1.6e-3,
|
|
114
|
+
p_2q_mean=6.2e-3, # fSim gate
|
|
115
|
+
p_2q_worst=1.1e-2,
|
|
116
|
+
t_1q_ns=25.0,
|
|
117
|
+
t_2q_ns=12.0, # fSim muito rápido
|
|
118
|
+
T1_us=15.0,
|
|
119
|
+
T2_us=20.0,
|
|
120
|
+
readout_error=0.037,
|
|
121
|
+
topology="grid-2d",
|
|
122
|
+
source="Arute et al., Nature 574, 505 (2019)",
|
|
123
|
+
),
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
# ── Integração IBM real (requer token) ────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
def from_ibm_backend(backend_name: str, token: str) -> CalibratedHardware:
|
|
129
|
+
"""
|
|
130
|
+
Puxa calibração real do IBM Quantum via qiskit-ibm-runtime.
|
|
131
|
+
Requer: pip install qiskit-ibm-runtime
|
|
132
|
+
Token gratuito em: https://quantum.ibm.com
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
from qiskit_ibm_runtime import QiskitRuntimeService
|
|
136
|
+
except ImportError:
|
|
137
|
+
raise ImportError("pip install qiskit-ibm-runtime")
|
|
138
|
+
|
|
139
|
+
service = QiskitRuntimeService(channel='ibm_quantum', token=token)
|
|
140
|
+
backend = service.backend(backend_name)
|
|
141
|
+
props = backend.properties()
|
|
142
|
+
config = backend.configuration()
|
|
143
|
+
|
|
144
|
+
# Agregar erros por tipo de porta
|
|
145
|
+
cx_errors, cx_durations = [], []
|
|
146
|
+
sx_errors, sx_durations = [], []
|
|
147
|
+
|
|
148
|
+
for gate in props.gates:
|
|
149
|
+
if gate.gate == 'cx':
|
|
150
|
+
for param in gate.parameters:
|
|
151
|
+
if param.name == 'gate_error': cx_errors.append(param.value)
|
|
152
|
+
if param.name == 'gate_length': cx_durations.append(param.value * 1e9)
|
|
153
|
+
if gate.gate in ('sx', 'x'):
|
|
154
|
+
for param in gate.parameters:
|
|
155
|
+
if param.name == 'gate_error': sx_errors.append(param.value)
|
|
156
|
+
if param.name == 'gate_length': sx_durations.append(param.value * 1e9)
|
|
157
|
+
|
|
158
|
+
T1_vals = [props.t1(q) * 1e6 for q in range(config.n_qubits) if props.t1(q)]
|
|
159
|
+
T2_vals = [props.t2(q) * 1e6 for q in range(config.n_qubits) if props.t2(q)]
|
|
160
|
+
ro_errs = [props.readout_error(q) for q in range(config.n_qubits)]
|
|
161
|
+
|
|
162
|
+
return CalibratedHardware(
|
|
163
|
+
name=f"IBM {backend_name} (calibração ao vivo)",
|
|
164
|
+
n_qubits=config.n_qubits,
|
|
165
|
+
p_1q_mean=sum(sx_errors)/len(sx_errors) if sx_errors else 3e-4,
|
|
166
|
+
p_2q_mean=sum(cx_errors)/len(cx_errors) if cx_errors else 6e-3,
|
|
167
|
+
p_2q_worst=max(cx_errors) if cx_errors else 2e-2,
|
|
168
|
+
t_1q_ns=sum(sx_durations)/len(sx_durations) if sx_durations else 56,
|
|
169
|
+
t_2q_ns=sum(cx_durations)/len(cx_durations) if cx_durations else 400,
|
|
170
|
+
T1_us=sum(T1_vals)/len(T1_vals) if T1_vals else 200,
|
|
171
|
+
T2_us=sum(T2_vals)/len(T2_vals) if T2_vals else 150,
|
|
172
|
+
readout_error=sum(ro_errs)/len(ro_errs) if ro_errs else 0.015,
|
|
173
|
+
topology="heavy-hex",
|
|
174
|
+
source=f"IBM Quantum live calibration — {backend_name}",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def noise_model_from_ibm(backend_name: str, token: str):
|
|
179
|
+
"""
|
|
180
|
+
Constrói AerSimulator com noise model real do backend IBM.
|
|
181
|
+
Permite simular localmente com ruído realista sem usar shots pagos.
|
|
182
|
+
Requer: pip install qiskit-ibm-runtime qiskit-aer
|
|
183
|
+
"""
|
|
184
|
+
try:
|
|
185
|
+
from qiskit_ibm_runtime import QiskitRuntimeService
|
|
186
|
+
from qiskit_aer.noise import NoiseModel
|
|
187
|
+
from qiskit_aer import AerSimulator
|
|
188
|
+
except ImportError:
|
|
189
|
+
raise ImportError("pip install qiskit-ibm-runtime qiskit-aer")
|
|
190
|
+
|
|
191
|
+
service = QiskitRuntimeService(channel='ibm_quantum', token=token)
|
|
192
|
+
backend = service.backend(backend_name)
|
|
193
|
+
noise_model = NoiseModel.from_backend(backend)
|
|
194
|
+
sim = AerSimulator(noise_model=noise_model,
|
|
195
|
+
coupling_map=backend.configuration().coupling_map,
|
|
196
|
+
basis_gates=noise_model.basis_gates)
|
|
197
|
+
return sim
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# ── Análise de viabilidade T1 ────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
def t1_feasibility_report(hw: CalibratedHardware,
|
|
203
|
+
n_physical_gates: int,
|
|
204
|
+
gate_overhead: float) -> None:
|
|
205
|
+
"""Imprime relatório de viabilidade baseado em T1/T2."""
|
|
206
|
+
check = hw.t1_constraint(n_physical_gates, gate_overhead)
|
|
207
|
+
status = "✓ VIÁVEL" if check["feasible"] else "✗ INVIÁVEL (excede T1)"
|
|
208
|
+
warn = "⚠ ATENÇÃO" if check["warning"] and check["feasible"] else ""
|
|
209
|
+
print(f" T1 check [{hw.name}]: {status} {warn}")
|
|
210
|
+
print(f" t_circuit = {check['t_circuit_us']:.1f} µs")
|
|
211
|
+
print(f" T1 médio = {check['T1_us']:.1f} µs")
|
|
212
|
+
print(f" ratio = {check['ratio']:.3f} ({check['ratio']*100:.1f}% de T1)")
|
autoq_qec/recommender.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AutoQ Recommender — rankeamento automático de código+hardware
|
|
3
|
+
para um dado circuito e alvo de fidelidade.
|
|
4
|
+
"""
|
|
5
|
+
import math
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from .qec_estimator import CodeResult, CircuitProfile
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class Recommendation:
|
|
12
|
+
rank: int
|
|
13
|
+
hardware: str
|
|
14
|
+
code: str
|
|
15
|
+
total_physical_qubits: int
|
|
16
|
+
execution_time_us: float
|
|
17
|
+
fidelity_circuit: float
|
|
18
|
+
score: float # menor = melhor (normalizado)
|
|
19
|
+
bottleneck: str # o que domina o custo
|
|
20
|
+
|
|
21
|
+
def _find_calibration(hw_name: str, hw_t_gate_ns: float, hw_p_phys: float,
|
|
22
|
+
calibrations: dict):
|
|
23
|
+
"""
|
|
24
|
+
Localiza a CalibratedHardware correspondente a um HardwareProfile.
|
|
25
|
+
HardwareProfile.name é escolhido livremente pelo usuário e não tem
|
|
26
|
+
relação garantida com as chaves de `calibrations` (ex.: HARDWARE_PROFILES
|
|
27
|
+
usa "IBM_Eagle_r3", mas um usuário pode nomear seu HardwareProfile
|
|
28
|
+
"IBM_Eagle"). Por isso, primeiro tenta casar por nome; se falhar, casa
|
|
29
|
+
pelas características numéricas (t_gate_ns e p_phys), que é como
|
|
30
|
+
HardwareProfile costuma ser derivado de uma CalibratedHardware na prática.
|
|
31
|
+
"""
|
|
32
|
+
if hw_name in calibrations:
|
|
33
|
+
return calibrations[hw_name]
|
|
34
|
+
for cal in calibrations.values():
|
|
35
|
+
if (math.isclose(cal.t_2q_ns, hw_t_gate_ns, rel_tol=1e-6)
|
|
36
|
+
and math.isclose(cal.p_phys, hw_p_phys, rel_tol=1e-6)):
|
|
37
|
+
return cal
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def rank(compare_result: dict,
|
|
42
|
+
hardware_calibrations: dict = None,
|
|
43
|
+
weight_qubits: float = 0.5,
|
|
44
|
+
weight_time: float = 0.3,
|
|
45
|
+
weight_fidelity: float = 0.2) -> list[Recommendation]:
|
|
46
|
+
"""
|
|
47
|
+
Rankeia combinações hardware+código por score ponderado.
|
|
48
|
+
Apenas combinações viáveis entram no ranking.
|
|
49
|
+
Weights: soma deve ser 1.0 (normaliza internamente se não for).
|
|
50
|
+
|
|
51
|
+
Se `hardware_calibrations` for fornecido (dict nome→CalibratedHardware,
|
|
52
|
+
ex. HARDWARE_PROFILES), combinações que violem o limite de T1
|
|
53
|
+
(t_circuit >= 0.5×T1) são excluídas do ranking.
|
|
54
|
+
"""
|
|
55
|
+
if abs(weight_qubits + weight_time + weight_fidelity - 1.0) > 1e-9:
|
|
56
|
+
total = weight_qubits + weight_time + weight_fidelity
|
|
57
|
+
weight_qubits /= total
|
|
58
|
+
weight_time /= total
|
|
59
|
+
weight_fidelity /= total
|
|
60
|
+
|
|
61
|
+
prof: CircuitProfile = compare_result["circuit_profile"]
|
|
62
|
+
hardware_profiles: dict = compare_result.get("hardware_profiles", {})
|
|
63
|
+
candidatos = []
|
|
64
|
+
|
|
65
|
+
for hw_name, code_results in compare_result["results"].items():
|
|
66
|
+
cal = None
|
|
67
|
+
if hardware_calibrations:
|
|
68
|
+
hw = hardware_profiles.get(hw_name)
|
|
69
|
+
if hw is not None:
|
|
70
|
+
cal = _find_calibration(
|
|
71
|
+
hw_name, hw.t_gate_ns, hw.p_phys, hardware_calibrations
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
for r in code_results:
|
|
75
|
+
if not r.feasible:
|
|
76
|
+
continue
|
|
77
|
+
|
|
78
|
+
if cal is not None and r.gate_overhead_per_logical:
|
|
79
|
+
check = cal.t1_constraint(
|
|
80
|
+
n_physical_gates=prof.n_physical_gates,
|
|
81
|
+
gate_overhead=r.gate_overhead_per_logical,
|
|
82
|
+
)
|
|
83
|
+
if not check["feasible"]:
|
|
84
|
+
continue
|
|
85
|
+
|
|
86
|
+
candidatos.append((hw_name, r))
|
|
87
|
+
|
|
88
|
+
if not candidatos:
|
|
89
|
+
return []
|
|
90
|
+
|
|
91
|
+
# Normalizar métricas para [0,1]
|
|
92
|
+
qubits_vals = [r.total_physical_qubits for _, r in candidatos]
|
|
93
|
+
time_vals = [r.execution_time_us for _, r in candidatos]
|
|
94
|
+
fid_vals = [r.fidelity_circuit for _, r in candidatos]
|
|
95
|
+
|
|
96
|
+
q_min, q_max = min(qubits_vals), max(qubits_vals)
|
|
97
|
+
t_min, t_max = min(time_vals), max(time_vals)
|
|
98
|
+
f_min, f_max = min(fid_vals), max(fid_vals)
|
|
99
|
+
|
|
100
|
+
def norm(v, lo, hi):
|
|
101
|
+
return 0.0 if hi == lo else (v - lo) / (hi - lo)
|
|
102
|
+
|
|
103
|
+
recommendations = []
|
|
104
|
+
for hw_name, r in candidatos:
|
|
105
|
+
q_norm = norm(r.total_physical_qubits, q_min, q_max)
|
|
106
|
+
t_norm = norm(r.execution_time_us, t_min, t_max)
|
|
107
|
+
f_norm = 1 - norm(r.fidelity_circuit, f_min, f_max) # inverso: maior fid = menor custo
|
|
108
|
+
|
|
109
|
+
score = (weight_qubits * q_norm
|
|
110
|
+
+ weight_time * t_norm
|
|
111
|
+
+ weight_fidelity * f_norm)
|
|
112
|
+
|
|
113
|
+
# Bottleneck: qual fator domina?
|
|
114
|
+
contributions = {
|
|
115
|
+
"qubits": weight_qubits * q_norm,
|
|
116
|
+
"tempo": weight_time * t_norm,
|
|
117
|
+
"fidelidade":weight_fidelity * f_norm,
|
|
118
|
+
}
|
|
119
|
+
bottleneck = max(contributions, key=contributions.get)
|
|
120
|
+
bottleneck_pct = contributions[bottleneck] / score * 100 if score > 0 else 0
|
|
121
|
+
|
|
122
|
+
recommendations.append(Recommendation(
|
|
123
|
+
rank=0, # preenchido abaixo
|
|
124
|
+
hardware=hw_name,
|
|
125
|
+
code=r.code_name,
|
|
126
|
+
total_physical_qubits=r.total_physical_qubits,
|
|
127
|
+
execution_time_us=r.execution_time_us,
|
|
128
|
+
fidelity_circuit=r.fidelity_circuit,
|
|
129
|
+
score=score,
|
|
130
|
+
bottleneck=f"{bottleneck} ({bottleneck_pct:.0f}% do score)",
|
|
131
|
+
))
|
|
132
|
+
|
|
133
|
+
recommendations.sort(key=lambda x: x.score)
|
|
134
|
+
for i, rec in enumerate(recommendations):
|
|
135
|
+
rec.rank = i + 1
|
|
136
|
+
|
|
137
|
+
return recommendations
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def print_report(compare_result: dict, recommendations: list[Recommendation]):
|
|
141
|
+
"""Imprime relatório completo."""
|
|
142
|
+
prof = compare_result["circuit_profile"]
|
|
143
|
+
print(f"\n{'═'*72}")
|
|
144
|
+
print(f" RELATÓRIO AutoQ QEC Estimator")
|
|
145
|
+
print(f"{'═'*72}")
|
|
146
|
+
print(f" Circuito: {prof.n_logical_qubits} qubits lógicos | "
|
|
147
|
+
f"{prof.n_physical_gates} portas físicas (CX+U) | "
|
|
148
|
+
f"profundidade {prof.depth_physical}")
|
|
149
|
+
print(f" CX: {prof.cx_count} U: {prof.t_count}")
|
|
150
|
+
|
|
151
|
+
print(f"\n {'#':>3} {'Hardware':<20} {'Código':<22} "
|
|
152
|
+
f"{'Qubits':>8} {'Tempo(µs)':>12} {'Fidelidade':>11} "
|
|
153
|
+
f"{'Score':>7} Gargalo")
|
|
154
|
+
print(f" {'─'*100}")
|
|
155
|
+
|
|
156
|
+
for r in recommendations:
|
|
157
|
+
marker = " ◀ MELHOR" if r.rank == 1 else ""
|
|
158
|
+
print(f" {r.rank:>3} {r.hardware:<20} {r.code:<22} "
|
|
159
|
+
f"{r.total_physical_qubits:>8} {r.execution_time_us:>12.2f} "
|
|
160
|
+
f"{r.fidelity_circuit:>11.4f} {r.score:>7.4f} "
|
|
161
|
+
f"{r.bottleneck}{marker}")
|
|
162
|
+
|
|
163
|
+
if recommendations:
|
|
164
|
+
best = recommendations[0]
|
|
165
|
+
print(f"\n RECOMENDAÇÃO: {best.hardware} + {best.code}")
|
|
166
|
+
print(f" → {best.total_physical_qubits} qubits físicos | "
|
|
167
|
+
f"{best.execution_time_us:.2f} µs | "
|
|
168
|
+
f"fidelidade {best.fidelity_circuit:.4f}")
|
|
169
|
+
print(f" → Gargalo principal: {best.bottleneck}")
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: autoq-qec
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Multi-code QEC resource estimator for arbitrary Qiskit circuits
|
|
5
|
+
Author-email: Ronaldo Rodrigues <Ronaldoengenhariadacomputacao@hotmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/Ronaldoengenhariadacomputacao/autoq-qec
|
|
8
|
+
Project-URL: Issues, https://github.com/Ronaldoengenhariadacomputacao/autoq-qec/issues
|
|
9
|
+
Project-URL: ORCID, https://orcid.org/0009-0006-7449-1190
|
|
10
|
+
Keywords: quantum,qec,surface-code,fault-tolerant,qiskit
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: qiskit>=1.0.0
|
|
22
|
+
Requires-Dist: numpy>=1.24
|
|
23
|
+
Provides-Extra: ibm
|
|
24
|
+
Requires-Dist: qiskit-ibm-runtime>=0.20; extra == "ibm"
|
|
25
|
+
Provides-Extra: sim
|
|
26
|
+
Requires-Dist: qiskit-aer>=0.14; extra == "sim"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# AutoQ QEC Estimator
|
|
33
|
+
|
|
34
|
+
[](https://doi.org/10.5281/zenodo.21327566)
|
|
35
|
+
|
|
36
|
+
**Multi-code fault-tolerant quantum error correction estimator for arbitrary Qiskit circuits.**
|
|
37
|
+
|
|
38
|
+
Given any Qiskit circuit and a set of hardware profiles, AutoQ QEC returns a ranked comparison of QEC codes (Surface Code, Floquet Code, Bacon-Shor, Steane [[7,1,3]]) with physically grounded resource estimates: physical qubit count, execution time, and circuit fidelity.
|
|
39
|
+
|
|
40
|
+
## What this does that nothing else does
|
|
41
|
+
|
|
42
|
+
| Tool | Multi-code | Arbitrary circuit | Analytic model | Hardware-agnostic |
|
|
43
|
+
|---|---|---|---|---|
|
|
44
|
+
| Azure Resource Estimator | ❌ Surface Code only | ✅ | ✅ | ❌ Azure only |
|
|
45
|
+
| stim | ✅ | ❌ needs rewrite | ❌ simulation | ❌ |
|
|
46
|
+
| qiskit-qec | ✅ | ❌ no estimator | ❌ | ✅ |
|
|
47
|
+
| **AutoQ QEC** | ✅ | ✅ | ✅ | ✅ |
|
|
48
|
+
|
|
49
|
+
## Install
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install autoq-qec
|
|
53
|
+
# With IBM Quantum integration:
|
|
54
|
+
pip install "autoq-qec[ibm,sim]"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quickstart
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from qiskit import QuantumCircuit
|
|
61
|
+
from autoq_qec import compare, rank
|
|
62
|
+
from autoq_qec import HARDWARE_PROFILES, CalibratedHardware, HardwareProfile
|
|
63
|
+
|
|
64
|
+
# Any Qiskit circuit
|
|
65
|
+
circuit = QuantumCircuit(4)
|
|
66
|
+
circuit.h(0); circuit.cx(0,1); circuit.cx(1,2); circuit.cx(2,3)
|
|
67
|
+
|
|
68
|
+
# Hardware profiles (built-in or custom)
|
|
69
|
+
hardwares = [
|
|
70
|
+
HardwareProfile("IBM_Eagle", t_gate_ns=391, p_phys=0.0062, topology="heavy-hex"),
|
|
71
|
+
HardwareProfile("IBM_Heron", t_gate_ns=100, p_phys=0.003, topology="heavy-hex"),
|
|
72
|
+
HardwareProfile("Quantinuum_H2", t_gate_ns=100e3, p_phys=0.00029,topology="all-to-all"),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
# One call — returns all codes × all hardwares
|
|
76
|
+
result = compare(circuit, hardwares, fidelity_target=0.99)
|
|
77
|
+
|
|
78
|
+
# Pass hardware_calibrations to exclude combinations that violate T1
|
|
79
|
+
# (t_circuit >= 0.5×T1) — matches by name or by (t_gate_ns, p_phys)
|
|
80
|
+
recommendations = rank(result, hardware_calibrations=HARDWARE_PROFILES)
|
|
81
|
+
|
|
82
|
+
for r in recommendations[:3]:
|
|
83
|
+
print(f"#{r.rank} {r.hardware} + {r.code}: "
|
|
84
|
+
f"{r.total_physical_qubits}q, {r.execution_time_us:.1f}µs, "
|
|
85
|
+
f"fidelity={r.fidelity_circuit:.4f}")
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## With real IBM calibration data
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from autoq_qec.real_hardware import from_ibm_backend, noise_model_from_ibm
|
|
92
|
+
|
|
93
|
+
# Pulls today's calibration — T1, T2, CX error per qubit pair
|
|
94
|
+
hw = from_ibm_backend("ibm_brisbane", token="YOUR_IBM_TOKEN")
|
|
95
|
+
|
|
96
|
+
# Simulate locally with real noise model (no queue, no cost)
|
|
97
|
+
sim = noise_model_from_ibm("ibm_brisbane", token="YOUR_IBM_TOKEN")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Physical models
|
|
101
|
+
|
|
102
|
+
| Code | Model | Reference |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| Surface Code | $p_L \approx A(p/p_{th})^{(d+1)/2}$, $q=2d^2-1$, overhead $=d^3$ | Fowler et al., PRA 86, 032324 (2012) |
|
|
105
|
+
| Floquet Code | $p_L \approx 0.07(p/p_{th})^{(d+1)/2}$, $q=4d^2+8(d-1)$, overhead $=\lfloor d/2\rfloor$ | Gidney & Fowler, arXiv:2202.11829 |
|
|
106
|
+
| Bacon-Shor | $p_L \approx (p/p_{th})^d$, $q=d^2$ | Aliferis & Cross (2007) |
|
|
107
|
+
| Steane [[7,1,3]] | $p_L \approx 21p^2$, $q=13$ | Steane, PRL 77, 793 (1996) |
|
|
108
|
+
|
|
109
|
+
Thresholds are enforced: `p ≥ p_th` raises `ValueError` — no silent wrong results.
|
|
110
|
+
|
|
111
|
+
## Algorithm Estimator
|
|
112
|
+
|
|
113
|
+
Order-of-magnitude T-count estimates for known algorithms, without building the full circuit:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from autoq_qec import AlgorithmEstimator
|
|
117
|
+
|
|
118
|
+
est = AlgorithmEstimator.shor(2048)
|
|
119
|
+
print(est.t_count_estimate, est.t_count_uncertainty) # ±5x — build the real circuit for precise numbers
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Covers `shor`, `grover`, `qft`, `vqe`. These are rough estimates (±2×–±10× depending on the algorithm) — use `extract_circuit_profile()` on a real circuit whenever possible.
|
|
123
|
+
|
|
124
|
+
## Test
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
pytest tests/ -v # 49 tests, all verify physics not arithmetic
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## What the tests check (unlike most QEC tools)
|
|
131
|
+
|
|
132
|
+
- `p ≥ threshold` raises `ValueError`, not wrong overhead
|
|
133
|
+
- `d` is always odd for Surface Code (rotated lattice requirement)
|
|
134
|
+
- `p_L ≤ p_L_target` guaranteed after distance selection
|
|
135
|
+
- Noisier hardware requires larger `d` (monotonicity)
|
|
136
|
+
- Circuit with 0 gates raises `ValueError` (destroyed by transpiler)
|
|
137
|
+
- Fidelity scales correctly with `t_gate`
|
|
138
|
+
|
|
139
|
+
## Author
|
|
140
|
+
|
|
141
|
+
Ronaldo Rodrigues — ORCID: [0009-0006-7449-1190](https://orcid.org/0009-0006-7449-1190)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
autoq_qec/__init__.py,sha256=hTvcez1cI0hSakkhGFh0knh0EZ0F0ZuwOrXVSBNgn5Y,686
|
|
2
|
+
autoq_qec/algorithm_estimator.py,sha256=0wwZLhzkwKotL_iQ9fuzlI5fuB4ND5MMhA8Mt-elS8w,3640
|
|
3
|
+
autoq_qec/qec_estimator.py,sha256=YhH_FqKWQt124N0vD7-Ng5wi5XLVW_GciFDTZkVbX30,14120
|
|
4
|
+
autoq_qec/real_hardware.py,sha256=Erim0NKVB3YCIcoMiu5cQUPDaMoHvrlOMtUctUb5iwU,8334
|
|
5
|
+
autoq_qec/recommender.py,sha256=iX_2mzfPDF82hng_EzXkrsvbliwDKK_Q-FeGNjK5ac4,6508
|
|
6
|
+
autoq_qec-0.2.1.dist-info/licenses/LICENSE,sha256=07NCoHQyczcX_ddSsm3KlW3F-iH9z0w8PK3MlGQOGPc,1074
|
|
7
|
+
autoq_qec-0.2.1.dist-info/METADATA,sha256=lZrYf7rmsVIlIQzN6Sktic6Sq1qAKhY7Xi9R7CXaprM,5595
|
|
8
|
+
autoq_qec-0.2.1.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
9
|
+
autoq_qec-0.2.1.dist-info/top_level.txt,sha256=ccgvJNSGIXy4sas82h-YqoqnIZyZxb7R1F78_zVXF6k,10
|
|
10
|
+
autoq_qec-0.2.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ronaldo Rodrigues
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
autoq_qec
|