Bounds (qiskit_addon_slc.bounds)¶
Bound computation functions.
This module provides various functions for computing the error bounds that make up a shaded lightcone.
- compute_forward_bounds(circuit, noise_model_paulis, /, observable, *, evolution_max_terms=1000000, eigval_max_qubits=14, atol=1e-08, atol_simplify=1e-08, atol_eigenvalue=1e-10, **kwargs)[source]¶
Compute the forward-evolved unequal-time commutator bounds.
Starting at the end of the circuit, compute the forward-evolved unequal-time commutator bounds for all Pauli error terms of each noisy layer in the target circuit.
That is, compute \(\| \left[ E_F, A_F \right] \|_2\) for all error terms, \(E_F\), where \(A_F\) is the target
observableto be measured oncircuit.The error terms, \(E_I\), are dictated by
noise_model_paulis. This dictionary maps noise model identifiers (samplomatic.InjectNoise.ref) to a list of Pauli error terms. The corresponding terms will be used whenever aBoxOpwith a matchingInjectNoiseannotation is encountered during the iteration overcircuit.Important
Every
BoxOpwith anInjectNoiseannotation must have a uniquemodifier_ref, since the bounds are keyed by it. Boxing a circuit withgenerate_boxing_pass_manager()provides this only withinject_noise_strategy="individual_modification". Otherwise, the bounds of boxes sharing amodifier_refsilently overwrite each other.Deprecated since version 0.2.0:
qiskit_addon_slc.bounds.forward.compute_forward_bounds()’s argumentatolis deprecated as of qiskit-addon-slc 0.2.0. It will be removed no earlier than 3 months after the release date. Use atol_simplify and atol_eigenvalue instead.- Parameters:
circuit (QuantumCircuit) – the target circuit.
noise_model_paulis (dict[str, QubitSparsePauliList]) – the Pauli error terms to consider for each noise model.
observable (Pauli | PauliList | SparseObservable | SparsePauliOp) – the target observable to be measured at the end of the circuit.
evolution_max_terms (int) – the maximum number of operator terms to keep track of during the evolution.
eigval_max_qubits (int) – the maximum number of qubits of a commutator for which the eigenvalue will still be attempted to be computed. When this value is exceeded, the bound is approximated via a simpler and more loose triangle inequality.
atol (float) – DEPRECATED use
atol_simplifyandatol_eigenvalueinstead!atol_simplify (float) – the absolute tolerance used for trimming terms from the commutator. Loosening this tolerance will result in a greater truncation of the commutator’s terms, rendering the computation of its eigenvalue cheaper but less accurate.
atol_eigenvalue (float) – the absolute tolerance used for detecting convergence of the commutator’s eigenvalue. Loosening this tolerance will result in a less accurate eigenvalue as computed by the iterative Davidson eigensolver.
kwargs – any additional keyword arguments will be forward to
compute_bounds().
- Returns:
The unequal-time commutator bound.
- Raises:
NotImplementedError – when the
observablecontains more than a single Pauli term. If you run into this, you will need to call this function for each target Pauli separately.- Return type:
- tighten_with_speed_limit(bounds, circuit, noise_model_paulis, /, observable)[source]¶
Tighten the provided bounds using limits on the speed of information propagation.
Inspired by the ideas behind the Lieb-Robinson bounds, this function leverages limits on the speed of information propagation to tighten previously computed forward-evolved unequal-time commutator bounds (see also
compute_forward_bounds()).- Parameters:
bounds (dict[str, PauliLindbladMap]) – the previously computed forward-evolved unequal-time commutator bounds.
circuit (QuantumCircuit) – the target circuit.
noise_model_paulis (dict[str, QubitSparsePauliList]) – the Pauli error terms to consider for each noise model.
observable (Pauli | PauliList | SparseObservable | SparsePauliOp) – the target observable to be measured at the end of the circuit.
- Returns:
A tightened copy of the unequal-time commutator bounds.
- Raises:
NotImplementedError – when the
observablecontains more than a single Pauli term. If you run into this, you will need to call this function for each target Pauli separately.ValueError – when encountering a gate that acts on more than 2 qubits.
- Return type:
- compute_backward_bounds(circuit, noise_model_paulis, /, *, evolution_max_terms=1000000, **kwargs)[source]¶
Compute the backward-evolved unequal-time commutator bounds.
Starting at the beginning of the circuit, compute the backward-evolved unequal-time commutator bounds for all Pauli error terms of each noisy layer in the target circuit.
That is, compute \(\| \left[ E_I, \rho_I \right] \|_1\) (using the Schatten 1-norm aka nuclear norm) for all error terms, \(E_I\), where \(\rho_I\) is assumed to be the all-zero state, \(\ket{0 \ldots 0}\), on all active qubits in
circuit.The error terms, \(E_I\), are dictated by
noise_model_paulis. This dictionary maps noise model identifiers (samplomatic.InjectNoise.ref) to a list of Pauli error terms. The corresponding terms will be used whenever aBoxOpwith a matchingInjectNoiseannotation is encountered during the iteration overcircuit.Important
Every
BoxOpwith anInjectNoiseannotation must have a uniquemodifier_ref, since the bounds are keyed by it. Boxing a circuit withgenerate_boxing_pass_manager()provides this only withinject_noise_strategy="individual_modification". Otherwise, this function may never return.Caution
Before computing the bounds, this function removes all
Measureoperations fromcircuit. This is required because the circuit is being inverted before being processed in reverse order, which allows the backward evolution to be treated like a forward evolution (in the inverted circuit).- Parameters:
circuit (QuantumCircuit) – the target circuit.
noise_model_paulis (dict[str, QubitSparsePauliList]) – the Pauli error terms to consider for each noise model.
evolution_max_terms (int) – the maximum number of operator terms to keep track of during the evolution. (If the operator exceeds this size, the smallest terms are truncated).
kwargs – any additional keyword arguments will be forward to
compute_bounds().
- Returns:
The backward-evolved unequal-time commutator bounds.
- Return type:
- merge_bounds(circuit, forward_bounds, backward_bounds, /, noise_rates=None, *, is_clifford_circuit=False)[source]¶
Merge forward and backward bounds.
The layer at which the switch from using backward bounds to using forward bounds takes place will be the same for all qubits. It is determined by taking into account the provided learned
noise_rates. If these are not provided, uniform noise rates are assumed. While this is an unrealistic assumption, previewing the merged bounds may still be useful.- Parameters:
circuit (QuantumCircuit) – the target circuit.
forward_bounds (dict[str, PauliLindbladMap] | None) – the forward bounds (see also
compute_forward_bounds()).backward_bounds (dict[str, PauliLindbladMap] | None) – the backward bounds (see also
compute_backward_bounds()).noise_rates (dict[str, PauliLindbladMap | None] | None) – the noise rates learned on the target backend.
is_clifford_circuit (bool) – whether the target circuit is fully Clifford.
- Returns:
The merged bounds.
- Raises:
ValueError – when both provided bounds are
None.KeyError – when the
boundscontain anInjectNoise.modifier_refkey which does not occur in the targetcircuitor whoseInjectNoise.refis not found.ValueError – if the noise model Pauli terms whose bounds are computed for a given
InjectNoise.modifier_refdo not match between theforward_boundsandbackward_bounds.NotImplementedError – when
is_clifford_circuitisTrue.
- Return type:
dict[str, PauliLindbladMap] | None
- compute_local_scales(circuit, bounds, /, noise_rates, *, sampling_cost_budget=inf, bias_tolerance=0.0, apply_in_post=False)[source]¶
Computes the
local_scalesargument to aSamplex.This
local_scalesargument is used to specify which individual error terms to mitigate.Either the
sampling_cost_budgetorbias_tolerancemust be specified. The former puts an upper bound on the sampling cost while the latter puts an upper bound on the remaining bias to tolerate.Note
If the order of Pauli terms in
boundsandnoise_ratesdo not match, the output of this function will assume the order set forth bynoise_ratesin order to ensure that the scales are compatible with the rates that will also be provided to theQuantumProgram.- Parameters:
circuit (QuantumCircuit) – the target circuit.
bounds (dict[str, PauliLindbladMap]) – the shaded lightcone bounds.
noise_rates (dict[str, PauliLindbladMap | None]) – the learned noise model rates.
sampling_cost_budget (float) – the maximum sampling cost to allow.
bias_tolerance (float) – the maximum bias to tolerate.
apply_in_post (bool) –
whether SLC will be applied in post-processing rather than at sampling time. By default (
False), the returnedlocal_scalesare provided as an input to theSamplex, such that Paulis are only injected for the error terms selected for mitigation. Set this toTruewhen the experiment is instead run as standard PEC (i.e. Paulis are injected for all error terms) and SLC is applied to the resulting data afterwards. This is necessary whenever the error terms to mitigate are not known at the time of the experiment, for example, when computing multiple observables (each with its own shaded lightcone) from a single dataset, or when computing even a single observable retroactively from existing PEC data.In this setting, the minus signs of the error terms which are not selected for mitigation are ignored. Their injected Paulis therefore act as noise rather than antinoise, effectively doubling their error rates. Setting this to
Trueaccounts for that by ranking the error terms greedily by their “value density” (i.e., their bias bound divided by their noise rate) instead of using the default prioritization.Warning
When this is
True, the returnedlocal_scalesmust not be provided to theSamplex, since the experiment must inject Paulis for all error terms. Instead, they specify which error terms’ signs to apply when processing the experiment’s results. Providing them to the samplex silently yields wrong results.
- Returns:
the
local_scalesdictionary to be provided as the direct input to thesamplomatic.samplex.Samplex.inputs()(unlessapply_in_postisTrue, in which case it is used in post-processing instead).the sampling cost overhead (\(\gamma^2\)) required to perform the sampling of
local_scales.the remaining bias on expectation values computed with these bounds.
- Return type:
A tuple of length 3, the items of which are
- Raises:
ValueError – if non-default values are provided for both, the
sampling_cost_budgetandbias_tolerance.KeyError – if
noise_ratesis missing an entry for any noise model identifier (InjectNoise.ref) encountered incircuit.
This module also contains some lower level functions which are usually not accessed by an end-user directly, but may prove useful for additional development on top of this package.
- compute_bounds(circuit, noise_model_paulis, light_cone, norm_fn, *, backwards, max_num_boxes=None, num_processes=1, timeout=None)[source]¶
Computes the unequal time commutator bounds.
Given a circuit with
BoxOpinstructions withInjectNoiseannotations and a mapping of noise model identifiers (InjectNoise.ref) to list of Pauli error terms (noise_model_paulis), this function computes the unequal time commutator bounds (the details of which are implemented bynorm_fn). In doing so, it only considers gates that lie within the light-cone of the observable (initialized bylight_cone). These computed bounds form the basis of the shaded light-cone.Important
The computed bounds are keyed by
InjectNoise.modifier_ref, so everyBoxOpwith anInjectNoiseannotation must have a uniquemodifier_ref. Boxing a circuit withgenerate_boxing_pass_manager()provides this only withinject_noise_strategy="individual_modification". With any other strategy, several boxes share amodifier_ref, which leads to silently wrong forward bounds or a backward computation that never finishes.Since this function performs a long-running computation, it gracefully handles
KeyboardInterruptexceptions, allowing the user to interrupt the computation at an arbitrary point in time and still obtain the results that have been computed up to that point.- Parameters:
circuit (QuantumCircuit) – the target circuit.
noise_model_paulis (dict[str, QubitSparsePauliList]) – the Pauli error terms to consider for each noise model.
light_cone (LightCone) – the initialized and stateful
LightConetracker.norm_fn (Callable[[Pauli, RotationGates], CommutatorBounds]) – the function implementing the specific unequal time commutator. It gets called as
norm_fn(pauli, gates)for every Pauli error term, wheregatesare the non-Clifford gates within the light-cone that the error term must be evolved through.backwards (bool) – whether to iterate over the
circuitin reverse.max_num_boxes (int | None) – the maximum number of boxes for which to compute bounds. Bounds for any additional boxes will be given the trivial upper bound value of \(2.0\).
num_processes (int) – the number of parallel processes to use.
timeout (float | None) – an optional timeout (in seconds) after which all remaining layers are filled with trivial numerical bounds of
2.0. Note, that this is not a strict timeout and the layer being processed at the time of reaching this timeout will complete normally.
- Returns:
The computed unequal time commutator bounds.
- Return type:
- time_evolved_norm_forward(pauli, gates, observable, *, evolution_max_terms=1000000, eigval_max_qubits=14, comm_norm_order=2, atol_simplify=1e-08, atol_eigenvalue=1e-10)[source]¶
Compute the bound of an error Pauli term evolved forward to the target observable.
Given an error Pauli term (
pauli), the non-Clifford component of a circuit (gates), and a target observable (observable) that is to be measured at the end of the circuit, this function computes the unequal-time commutator between the noise term and observable. That means, the error Pauli term (which may reside anywhere in the middle of the target circuit) is being evolved forwards to the end of the circuit where the target observable is being measured. In doing so, it must be evolved through the non-Clifford component of the circuit within the light-cone (here, given bygates).- Parameters:
pauli (Pauli) – the error Pauli term to be evolved.
gates (RotationGates) – the non-Clifford circuit component encoded as rotation gates (as produced by
propagate_through_rotation_gates()).observable (Pauli) – the target observable to be measured at the end of the circuit.
evolution_max_terms (int) – the maximum number of operator terms to keep track of during the evolution.
eigval_max_qubits (int) – the maximum number of qubits of a commutator for which the eigenvalue computation will still be attempted. When this value is exceeded, the bound is approximated via a simpler and more loose triangle inequality.
comm_norm_order (int) – the order of the commutator norm to compute.
atol_simplify (float) – the absolute tolerance used for trimming terms from the commutator. Loosening this tolerance will result in a greater truncation of the commutator’s terms, rendering the computation of its eigenvalue cheaper but less accurate.
atol_eigenvalue (float) – the absolute tolerance used for detecting convergence of the commutator’s eigenvalue. Loosening this tolerance will result in a less accurate eigenvalue as computed by the iterative Davidson eigensolver.
- Returns:
The unequal-time commutator bound.
- Return type:
- time_evolved_norm_backward(pauli, gates, *, evolution_max_terms=1000000)[source]¶
Bound the effect of an error Pauli term on the quantum state by evolving the error backward.
Evolving backwards means moving the error through the circuit to its start.
Given an error Pauli term (
pauli) and the non-Clifford component of a circuit (gates), this function computes the unequal-time commutator between the Pauli error and the quantum state. The error Pauli term (which may reside anywhere in the middle of the target circuit) is evolved backwards to the start of the circuit. In doing so, it must be evolved through the non-Clifford component of the circuit within the light-cone (here, given bygates).The state is assumed to be the all-zero state, \(\ket{0 \ldots 0}\), which is what
compute_backward_bounds()assumes for every active qubit of the circuit.Note, that this function is designed for the context of
compute_backward_bounds()which inverts the target circuit first. Hence, a backward evolution actually amounts to the Schrödinger frame evolution ofpaulithroughgates.- Parameters:
pauli (Pauli) – the error Pauli term to be evolved.
gates (RotationGates) – the non-Clifford circuit component encoded as rotation gates (as produced by
propagate_through_rotation_gates()).evolution_max_terms (int) – the maximum number of operator terms to keep track of during the evolution.
- Returns:
The unequal-time commutator bound \(\| \left[E, \rho\right] \|_1\) for Pauli error \(E\) and state \(\rho\), where the norm is the Schatten 1 norm (nuclear norm).
- Return type:
- class LightCone(qubits, operations, offset_cache)[source]¶
Bases:
NamedTupleA stateful light cone, grown one circuit instruction at a time.
compute_bounds()uses a light cone to skip work: while iterating a circuit, any instruction thatcommutes()with the light cone cannot affect what is being bounded, and any instruction that does not commute is added to it, widening the cone.The light cone is seeded with the operator that the error terms are bounded against. The two classmethods cover the cases of the built-in bounds:
initialize_from_pauli()seeds it with a Pauli observable, ascompute_forward_bounds()does, andinitialize_from_measurements()seeds it with a set of qubits, such as every active qubit of the circuit, ascompute_backward_bounds()does. To bound against anything else, construct it directly from the qubits it spans and the operations it starts out with, and an emptyoffset_cache.Caution
A light cone is mutated by
commutes(), so a fresh one is needed for every call tocompute_bounds().Create new instance of LightCone(qubits, operations, offset_cache)
- Parameters:
- commutes(inst)[source]¶
Checks whether the provided instruction commutes with this light cone.
If it does not, the instruction gets added to this light cone.
- Parameters:
inst (CircuitInstruction) – the circuit instruction to check.
- Returns:
Whether the instruction commutes with this light cone.
- Return type:
- classmethod initialize_from_measurements(circuit, *, measure_active=False, measure_all=False)[source]¶
Initialize a LightCone using the measurement gates inside the circuit.
- Parameters:
circuit (QuantumCircuit) – the QuantumCircuit for which to initialize the LightCone.
measure_active (bool) – ignore any measurement information from the circuit and simply assume all non-idle qubits to be measured.
measure_all (bool) – ignore any measurement information from the circuit and simply assume all qubits to be measured. This takes precedence over
measure_active.
- Returns:
An empty LightCone initialized with the to-be-measured qubits.
- Return type:
- classmethod initialize_from_pauli(circuit, pauli)[source]¶
Initialize a LightCone assuming the given pauli is measured on the given circuit.
- Parameters:
circuit (QuantumCircuit) – the QuantumCircuit for which to initialize the LightCone.
pauli (Pauli) – the Pauli to be measured.
- Returns:
An empty LightCone initialized with the provided pauli measured on the circuit.
- Return type:
- class CommutatorBounds(commutator_bound, truncation_bias, fallback_to_tri_ineq)[source]¶
Bases:
NamedTupleA dataclass to store metadata about the computed commutator bounds.
Create new instance of CommutatorBounds(commutator_bound, truncation_bias, fallback_to_tri_ineq)
- commutator_bound: float¶
The bound on the commutator.
This bound will be computed in different means depending on the application. For example, backward bounds will compute the nuclear norm (Schatten 1-norm) while forward bounds are typically computed using the spectral norm (Schatten infinity-norm).
If the norm computation exceeds specified difficulty limits, it will be abandoned in favor of a simpler bound based on the triangle inequality, which is indicated by
fallback_to_tri_ineqbeing set toTrue.This value may be
NaNwhen the computation of the commutor bound was aborted. This can happen when thetruncation_biasalready exceeds the theoretical bound of2.0.
- fallback_to_tri_ineq: bool¶
Whether
commutator_boundwas computed “loosely” using a simple triangle inequality.
- min()[source]¶
Returns the minimum bound encoded by this metadata.
The minimal bound is the smaller of the sum of
commutator_boundandtruncation_biasor the theoretical bound of2.0.The value of
2.0is used because a Pauli observable bounded on the range[-1, +1]cannot be biased by more than2.0.- Return type: