Circuit cutting (qiskit_addon_cutting)¶
Circuit cutting.
- cut_wires(circuit, /)[source]¶
Transform all
CutWireinstructions in a circuit toMoveinstructions marked for cutting.The returned circuit will have one newly allocated qubit for every
CutWireinstruction.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 withCutWireinstructions- Returns:
New circuit with
CutWireinstructions replaced byMoveinstructions wrapped inTwoQubitQPDGates- Return type:
circuit
- expand_observables(observables, original_circuit, final_circuit, /)[source]¶
Expand observable(s) according to the qubit mapping between
original_circuitandfinal_circuit.The
Qubits onfinal_circuitmust be a superset of those onoriginal_circuit.Given a
PauliListof observables, this function returns new observables with identity operators placed on the qubits that did not exist inoriginal_circuit. This way, observables onoriginal_circuitcan 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_qubitsand returns new observables such thatretval.num_qubits == final_circuit.num_qubits.- Parameters:
observables (
PauliList) – Observables corresponding tooriginal_circuitoriginal_circuit (
QuantumCircuit) – Original circuitfinal_circuit (
QuantumCircuit) – Final circuit, whose qubits the originalobservablesshould be expanded to
- Return type:
- Returns:
New \(N\)-qubit observables which are compatible with the \(N\)-qubit
final_circuit- Raises:
ValueError –
observablesandoriginal_circuithave different number of qubits.ValueError – Qubit from
original_circuitcannot 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.TwoQubitQPDGates 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 withTwoQubitQPDGates.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_labelsis 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 labelNoneis treated specially: any qubit with that partition label must be unused in the circuit.If
partition_labelsis not provided, then it will be determined automatically from the connectivity of the circuit. This automatic determination ignores anyTwoQubitQPDGates 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
SingleQubitQPDGates.If provided,
observableswill 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
Noneis not idleValueError – The input circuit should contain no classical bits or registers.
- cut_gates(circuit, gate_ids, inplace=False)[source]¶
Transform specified gates into
TwoQubitQPDGates.- 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
TwoQubitQPDGates and a list ofQPDBasisinstances – 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 aQuantumCircuitinstance, the output subexperiments will be contained within a 1D array, andobservablesis expected to be aPauliListinstance.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
QuantumCircuitinstance, 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_samplesmust be at least one.ValueError –
circuitsandobservablesare incompatible typesValueError –
SingleQubitQPDGateinstances must have their cut ID appended to the gate label so they may be associated with other gates belonging to the same cut.ValueError –
SingleQubitQPDGateinstances 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
SamplerResultinstance 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
PauliListifresultsis aSamplerResultinstance. Otherwise, it should be a dictionary mapping partition labels to the observables associated with that partition.
- Return type:
list[float]
- Returns:
A
listoffloats, such that each float is an expectation value corresponding to the input observable in the same position- Raises:
ValueError –
observablesandresultsare of incompatible types.ValueError – An input observable has a phase not equal to 1.
- class PartitionedCuttingProblem(subcircuits, bases, subobservables=None)[source]¶
Bases:
NamedTupleThe 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
BaseQPDGateinstances. 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’sdatafield.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 = Falsecould 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:
objectSpecify parameters that control the optimization.
If either of the constraints specified by
max_backjumpsormax_gammaare 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
Noneimplies 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.