Circuit cutting (qiskit_addon_cutting
)¶
Circuit cutting.
- cut_wires(circuit, /)[source]¶
Transform all
CutWire
instructions in a circuit toMove
instructions marked for cutting.The returned circuit will have one newly allocated qubit for every
CutWire
instruction.See Sec. 3 and Appendix A of 2302.03366v1 for more information about the two different representations of wire cuts: single-qubit (
CutWire
) vs. two-qubit (Move
).- Parameters:
circuit (
QuantumCircuit
) – Original circuit withCutWire
instructions- Returns:
New circuit with
CutWire
instructions replaced byMove
instructions wrapped inTwoQubitQPDGate
s- Return type:
circuit
- expand_observables(observables, original_circuit, final_circuit, /)[source]¶
Expand observable(s) according to the qubit mapping between
original_circuit
andfinal_circuit
.The
Qubit
s onfinal_circuit
must be a superset of those onoriginal_circuit
.Given a
PauliList
of observables, this function returns new observables with identity operators placed on the qubits that did not exist inoriginal_circuit
. This way, observables onoriginal_circuit
can be mapped to appropriate observables onfinal_circuit
.This function is designed to be used after calling
final_circuit = transform_cuts_to_moves(original_circuit)
(seetransform_cuts_to_moves()
).This function requires
observables.num_qubits == original_circuit.num_qubits
and returns new observables such thatretval.num_qubits == final_circuit.num_qubits
.- Parameters:
observables (
PauliList
) – Observables corresponding tooriginal_circuit
original_circuit (
QuantumCircuit
) – Original circuitfinal_circuit (
QuantumCircuit
) – Final circuit, whose qubits the originalobservables
should be expanded to
- Return type:
- Returns:
New \(N\)-qubit observables which are compatible with the \(N\)-qubit
final_circuit
- Raises:
ValueError –
observables
andoriginal_circuit
have different number of qubits.ValueError – Qubit from
original_circuit
cannot be found infinal_circuit
.
- partition_circuit_qubits(circuit, partition_labels, inplace=False)[source]¶
Replace all nonlocal gates belonging to more than one partition with instances of
TwoQubitQPDGate
.TwoQubitQPDGate
s belonging to a single partition will not be affected.- Parameters:
circuit (
QuantumCircuit
) – The circuit to partitionpartition_labels (
Sequence
[Hashable
]) – A sequence containing a partition label for each qubit in the input circuit. Nonlocal gates belonging to more than one partition will be replaced withTwoQubitQPDGate
s.inplace (
bool
) – Flag denoting whether to copy the input circuit before acting on it
- Return type:
- Returns:
The output circuit with each nonlocal gate spanning two partitions replaced by a
TwoQubitQPDGate
- Raises:
ValueError – The length of partition_labels does not equal the number of qubits in the circuit.
ValueError – Input circuit contains unsupported gate.
- partition_problem(circuit, partition_labels=None, observables=None)[source]¶
Separate an input circuit and observable(s).
If
partition_labels
is provided, then qubits with matching partition labels will be grouped together, and non-local gates spanning more than one partition will be cut apart. The labelNone
is treated specially: any qubit with that partition label must be unused in the circuit.If
partition_labels
is not provided, then it will be determined automatically from the connectivity of the circuit. This automatic determination ignores anyTwoQubitQPDGate
s in thecircuit
, as these denote instructions that are explicitly destined for cutting. The resulting partition labels, in the automatic case, will be consecutive integers starting with 0. Qubits which are idle throughout the circuit will be assigned a partition label ofNone
.All cut instructions will be replaced with
SingleQubitQPDGate
s.If provided,
observables
will be separated along the boundaries specified by the partition labels.- Parameters:
circuit (QuantumCircuit) – The circuit to partition and separate
partition_labels (Sequence[Hashable] | None) – A sequence of labels, such that each label corresponds to the circuit qubit with the same index
observables (PauliList | None) – The observables to separate
- Return type:
PartitionedCuttingProblem
- Returns:
A tuple containing a dictionary mapping a partition label to a subcircuit, a list of QPD bases (one for each circuit gate or wire which was decomposed), and, optionally, a dictionary mapping a partition label to a list of Pauli observables.
- Raises:
ValueError – The number of partition labels does not equal the number of qubits in the circuit.
ValueError – An input observable acts on a different number of qubits than the input circuit.
ValueError – An input observable has a phase not equal to 1.
ValueError – A qubit with a label of
None
is not idleValueError – The input circuit should contain no classical bits or registers.
- cut_gates(circuit, gate_ids, inplace=False)[source]¶
Transform specified gates into
TwoQubitQPDGate
s.- Parameters:
circuit (
QuantumCircuit
) – The circuit containing gates to be decomposedgate_ids (
Sequence
[int
]) – The indices of the gates to decomposeinplace (
bool
) – Flag denoting whether to copy the input circuit before acting on it
- Return type:
- Returns:
A copy of the input circuit with the specified gates replaced with
TwoQubitQPDGate
s and a list ofQPDBasis
instances – one for each decomposed gate.- Raises:
ValueError – The input circuit should contain no classical bits or registers.
- generate_cutting_experiments(circuits, observables, num_samples)[source]¶
Generate cutting subexperiments and their associated coefficients.
If the input,
circuits
, is aQuantumCircuit
instance, the output subexperiments will be contained within a 1D array, andobservables
is expected to be aPauliList
instance.If the input circuit and observables are specified by dictionaries with partition labels as keys, the output subexperiments will be returned as a dictionary which maps each partition label to a 1D array containing the subexperiments associated with that partition.
In both cases, the subexperiment lists are ordered as follows:
\([sample_{0}observable_{0}, \ldots, sample_{0}observable_{N-1}, sample_{1}observable_{0}, \ldots, sample_{M-1}observable_{N-1}]\)
The coefficients will always be returned as a 1D array – one coefficient for each unique sample.
- Parameters:
circuits (QuantumCircuit | dict[Hashable, QuantumCircuit]) – The circuit(s) to partition and separate
observables (PauliList | dict[Hashable, PauliList]) – The observable(s) to evaluate for each unique sample
num_samples (int | float) – The number of samples to draw from the quasi-probability distribution. If set to infinity, the weights will be generated rigorously rather than by sampling from the distribution.
- Return type:
tuple[list[QuantumCircuit] | dict[Hashable, list[QuantumCircuit]], list[tuple[float, WeightType]]]
- Returns:
A tuple containing the cutting experiments and their associated coefficients. If the input circuits is a
QuantumCircuit
instance, the output subexperiments will be a sequence of circuits – one for every unique sample and observable. If the input circuits are represented as a dictionary keyed by partition labels, the output subexperiments will also be a dictionary keyed by partition labels and containing the subexperiments for each partition. The coefficients are always a sequence of length-2 tuples, where each tuple contains the coefficient and theWeightType
. Each coefficient corresponds to one unique sample.- Raises:
ValueError –
num_samples
must be at least one.ValueError –
circuits
andobservables
are incompatible typesValueError –
SingleQubitQPDGate
instances must have their cut ID appended to the gate label so they may be associated with other gates belonging to the same cut.ValueError –
SingleQubitQPDGate
instances are not allowed in unseparated circuits.
- reconstruct_expectation_values(results, coefficients, observables)[source]¶
Reconstruct an expectation value from the results of the sub-experiments.
- Parameters:
results (SamplerResult | PrimitiveResult | dict[Hashable, SamplerResult | PrimitiveResult]) –
The results from running the cutting subexperiments. If the cut circuit was not partitioned between qubits and run separately, this argument should be a
SamplerResult
instance or a dictionary mapping a single partition to the results. If the circuit was partitioned and its pieces were run separately, this argument should be a dictionary mapping partition labels to the results from each partition’s subexperiments.The subexperiment results are expected to be ordered in the same way the subexperiments are ordered in the output of
generate_cutting_experiments()
– one result for every sample and observable, as shown below. The Qiskit Sampler primitive will return the results in the same order the experiments are submitted, so users who do not usegenerate_cutting_experiments()
to generate their experiments should take care to order their subexperiments as follows before submitting them to the sampler primitive:\([sample_{0}observable_{0}, \ldots, sample_{0}observable_{N-1}, sample_{1}observable_{0}, \ldots, sample_{M-1}observable_{N-1}]\)
coefficients (Sequence[tuple[float, WeightType]]) – A sequence containing the coefficient associated with each unique subexperiment. Each element is a tuple containing the coefficient (a
float
) together with itsWeightType
, which denotes how the value was generated. The contribution from each subexperiment will be multiplied by its corresponding coefficient, and the resulting terms will be summed to obtain the reconstructed expectation value.observables (PauliList | dict[Hashable, PauliList]) – The observable(s) for which the expectation values will be calculated. This should be a
PauliList
ifresults
is aSamplerResult
instance. Otherwise, it should be a dictionary mapping partition labels to the observables associated with that partition.
- Return type:
list[float]
- Returns:
A
list
offloat
s, such that each float is an expectation value corresponding to the input observable in the same position- Raises:
ValueError –
observables
andresults
are of incompatible types.ValueError – An input observable has a phase not equal to 1.
- class PartitionedCuttingProblem(subcircuits, bases, subobservables=None)[source]¶
Bases:
NamedTuple
The result of decomposing and separating a circuit and observable(s).
Create new instance of PartitionedCuttingProblem(subcircuits, bases, subobservables)
- Parameters:
- bases: list[QPDBasis]¶
Alias for field number 1
- count(value, /)¶
Return number of occurrences of value.
- index(value, start=0, stop=9223372036854775807, /)¶
Return first index of value.
Raises ValueError if the value is not present.
- subcircuits: dict[Hashable, QuantumCircuit]¶
Alias for field number 0
- subobservables: dict[Hashable, PauliList] | None¶
Alias for field number 2
Automatic Cut Finding¶
- find_cuts(circuit, optimization, constraints)[source]¶
Find cut locations in a circuit, given optimization parameters and cutting constraints.
- Parameters:
circuit (
QuantumCircuit
) – The circuit to cut. The input circuit may not contain gates acting on more than two qubits.optimization (
OptimizationParameters
) – Options for controlling optimizer behavior. Currently, the optimal cuts are chosen using Dijkstra’s best-first search algorithm.constraints (
DeviceConstraints
) – Constraints on how the circuit may be partitioned
- Return type:
tuple
[QuantumCircuit
,dict
[str
,float
]]- Returns:
A circuit containing
BaseQPDGate
instances. The subcircuits resulting from cutting these gates will be runnable on the devices meeting theconstraints
.- A metadata dictionary:
cuts: A list of length-2 tuples describing each cut in the output circuit. The tuples are formatted as
(cut_type: str, cut_id: int)
. The cut ID is the index of the cut gate or wire in the output circuit’sdata
field.sampling_overhead: The sampling overhead incurred from cutting the specified gates and wires.
minimum_reached: A bool indicating whether or not the search conclusively found the minimum of cost function.
minimum_reached = False
could also mean that the cost returned was actually the lowest possible cost but that the search was not allowed to run long enough to prove that this was the case.
- Raises:
ValueError – The input circuit contains a gate acting on more than 2 qubits.
- class OptimizationParameters(seed=None, max_gamma=1024, max_backjumps=10000, gate_lo=True, wire_lo=True)[source]¶
Bases:
object
Specify parameters that control the optimization.
If either of the constraints specified by
max_backjumps
ormax_gamma
are exceeded, the search terminates but nevertheless returns the result of a greedy best first search, which gives an upper-bound on gamma.- Parameters:
- gate_lo: bool = True¶
Bool indicating whether or not to allow LO gate cuts while finding cuts.
- max_backjumps: None | int = 10000¶
Maximum number of backjumps that can be performed before the search is forced to terminate; setting it to
None
implies that no such restriction is placed.
- max_gamma: float = 1024¶
Maximum allowed value of gamma which, if exceeded, forces the search to terminate.
- seed: int | None = None¶
The seed to use when initializing Numpy random number generators in the best first search priority queue.
- wire_lo: bool = True¶
Bool indicating whether or not to allow LO wire cuts while finding cuts.