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 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)")
@@ -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
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.21327566.svg)](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,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -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