Strategy configuration¶
A strategy is represented by qbalance.StrategySpec. It is immutable and combines compilation, suppression, mitigation, cutting, and runtime knobs.
Fields¶
Field |
Type |
Default |
Notes |
|---|---|---|---|
|
|
|
Qiskit preset optimization level, |
|
|
|
Qiskit layout method or |
|
|
|
Qiskit routing method, commonly |
|
|
|
Qiskit translation method. |
|
|
|
Transpiler seed; must be non-negative. |
|
|
|
Twirls the two-qubit gates of the compiled circuit into |
|
|
|
Number of randomized instances for Pauli and/or measurement twirling; must be at least |
|
|
|
Adds a DD pass manager after compilation when supported. |
|
|
|
One of |
|
|
|
Flips each terminal measurement at random and records the flips, per instance ( |
|
|
|
Seed for suppression transforms; must be non-negative. Instance |
|
|
|
Enables M3 mitigation during execution workflows. |
|
|
|
Enables zero-noise extrapolation during execution workflows. |
|
|
|
Must be finite, sorted, include |
|
|
|
Must be non-negative generally and, when |
|
|
|
Cuts circuits wider than |
|
|
|
Required when |
|
|
|
Optional IBM Runtime-style level; valid values are |
Boolean values are rejected for integer-like fields even though Python treats bool as a subclass of int.
Unknown fields are rejected as well, so a misspelled key fails loudly instead of silently producing a default strategy.
How
qbalance_noise_awareplaces qubits. Physical qubits are scored by readout error, with T1/T2 relative to the backend median as a tie-breaker. On a backend with a coupling map, couplers calibrated as broken (error 1.0) are ignored and logical qubits are placed one at a time, the busiest first: each goes next to the partners it interacts with whenever a free neighbour exists, and among the qubits that need equally few routing hops, to the cheapest in error units (its readout error, the calibrated two-qubit gate error of the coupled pairs it uses, and, for a pair that must be routed, a SWAP’s three gate errors per extra hop plus the readout error of the qubit routing moves it onto). The placement is grown from the 24 most promising start qubits and the cheapest wins; the API reference gives the exact cost. It therefore keeps interacting qubits adjacent and routes around broken couplers: on thefake:ibm:sherbrookesnapshot an 8-qubit chain compiles at optimization level 2 to depth 29, 7 two-qubit gates, and estimated error 0.125, where the SABRE layout lands on error-1.0 couplers (estimated error 1.0). Without a coupling map the best-scoring qubits go to the busiest logical qubits.
How cutting is evaluated. Terminal measurements are split off (they define the observable:
Zon every measured qubit), the unitary part is cut withfind_cuts, partitioned, and expanded into every quasi-probability term exactly (num_samples=inf, so results are reproducible). Each subexperiment is compiled on its own; the candidate’sdepth,size,width,two_qubit_ops, andestimated_errorare the maxima over them (every subexperiment runs as a separate circuit),compile_time_sis the total, andcut_count,cut_types,sampling_overhead,num_subcircuits,subcircuit_widths, andnum_subexperimentsdescribe the cut. With execution, every subexperiment runs onshotsshots throughBackendSamplerV2and the reconstructed value is stored ascut_parity_expval(compare withraw_parity_expvalof uncut candidates). A circuit no wider thanmax_subcircuit_qubitsruns uncut (cut_count: 0, no subexperiment metrics). Qubits no gate acts on are dropped before cutting (a measured idle qubit reads|0>, contributing exactly+1to the parity), and a wider circuit whose connected parts each fit is split into those parts without any cut (cut_count: 0,sampling_overhead: 1.0), so no subcircuit ever exceeds the limit. A candidate is recorded as infeasible, with the reason logged and kept in its metrics (cutting_error, orcompile_errorwhen a subexperiment cannot be compiled for the backend), when the circuit has non-terminal measurements or classically controlled operations, when a subexperiment cannot be compiled, or when the cut would need more than 1024 subexperiments. The default objective chargessampling_overhead(9 per cut CNOT, multiplied over the cuts; 1 for an uncut circuit) so shallower subexperiments are weighed against their shot cost.
Default candidate generation¶
qbalance.search.default_candidate_strategies(max_candidates=24, seed=0) builds a deterministic pool of 23 distinct candidates:
optimization levels 0-3, each with default routing, SABRE routing, SABRE layout and routing, and the qbalance noise-aware layout with SABRE routing (16 candidates);
at optimization level 2 with SABRE routing: Pauli twirling (8 instances), dynamical decoupling (
XY4), measurement twirling (8 instances), and all three combined;M3 and ZNE, each with measurement twirling over 8 instances, at optimization level 2 with SABRE routing;
a cutting candidate (optimization level 1, noise-aware layout, SABRE routing, 4-qubit subcircuits: at optimization level 1 Qiskit would otherwise keep each small subexperiment on qubits 0, 1, … whatever their error rates).
StrategySpec(optimization_level=0) always comes first; the other 22 are shuffled deterministically by seed, and the first max_candidates of the result are returned, so a smaller max_candidates keeps a seed-dependent subset.
Explicit strategy sets¶
Use explicit strategies when you need a reproducible curated search space:
from qbalance import StrategySpec, Workload, load_data
strategies = [
StrategySpec(optimization_level=1, routing_method="sabre"),
StrategySpec(
optimization_level=2,
layout_method="qbalance_noise_aware",
routing_method="sabre",
),
]
balanced = (
Workload.from_dataset(load_data("tiny"))
.set_target("fake:generic:5")
.adjust(strategies=strategies)
)
Workload.adjust(strategies=...) accepts any iterable of StrategySpec objects or mapping objects. Inputs are validated and duplicate strategies are removed while preserving first-seen order. When explicit strategies intentionally exclude the baseline, allow_regression=False can still select the baseline as a guarded fallback when every candidate in the selection pool is worse than the finite-safe baseline objective on the objective terms both report (a regressing top candidate is first replaced by the best one that does not regress); equal scores and incomparable baselines do not trigger fallback.
Strategy JSON formats¶
qbalance.load_strategy_specs(path) accepts these JSON shapes:
Single strategy object¶
{"optimization_level": 1, "routing_method": "sabre"}
List of strategy objects¶
[
{"optimization_level": 1, "routing_method": "sabre"},
{"optimization_level": 2, "measurement_twirling": true, "num_twirls": 8}
]
Wrapped list¶
{
"strategies": [
{"optimization_level": 1, "routing_method": "sabre"},
{"optimization_level": 2, "measurement_twirling": true, "num_twirls": 8}
]
}
Saved qbalance outputs¶
The loader can also extract strategies from saved balanced workload result files containing selections and from matrix JSON files containing results with strategy entries. This makes it possible to reuse selected or benchmarked strategies in later runs.