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, num_processes=1, timeout=None)[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 observable to be measured on 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 a BoxOp with a matching InjectNoise annotation is encountered during the iteration over circuit.

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) – the absolute tolerance used for trimming terms from the commutator and for detecting convergence of the commutator’s eigenvalue.

  • 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 unequal-time commutator bound.

Raises:

NotImplementedError – when the observable contains 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:

dict[str, PauliLindbladMap]

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:
Returns:

A tightened copy of the unequal-time commutator bounds.

Raises:
  • NotImplementedError – when the observable contains 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:

dict[str, PauliLindbladMap]

compute_backward_bounds(circuit, noise_model_paulis, /, *, evolution_max_terms=1000000, num_processes=1, timeout=None)[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 a BoxOp with a matching InjectNoise annotation is encountered during the iteration over circuit.

Caution

Before computing the bounds, this function removes all Measure operations from circuit. 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).

  • 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 backward-evolved unequal-time commutator bounds.

Return type:

dict[str, PauliLindbladMap]

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:
Returns:

The merged bounds.

Raises:
  • ValueError – when both provided bounds are None.

  • KeyError – when the bounds contain an InjectNoise.modifier_ref key which does not occur in the target circuit or whose InjectNoise.ref is not found.

  • ValueError – if the noise model Pauli terms whose bounds are computed for a given InjectNoise.modifier_ref do not match between the forward_bounds and backward_bounds.

  • NotImplementedError – when is_clifford_circuit is True.

Return type:

dict[str, PauliLindbladMap] | None

compute_local_scales(circuit, bounds, /, noise_rates, *, sampling_cost_budget=inf, bias_tolerance=0.0)[source]

Computes the local_scales argument to a Samplex.

This local_scales argument is used to specify which individual error terms to mitigate.

Either the sampling_cost_budget or bias_tolerance must 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 bounds and noise_rates do not match, the output of this function will assume the order set forth by noise_rates in order to ensure that the scales are compatible with the rates that will also be provided to the QuantumProgram.

Parameters:
Returns:

  • the local_scales dictionary to be provided as the direct input to the samplomatic.samplex.Samplex.inputs().

  • 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_budget and bias_tolerance.

  • KeyError – if noise_rates is missing an entry for any noise model identifier (InjectNoise.ref) encountered in circuit.