Multi-code QEC resource estimator for arbitrary Qiskit circuits
Project description
AutoQ QEC Estimator
Multi-code fault-tolerant quantum error correction estimator for arbitrary Qiskit circuits.
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.
What this does that nothing else does
| Tool | Multi-code | Arbitrary circuit | Analytic model | Qiskit-native |
|---|---|---|---|---|
| Azure Resource Estimator | ✅ Surface + Floquet + custom | ✅ | ✅ | ⚠️ accepts Qiskit input, compiles via QIR |
| stim | ✅ | ❌ needs rewrite | ❌ simulation | ❌ |
| qiskit-qec | ✅ | ❌ no estimator | ❌ | ✅ |
| AutoQ QEC | ✅ | ✅ | ✅ | ✅ |
Install
pip install autoq-qec
# With IBM Quantum integration:
pip install "autoq-qec[ibm,sim]"
Quickstart
from qiskit import QuantumCircuit
from autoq_qec import compare, rank
from autoq_qec import HARDWARE_PROFILES, CalibratedHardware, HardwareProfile
# Any Qiskit circuit
circuit = QuantumCircuit(4)
circuit.h(0); circuit.cx(0,1); circuit.cx(1,2); circuit.cx(2,3)
# Hardware profiles: built-in (recommended — carries real readout_error/T1_us/
# T2_us automatically) or custom, built by hand for hardware not in the list.
hardwares = [HardwareProfile.from_calibrated(hw) for hw in [
HARDWARE_PROFILES["IBM_Eagle_r3"],
HARDWARE_PROFILES["IBM_Heron_r2"],
HARDWARE_PROFILES["Quantinuum_H2"],
]]
# Custom hardware (not in HARDWARE_PROFILES): build HardwareProfile by hand.
# readout_error/T1_us/T2_us are all optional (default 0.0/None) but
# recommended — omitting readout_error overestimates fidelity (see "Physical
# models" below); omitting T2_us skips the decoherence penalty entirely
# (see "Two-tier ranking" below for why that matters for rank()).
# hardwares = [HardwareProfile("MyChip", t_gate_ns=200, p_phys=0.004,
# topology="heavy-hex", readout_error=0.01, T2_us=150)]
# One call — returns all codes × all hardwares
result = compare(circuit, hardwares, fidelity_target=0.99)
# Pass hardware_calibrations to exclude combinations that violate T1
# (t_circuit >= 0.5×T1) — matches by name or by (t_gate_ns, p_phys)
recommendations = rank(result, hardware_calibrations=HARDWARE_PROFILES)
for r in recommendations[:3]:
print(f"#{r.rank} {r.hardware} + {r.code}: "
f"{r.total_physical_qubits}q, {r.execution_time_us:.1f}µs, "
f"fidelity={r.fidelity_circuit:.4f}, meets_target={r.meets_fidelity_target}")
Using real calibrated T2_us (via from_calibrated() above), this particular circuit/hardware combination doesn't actually clear fidelity_target=0.99 on any of the 3 built-in profiles — meets_target prints False for all of them. That's the point of meets_fidelity_target: it tells you honestly when nothing tested delivers what you asked for, instead of silently ranking the least-bad option as if it were a real answer. Try fidelity_target=0.9 or a deeper/wider circuit to see meets_target=True results.
rank() weights
rank() combines three different metrics into a single score, using weights you choose. Each weight controls how much that metric counts against the other two:
| Parameter | Default | Controls | Increasing it favors... |
|---|---|---|---|
weight_qubits |
0.5 |
Total physical qubits the hardware+code combination needs | Combinations that save qubits, even if slower or with less fidelity margin |
weight_time |
0.3 |
Execution time (µs) the combination takes | Fast combinations, even if they need more qubits |
weight_fidelity |
0.2 |
Fidelity margin among combinations that already meet fidelity_target (see "Two-tier ranking" below — this weight never lets a combination below your target outrank one that meets it) |
Combinations with more fidelity safety margin, even if costlier in qubits/time |
Weights don't need to sum to 1.0 — they're normalized automatically (the ratio between them is what matters, not the absolute values). The default weights genuinely change the outcome — verified empirically: the same circuit/hardware list ranked with weight_time=0.9 instead of the default reordered the ranking substantially (a combination that ranked #3 by default dropped out of the top 5 entirely, replaced by a faster-but-more-qubit-hungry option). Qubits, time, and fidelity are different units with no natural common scale — the weights are the exchange rate between them, and the right rate depends on your actual constraints (e.g., queue-time-billed access cares more about time; qubit-scarce hardware cares more about qubit count). There is no single "objective" ranking — pass weights that reflect your real situation.
# 1. Default: prioritize qubit savings
recommendations = rank(result) # weight_qubits=0.5, weight_time=0.3, weight_fidelity=0.2
# 2. Prioritize execution speed
recommendations = rank(result, weight_qubits=0.1, weight_time=0.8, weight_fidelity=0.1)
# 3. Prioritize fidelity margin
recommendations = rank(result, weight_qubits=0.1, weight_time=0.1, weight_fidelity=0.8)
# 4. Balanced: all three metrics count equally
recommendations = rank(result, weight_qubits=1, weight_time=1, weight_fidelity=1)
# 5. Operational cost (qubits + time); fidelity only needs to clear the target
recommendations = rank(result, weight_qubits=0.45, weight_time=0.45, weight_fidelity=0.1)
| # | Preset | When to use |
|---|---|---|
| 1 | Default (qubit savings) | NISQ hardware with few physical qubits available — the most common constraint today |
| 2 | Speed | You pay for queue/execution time, or decoherence over time is the bigger concern |
| 3 | Fidelity margin | Your use case can't tolerate being right at the edge — you want extra safety margin, not just barely clearing the bar |
| 4 | Balanced | You don't yet know which constraint matters most and want a neutral starting point |
| 5 | Operational cost | Fidelity is already guaranteed by fidelity_target (a minimum requirement, not something to maximize) — what's left to decide is qubits vs. time |
Two-tier ranking: meets_fidelity_target
A QEC code being "feasible" (a valid distance d exists for your p_phys) only guarantees the gate-error contribution to p_L stays under budget. It does not guarantee the final fidelity_circuit — which also multiplies in readout_error and, if you set T2_us, a decoherence penalty (exp(-execution_time_us / T2_us)) — actually reaches your fidelity_target. A slow combination (e.g. deep QEC syndrome-extraction overhead on a long-t_gate_ns hardware) can be "feasible" by the gate-error criterion alone while its real fidelity, once decoherence is accounted for, has collapsed to near zero.
Each Recommendation (and the underlying CodeResult) has a meets_fidelity_target: bool field for exactly this: True only when fidelity_circuit >= fidelity_target. rank() uses it to build two tiers, without excluding anything:
- Tier A (
meets_fidelity_target=True): ranked normally, by the weighted score above (the 5 presets, weights table).#1only ever comes from this tier. - Tier B (
meets_fidelity_target=False): never outranks Tier A, regardless of weights — qubits/time trade-offs are meaningless for a combination that doesn't deliver the fidelity you asked for. Sorted purely byfidelity_circuitdescending (closest to the target first), withbottleneckexplaining the shortfall (e.g."NÃO atinge fidelity_target=0.9900 (fidelidade real: 0.0000) — ...").
If Tier A is empty (nothing tested actually reaches your target), rank() still returns Tier B — check meets_fidelity_target before trusting #1 as an actual answer to your request, not just "the best of what was tried."
recommendations = rank(result)
if not recommendations[0].meets_fidelity_target:
print("Nenhuma combinação testada atinge o fidelity_target pedido.")
Getting real decoherence data: HardwareProfile.from_calibrated()
T2_us defaults to None (no decoherence penalty applied) — and it's easy to lose accidentally: hand-building a HardwareProfile (as the Quickstart above does) by copying only t_gate_ns/p_phys/topology/readout_error silently drops T2_us/T1_us even when real values exist. The built-in HARDWARE_PROFILES (CalibratedHardware objects) already have real T2_us for every entry — use from_calibrated() to carry all of it over at once instead of copying fields by hand:
from autoq_qec import HardwareProfile
from autoq_qec.real_hardware import HARDWARE_PROFILES
hw = HardwareProfile.from_calibrated(HARDWARE_PROFILES["IBM_Heron_r2"])
# hw.T2_us, hw.T1_us, hw.readout_error are all populated — nothing to remember
If you build a HardwareProfile by hand and leave readout_error/T1_us/T2_us at their defaults, compare()/estimate() emit a UserWarning naming exactly which of the three are still missing (with a short description of what each one does) — set one and it drops out of the message; set (or load via from_calibrated()) all three and the warning stops. Zero or negative values for t_gate_ns, p_phys, T1_us, T2_us, or t_meas_ns (and readout_error outside [0, 1)) raise ValueError immediately — they have no physical meaning, and previously produced silently wrong results (t_gate_ns=0 gave execution_time_us=0.0; readout_error=5.0 gave fidelity_circuit above 1.0).
rank_by_metric() — when weights can't show you the trade-off
When one hardware+code combination beats every other candidate on every metric at once (qubits, time, and fidelity — a "Pareto-dominant" option), no weight combination in rank() can ever pick anything else as #1: a weighted sum can't rank a dominated option above a dominant one. That's mathematically correct, but it can hide real trade-offs among the non-dominant options. rank_by_metric() sidesteps this by ranking each metric independently, with no weighting at all:
from autoq_qec import rank_by_metric
por_metrica = rank_by_metric(result)
for metrica, lista in por_metrica.items():
top = lista[0]
print(f"Best by {metrica}: {top.hardware}/{top.code} — "
f"{top.total_physical_qubits}q, {top.execution_time_us}µs, fid={top.fidelity_circuit:.5f}")
Real example from this package's own test suite (QFT on 6 qubits, fidelity_target=0.999, comparing IBM Eagle/Heron and Quantinuum H2) — rank() converges to the same #1 under all 5 presets above, yet the per-metric breakdown reveals a real trade-off hidden behind that single "winner":
| Metric | Winner | Qubits | Time | Fidelity |
|---|---|---|---|---|
| Qubits | Quantinuum_H2/Bacon-Shor |
294 | 680,400 µs | 0.99934 |
| Time | IBM_Heron/Floquet Code |
6,072 | 77.7 µs | 0.99949 |
| Fidelity | Quantinuum_H2/Floquet Code |
2,328 | 32,400 µs | 0.99957 |
A ~78× time spread and a ~20× qubit spread between the extremes — none of that is visible if you only look at rank()'s combined #1. Use rank_by_metric() first to see the actual shape of your trade-off space, then use rank() with weights once you know which end of that space you actually care about.
rank_by_metric() inherits the same two-tier logic as rank(): within Tier A (meets_fidelity_target=True), each list is genuinely sorted by its own metric — ["qubits"] by qubit count, ["tempo"] by time. But if nothing tested reaches fidelity_target, all three lists fall entirely into Tier B and get sorted by fidelity_circuit instead, regardless of the list's name — check meets_fidelity_target on the entries before assuming ["qubits"][0] is actually the qubit-cheapest option.
Comparing across vendors — weights don't guarantee a "neutral" answer
rank()'s weights let you pick what matters, but that's configurability, not neutrality — the ranking still reflects each hardware's real physical numbers (p_phys, T1_us, T2_us, gate times), and different circuits genuinely favor different vendors depending on which weight you emphasize. There's no fixed pattern where one vendor "usually wins" a given preset — verified empirically: ranking a 4-qubit GHZ circuit across IBM_Heron_r2, Quantinuum_H2, and IonQ_Aria (all via HardwareProfile.from_calibrated()) puts Quantinuum_H2 at #1 under the default preset, the fidelity-focused preset (weight_fidelity=0.8), and the speed-focused preset (weight_time=0.8) — not the "IBM wins by default, ion-trap wins on fidelity" pattern you might expect. A different circuit can flip this entirely.
hardwares = [HardwareProfile.from_calibrated(hw) for hw in [
HARDWARE_PROFILES["IBM_Heron_r2"],
HARDWARE_PROFILES["Quantinuum_H2"],
HARDWARE_PROFILES["IonQ_Aria"],
]]
result = compare(circuit, hardwares, fidelity_target=0.9)
for r in rank(result, weight_qubits=0.1, weight_time=0.1, weight_fidelity=0.8)[:3]:
print(f"#{r.rank} {r.hardware} + {r.code}: {r.total_physical_qubits}q, fid={r.fidelity_circuit:.4f}")
The practical takeaway: always compare against your actual circuit with multiple real hardware profiles (HARDWARE_PROFILES, not just one vendor) before trusting a #1 — and pick weights that match your real constraints (see the presets table above), not weights chosen to make a particular hardware win.
Variational circuits (VQE, QAOA, etc.) must have parameters bound first. real_amplitudes(), efficient_su2(), QAOAAnsatz, and similar templates carry symbolic parameters until you call assign_parameters() — T-count depends on the actual rotation angles, which don't exist in a symbolic circuit. Passing an unbound circuit raises a clear ValueError (as of 3.2.2):
from qiskit.circuit.library import real_amplitudes
import numpy as np
template = real_amplitudes(4, reps=2)
rng = np.random.default_rng(42)
circuit = template.assign_parameters(rng.uniform(0, 2*np.pi, template.num_parameters))
result = compare(circuit, hardwares, fidelity_target=0.99) # now works
With real IBM calibration data
from autoq_qec.real_hardware import from_ibm_backend, noise_model_from_ibm
# Pulls today's calibration — T1, T2, CX error per qubit pair
hw = from_ibm_backend("ibm_brisbane", token="YOUR_IBM_TOKEN")
# Simulate locally with real noise model (no queue, no cost)
sim = noise_model_from_ibm("ibm_brisbane", token="YOUR_IBM_TOKEN")
Physical models
| Code | Model | Reference |
|---|---|---|
| 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) |
| Floquet Code | $p_L \approx 0.07(p/p_{th})^{(d+1)/2}$, $q=4d^2+8(d-1)$, overhead $=\lfloor d/2\rfloor$ | Paetznick, Knapp, Delfosse, Bauer, Haah, Hastings & da Silva, arXiv:2202.11829 |
| Bacon-Shor | $p_L \approx (p/p_{th})^d$, $q=d^2$, overhead $=d\cdot2(d-1)$ | Aliferis & Cross, PRL 98, 220502 (2007); overhead: Li, Miller & Brown, arXiv:1804.01127 (2018) + Aliferis PhD thesis, quant-ph/0703230, p.93 |
| Steane [[7,1,3]] | $p_L \approx 21p^2$, $q=13$, overhead $=6$ (fixo) | Steane, PRL 77, 793 (1996) |
Thresholds are enforced: p ≥ p_th raises ValueError — no silent wrong results.
Majorana / topological qubits
The Floquet Code model above is already Majorana-native, even though nothing in its implementation says so: the cited paper (Paetznick, Knapp, Delfosse, Bauer, Haah, Hastings & da Silva, arXiv:2202.11829, Microsoft authors) defines its physical error rate p as "each two-qubit measurement fails independently with probability p" — the native operation for measurement-only Majorana zero mode (MZM) qubits, not a gate. p_th=0.01/A=0.07 were checked number-for-number against the paper's own formula. No change to extract_circuit_profile()'s gate-count-based pipeline was needed for this to hold — a counted 2-qubit operation already serves as the right proxy for a 2-qubit measurement, whichever physical mechanism actually implements it.
What Majorana qubits need that gate-based hardware doesn't: topological protection covers Clifford operations (measurement) but not the physical T-gate, which has a separate, much higher error rate. HardwareProfile.p_t_state (and CalibratedHardware.p_t_state) exists for exactly this — an optional field, distinct from p_phys, used only by magic state distillation (model_magic_state_distillation=True) as the T-state injection error Q_0. p_t_state=None (default) preserves the old behavior (Q_0 = p_phys) for every hardware that doesn't need the distinction.
from autoq_qec import HardwareProfile
from autoq_qec.real_hardware import HARDWARE_PROFILES
hw = HardwareProfile.from_calibrated(
HARDWARE_PROFILES["Majorana_MS_ResourceEstimator_illustrative"]
)
print(hw.p_phys, hw.p_t_state) # 1e-05 0.015 -- distinct values
This built-in entry is illustrative, not measured hardware — the name says so on purpose. It's built from Microsoft's own public planning targets for the Azure Quantum Resource Estimator's qdk.qre.models.qubits.Majorana class (error_rate default 1e-5, 1µs fixed time for every measurement/T-gate, "realistic"-regime non-Clifford T-gate error of 1.5% — Microsoft Learn, citing Karzig et al. arXiv:1610.05289, Kitaev cond-mat/0010440, Das Sarma et al. arXiv:1501.02813), plus the ~20-second mean qubit lifetime Microsoft reported for its "Majorana 2" chip launch (Forbes, Jul 2026) used as the closest public analogue for the T1_us viability filter. As of that same coverage, two-qubit entangling/braiding operations had not yet been publicly demonstrated on real Majorana hardware — "two-qubit entangling gates, which are essential for quantum computing, have yet to be demonstrated" — and independent physicists remain skeptical of the platform's core claims (Nature, "Microsoft upgrades controversial quantum chip — researchers are still sceptical"). p_phys/p_t_state on this entry are Microsoft's own design targets, not measurements of a working chip — use it to explore the shape of the cost model, not as a performance prediction. This caveat is in the hardware's name field itself ("... — not measured hardware"), so it surfaces directly in any printed ranking table, not just here.
Microsoft has also published an older, distinct Majorana parameter pair in the Azure Quantum Resource Estimator itself (qubit_maj_ns_e4/qubit_maj_ns_e6, from Beverland, Kliuchnikov, Schoute et al., arXiv:2211.07629, Table 2 — 100ns operations, 5%/1% T-gate error at the 1e-4/1e-6 Clifford-error tiers, vs. this package's 1µs/1.5% from the newer qdk.qre Python API). Both are real, citable, Microsoft-published planning targets for the same underlying hardware concept — this package uses the newer API's numbers; if you need the older preset's numbers specifically, build a HardwareProfile by hand from Table 2 rather than assuming they match this entry.
Surface Code, Bacon-Shor, and Steane [[7,1,3]] are not validated for this physical error mechanism. Their thresholds come from gate-error literature (Fowler et al. PRA 86 032324 for Surface Code; Aliferis & Cross PRL 98 220502 for Bacon-Shor; Steane PRL 77 793) — a different physical error mechanism than the 2-qubit-measurement error Majorana qubits actually have. This is a documented gap, not a correction — there's no published data to recalibrate those three models against measurement-based error for topological qubits, so running them against a Majorana HardwareProfile extrapolates outside what their source literature covers.
Magic state distillation (opt-in). By default, total_physical_qubits/execution_time_us scale with n_physical_gates only and ignore T-gate count — physically inaccurate, since magic state distillation (needed for T-gates, not Clifford gates) is normally the dominant resource cost in real fault-tolerant quantum computing. Pass model_magic_state_distillation=True to compare()/estimate() to include it: each CodeResult gets magic_state_qubits, magic_state_factories, and magic_state_t_state_error populated, and both totals grow accordingly.
result = compare(circuit, hardwares, fidelity_target=0.99, model_magic_state_distillation=True)
The T-factory cost model (autoq_qec/distillation.py) implements the multi-round 15-to-1 distillation formulas from Beverland, Kliuchnikov, Schoute et al., "Assessing requirements to scale to practical quantum advantage", arXiv:2211.07629 (Appendix C, Table VI; Appendix E, Eqs. C1–C4, E4–E6) — validated against the paper's own worked examples (Table VII) as regression tests (tests/test_distillation.py), reproducing their exact numbers (qubits, time, output error) for both a 1-round and a 2-round factory. Two simplifications versus the paper, documented in the module docstring: the per-round factory search is greedy (cheapest distance meeting the round's error target) rather than a global optimum over a full factory catalog, and the physical T-state input error defaults to the hardware's Clifford error rate p_phys unless you pass HardwareProfile.p_t_state — a separate value for the physical T-gate error, relevant when it differs from the Clifford/measurement error rate (the paper's own motivating case is Majorana qubits — see "Majorana / topological qubits" below). Default is False — existing code is unaffected.
Fidelity formula: fidelity_circuit = (1 - p_L)^n_gates × (1 - readout_error)^n_logical_qubits × exp(-execution_time_us / T2_us). readout_error and T2_us are optional fields on HardwareProfile (default 0.0 / None, preserving the old formula if omitted).
Known limitation: mid-circuit measurement error is not modeled separately. readout_error represents end-of-circuit measurement error only. Real hardware (including Quantinuum's own datasheet, cited above) reports a distinct, usually different "mid-circuit measurement and reset cross-talk error" for measurements that happen mid-circuit — common in real fault-tolerant protocols using dynamic circuits (measurement + conditional reset). This package does not currently distinguish between the two — any measurement in your circuit, wherever it occurs, is treated as if it happens at the end. If your circuit relies heavily on mid-circuit measurement, treat readout_error's contribution to fidelity_circuit as an approximation, not a precise model of that specific error source.
This is an order-of-magnitude estimator, not a calibrated simulator. The table below validates the formula's shape — not a precise calibration — against the actual Quickstart circuit above, on real live calibration for 3 IBM backends (ibm_fez, ibm_marrakesh, ibm_kingston, pulled via QiskitRuntimeService, Open Plan) and 1 synthetic profile (Google Willow, HARDWARE_PROFILES["Google_Willow"], built from the published Nature 638 specs — Google has no public self-service hardware access). Two different, methodology-matched ground truths are used per column: the no-readout prediction is checked against state fidelity (density-matrix comparison, no measurement — readout error structurally can't appear here, so this is the right ground truth for that column); the with-readout prediction is checked against classical shot fidelity (real measurement, 8192 shots — the only way readout error actually manifests):
| Hardware | Empirical (state fidelity) | Predicted, no readout | Error | Empirical (shots) | Predicted, with readout | Error |
|---|---|---|---|---|---|---|
| ibm_fez (real) | 98.24% | 99.62% | 1.4% | 93.46% | 92.31% | 1.2% |
| ibm_marrakesh (real) | 98.99% | 99.62% | 0.6% | 97.04% | 88.43% | 8.9% |
| ibm_kingston (real) | 99.35% | 99.74% | 0.4% | 96.90% | 92.41% | 4.6% |
| Google Willow (synthetic) | 99.04% | 99.92% | 0.9% | 99.22% | 96.77% | 2.5% |
(Last measured 2026-07-16. Google_Willow's figures use a simplified synthetic noise model — depolarizing errors sized from p_1q_mean/p_2q_mean, no full thermal-relaxation channel — since there's no live backend to query; treat it as a rougher check than the 3 real rows.)
The no-readout column is consistently accurate (0.4%–1.4% error). The with-readout column is less precise (1.2%–8.9%) but still directionally correct — treat fidelity_circuit as an order-of-magnitude estimate, not a calibrated prediction. These exact percentages will not reproduce on a future run: IBM's live calibration drifts daily (this table is a snapshot, not a fixed benchmark), and no fixed random seed underlies the noisy simulation. If you need this validated against today's calibration, the methodology above is straightforward to rerun — pull CalibratedHardware via from_ibm_backend(), build a noisy AerSimulator via NoiseModel.from_backend(), and compare state_fidelity/shot-based classical fidelity against estimate()'s fidelity_circuit.
Algorithm Estimator
Order-of-magnitude T-count estimates for known algorithms, without building the full circuit:
from autoq_qec import AlgorithmEstimator
est = AlgorithmEstimator.shor(2048)
print(est.t_count_estimate, est.t_count_uncertainty) # ±5x — build the real circuit for precise numbers
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.
Hardware profiles
Includes Google_Willow (105q, Acharya et al., Nature 638, 964-971, 2025) and IBM_Heron_r3 (ibm_pittsburgh, Q4 2025), alongside IBM_Eagle_r3, IBM_Heron_r2, Quantinuum_H2, IonQ_Aria, Google_Sycamore — all with real T1_us/T2_us/readout_error. Use HardwareProfile.from_calibrated(HARDWARE_PROFILES["..."]) to turn any of them into a HardwareProfile for compare() (see "Getting real decoherence data" above).
Majorana_MS_ResourceEstimator_illustrative is also included, but it's a different kind of entry — a topological-qubit design target, not measured hardware. See "Majorana / topological qubits" above before using it.
Visualization
pip install "autoq-qec[viz]"
from autoq_qec.visualizer import plot_tradeoff
result = compare(circuit, hardwares, fidelity_target=0.99)
plot_tradeoff(result, output="tradeoff.png") # log-log qubits × time, color = fidelity
Test
pytest tests/ -v # 142 tests, all verify physics not arithmetic
What the tests check (unlike most QEC tools)
p ≥ thresholdraisesValueError, not wrong overheaddis always odd for Surface Code (rotated lattice requirement)p_L ≤ p_L_targetguaranteed after distance selection (gate-error contribution only — see "Two-tier ranking" above for what this does not guarantee about the finalfidelity_circuit)- Noisier hardware requires larger
d(monotonicity) estimate()raisesValueErroron a zero-gateCircuitProfile(e.g. destroyed by transpiler) — notextract_circuit_profile()itself, which returns a valid zero-gate profile for an actually empty circuitextract_circuit_profile()raisesValueErroron circuits with unbound parameters — see "Variational circuits" above- Fidelity scales correctly with
t_gate meets_fidelity_target=Falsewhen decoherence (T2_us) collapsesfidelity_circuitbelowfidelity_target, even though the code is stillfeasibleby the gate-error criterion alonerank()never lets a combination belowfidelity_targetoutrank one that meets it, regardless of weights
Author
Ronaldo Rodrigues — ORCID: 0009-0006-7449-1190
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file autoq_qec-3.4.1.tar.gz.
File metadata
- Download URL: autoq_qec-3.4.1.tar.gz
- Upload date:
- Size: 67.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9967a3b4e3cd1ca073637aacf4b73d1d330e513ab7970c1869d43fb489b59285
|
|
| MD5 |
4f6a088bc39ec4ddd98fbdd8ed1901a9
|
|
| BLAKE2b-256 |
b3a9a474d9cde3a2bcab2d6467427a9c4c907c6f147453d8011f0ddf038206bd
|
File details
Details for the file autoq_qec-3.4.1-py3-none-any.whl.
File metadata
- Download URL: autoq_qec-3.4.1-py3-none-any.whl
- Upload date:
- Size: 40.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16ba18bd0b1384329b6b67b5db30050ff9b976dd75787684ad9c46539ce2785a
|
|
| MD5 |
56f481349c2916ac2d8fc5b5629ae8c7
|
|
| BLAKE2b-256 |
5ab4b53dc26c39cf5c28feb5cad12655205d8bc0cfc40d1a86fab63824f80f71
|