Utilities (qiskit_addon_slc.utils)¶
Various utilities.
This module provides a number of utility functions. Some of these exist only temporarily to work around open issues of the Qiskit SDK. When this is the case, they are marked as such and may be removed without deprecation or further notice.
- find_indices(circuit, bits_or_instruction, /)[source]¶
Find the qubit-indices of the provided bit indices or circuit instructions.
Caution
This function is not considered part of the stable API! It will get removed without warning or deprecation when the same functionality is supported natively by the Qiskit SDK. See this issue for more details.
A sequence of bits maps to a list of indices, in the order the bits were given:
>>> from qiskit import QuantumCircuit >>> from qiskit_addon_slc.utils import find_indices
>>> circuit = QuantumCircuit(3) >>> find_indices(circuit, [circuit.qubits[2], circuit.qubits[0]]) [2, 0]
A single bit maps to a single index, and an instruction to the indices of the qubits it acts on:
>>> find_indices(circuit, circuit.qubits[1]) 1
>>> _ = circuit.cx(2, 0) >>> find_indices(circuit, circuit.data[0]) [2, 0]
- Parameters:
circuit (QuantumCircuit) – the quantum circuit whose qubit indices to find.
bits_or_instruction (Bit | CircuitInstruction | Sequence[Bit]) – the bits whose indices to find. If this is a
CircuitInstruction, the qubits which this instruction acts upon are used.
- Returns:
The indices of the queried bits in the circuit’s registers. If a single bit object was provided, a single
intis returned for its index. Otherwise the return type will be alist[int]whose length equals the number of provided bits.- Raises:
TypeError – when an unexpected type of
bits_or_instructiongets provided.- Return type:
- generate_noise_model_paulis(instructions, coupling_map=None, circuit=None)[source]¶
Generate the 1- and 2-weight Pauli terms for each of the unique 2q layer boxes provided.
- Parameters:
instructions (list[CircuitInstruction]) – the output of
find_unique_box_instructions(). Any of the provided instructions are assumed to either consists of only measurement gates or correspond to a layer of 2-qubit gate instructions. For the former, the generated noise model will contain only single-qubitXerrors, for the latter all 1- and 2-weight Pauli errors on the reduced coupling map will be included.coupling_map (CouplingMap | None) – the coupling map of the backend on which the instructions have been laid out. If this is
None, a 1d line of qubits is assumed.circuit (QuantumCircuit | None) – the transpiled circuit which has been laid out on the provided coupling map. This may only be
Nonewhen thecoupling_mapis alsoNone.
- Returns:
A dictionary mapping the
refattributes of theInjectNoiseannotation of each unique Box to the 1- and 2-weight Pauli terms whose errors are learned for this box.- Return type:
- get_extremal_eigenvalue(spo, *, tol=1e-10, max_cycle=500, max_space=12, lindep=1e-11, **kwargs)[source]¶
Compute the spectral norm of a Hermitian Pauli operator (as a signed extremal eigenvalue).
Given a Hermitian operator written as a weighted sum of Paulis,
C = sum_k a_k P_k, this returns its most-negative eigenvalue. Because such an operator has a spectrum that is symmetric about zero, the magnitude of that eigenvalue is the operator’s spectral norm||C||(its largest eigenvalue in absolute value) – takeabs()of the result if that is what you need. In this addon it is used to evaluate the commutator-norm error bound||[E, O]||for an error PauliEand observableO.This exploits how a sum of Paulis lives in a space of dimension
2^(p + c), wherepis the number of independent anticommuting pairs the Paulis generate andccounts the remaining commuting directions. The commuting directions are conserved, so this reduced operator is block-diagonal, with2^cblocks of size2^p. When these blocks are small enough each is diagonalized exactly, giving a result exact to machine precision. Otherwise, the reduced operator is instead solved with the compiled Rust Davidson solver (a diagonally-preconditioned Davidson iteration, which checks a converged eigenvalue with a short Lanczos iteration for a lower one before reporting it). This path is accurate but not exact, and the arguments below affect only it. It builds the whole2^(p + c)-dimensional operator in sparse form and holds a few vectors of that length, so its memory use grows with the qubit count ofspo; callers are expected to bound it, as the bound computations do witheigval_max_qubits.Note the sign convention: the returned eigenvalue is the most-negative one, so the spectral norm is its absolute value.
>>> from qiskit.quantum_info import SparsePauliOp >>> from qiskit_addon_slc.utils import get_extremal_eigenvalue
>>> operator = SparsePauliOp(["ZI", "IZ"], [1.0, 0.5]) >>> converged, eigenvalue = get_extremal_eigenvalue(operator) >>> converged True >>> float(eigenvalue) -1.5 >>> abs(eigenvalue) # the spectral norm 1.5
- Parameters:
spo (SparsePauliOp) – the Hermitian operator whose most-negative eigenvalue (and hence spectral norm) to compute.
tol (float) – the convergence threshold of the iterative solver. It bounds the change of the eigenvalue between iterations and, unless rounding errors at the scale of the operator norm dominate, the residual norm of the eigenvector.
max_cycle (int) – the maximum number of iterations of the iterative solver.
max_space (int) – the maximum size of the subspace built up by the iterative solver. Once reached, the subspace is restarted from its
max_space // 2lowest Ritz vectors.lindep (float) – the fraction of its squared norm below which a new subspace vector, after being orthogonalized against the existing subspace, is considered linearly dependent on it (and, thus, discarded).
kwargs – deprecated and ignored. Additional keyword arguments used to be passed on to the
pyscf.lib.davidson1solver that this function no longer uses. They are still accepted for backwards compatibility, but have no effect and raise aDeprecationWarning.
- Returns:
A
(converged, eigenvalue)pair.convergedreports whether the computation succeeded (may beFalseif Davidson fails to converge).eigenvalueis the most-negative eigenvalue ofspo.- Raises:
ValueError – if
tolis not positive and finite,max_cycleis not positive,max_spaceis less than 2, orlindepis not in[0, 1). These are checked for everyspo, even one that is solved exactly.- Return type:
- iter_circuit(circuit, *, reverse=False, log_process=True)[source]¶
Iterates over the instructions in a circuit.
Note
This function recurses into
BoxOpinstructions.Note
Barriers in the circuit are being ignored.
- Parameters:
circuit (QuantumCircuit) – the circuit to iterate over.
reverse (bool) – whether to iterate the circuit in reverse order.
log_process (bool) – whether to log process.
- Yields:
Tuples of length four, consisting of the encountered circuit instruction, the canonical qubit indices (i.e. the integer indices of the acted-upon qubits in the context of the input
circuit), theInjectNoiseattributes:modifier_refandref. The latter two items may beNoneindicating a circuit instruction that was not part of an unrolledBoxOp.- Return type:
Generator[tuple[CircuitInstruction, list[int], str | None, str | None]]
- map_modifier_ref_to_ref(circuit)[source]¶
Iterate a circuit and map
InjectNoiseannotation references.The bounds computation keys its results by
modifier_ref, while a noise model is keyed byref, so this mapping is what relates the two. This requires everymodifier_refto be unique, as provided by boxing a circuit withinject_noise_strategy="individual_modification"(seecompute_bounds()). Boxes without anInjectNoiseannotation are skipped.>>> from qiskit import QuantumCircuit >>> from qiskit.circuit import BoxOp >>> from samplomatic import InjectNoise >>> from qiskit_addon_slc.utils import map_modifier_ref_to_ref
>>> circuit = QuantumCircuit(2) >>> for idx in range(2): ... body = QuantumCircuit(2) ... _ = body.cx(0, 1) ... annotation = InjectNoise(ref=f"noise_{idx}", modifier_ref=f"box_{idx}") ... _ = circuit.append(BoxOp(body, annotations=[annotation]), [0, 1])
>>> map_modifier_ref_to_ref(circuit) {'box_0': 'noise_0', 'box_1': 'noise_1'}
- Parameters:
circuit (QuantumCircuit) – the circuit to iterate over.
- Returns:
A dictionary mapping each
InjectNoise.modifier_refto itsInjectNoise.ref.- Return type:
- remove_measure(circuit)[source]¶
Remove any
Measureoperations from the provided circuit.Note
This function recurses into
BoxOpinstructions.>>> from qiskit import QuantumCircuit >>> from qiskit_addon_slc.utils import remove_measure
>>> circuit = QuantumCircuit(2, 2) >>> _ = circuit.h(0) >>> _ = circuit.measure(0, 0) >>> _ = circuit.cx(0, 1) >>> dict(sorted(circuit.count_ops().items())) {'cx': 1, 'h': 1, 'measure': 1}
>>> dict(sorted(remove_measure(circuit).count_ops().items())) {'cx': 1, 'h': 1}
The original circuit is left untouched:
>>> dict(sorted(circuit.count_ops().items())) {'cx': 1, 'h': 1, 'measure': 1}
- Parameters:
circuit (QuantumCircuit) – the circuit whose measurements to remove.
- Returns:
The circuit without any Measure operations.
- Return type: