Circuit cutting for periodic boundary conditions
Usage estimate: Two minutes on an Eagle processor (NOTE: This is an estimate only. Your runtime might vary.)
Background
In this notebook, we consider the simulation of a periodic chain of qubits where there is two qubit operation between every two adjacent qubits, including the first and the last ones. Periodic chains are often found in physics and chemistry problems such as Ising models and molecular simulation.
Current IBM Quantum® devices are planar. It is possible to embed some periodic chains on the topology directly where the first and last qubits are neighbors. However, for big enough problems, the first and last qubits can be far apart, thus requiring many SWAP gates for the 2-qubit operation between these two qubits. Such a periodic boundary problem has been studied in this paper.
In this notebook we show the usage of circuit cutting to deal with such a utility scale periodic chain problem where the first and last qubits are not neighbors. Cutting this long range connectivity avoids the extra SWAP gates at the cost of executing multiple instances of the circuit, and some classical post-processing. In summary, cutting can be incorporated to logically calculate the long distance 2-qubit operations. In other words, this approach leads to an effective increase in the connectivity of the coupling map, thus leading to a fewer number of SWAP gates.
Note that there are two types of cuts - cutting the wire of a circuit (called wire cutting), or replacing a 2-qubit gate with multiple single qubit operations (called gate cutting). In this notebook, we shall focus on gate cutting. For more details on gate cutting, refer to the explanatory materials in qiskit-addon-cutting, and the corresponding references. For more details on wire cutting, refer to the Wire cutting for expectation values estimation tutorial, or the tutorials in qiskit-addon-cutting.
Requirements
Before starting this tutorial, be sure you have the following installed:
- Qiskit SDK v1.2 or later (
pip install qiskit) - Qiskit Runtime v0.3 or later (
pip install qiskit-ibm-runtime) - Circuit cutting Qiskit addon v.9.0 or later (
pip install qiskit-addon-cutting)
Setup
# Added by doQumentation — installs packages not in the Binder environment
%pip install -q qiskit-addon-cutting
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import (
BasisTranslator,
Optimize1qGatesDecomposition,
)
from qiskit.circuit.equivalence_library import (
SessionEquivalenceLibrary as sel,
)
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.result import sampled_expectation_value
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.circuit.library import TwoLocal
from qiskit_addon_cutting import (
cut_gates,
generate_cutting_experiments,
reconstruct_expectation_values,
)
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2, SamplerOptions, Batch
Step 1: Map classical inputs to a quantum problem
Here, we'll generate a TwoLocal circuit and define some observables.
- Input: Parameters to create a circuit
- Output: Abstract circuit and observables
We consider a hardware-efficient entangler map for the TwoLocal circuit with periodic connectivity between the last and the first qubits of the entangler map. This long range interaction can lead to extra SWAP gates during transpilation, thus increasing the depth of the circuit.
Select backend and initial layout
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)
For this notebook we shall consider a 109 qubit periodic 1D chain, which is the longest 1D chain in the topology of a 127-qubit IBM Quantum device. It is not possible to arrange a 109 qubit periodic chain on a 127 qubit device such that the first and last qubits are neighbors without incorporating extra SWAP gates.
init_layout = [
13,
12,
11,
10,
9,
8,
7,
6,
5,
4,
3,
2,
1,
0,
14,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
36,
51,
50,
49,
48,
47,
46,
45,
44,
43,
42,
41,
40,
39,
38,
37,
52,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
74,
89,
88,
87,
86,
85,
84,
83,
82,
81,
80,
79,
78,
77,
76,
75,
90,
94,
95,
96,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
112,
126,
125,
124,
123,
122,
121,
120,
119,
118,
117,
116,
115,
114,
113,
]
# the number of qubits in the circuit is governed by the length of the initial layout
num_qubits = len(init_layout)
num_qubits
109
Build the entangler map for the TwoLocal circuit
coupling_map = [(i, i + 1) for i in range(0, len(init_layout) - 1)]
coupling_map.append(
(len(init_layout) - 1, 0)
) # adding in the periodic connectivity
TwoLocal circuit allows the repetition of the rotation_blocks and the entangler map multiple times. For this case, the number of repetitions determine the number of periodic gates that need to be cut. Since the sampling overhead increases exponentially with the number of cuts (refer to the Wire cutting for expectation values estimation tutorial for more details), we shall fix the number of repetitions to 2 in this notebook.
num_reps = 2
entangler_map = []
for even_edge in coupling_map[0 : len(coupling_map) : 2]:
entangler_map.append(even_edge)
for odd_edge in coupling_map[1 : len(coupling_map) : 2]:
entangler_map.append(odd_edge)
ansatz = TwoLocal(
num_qubits=num_qubits,
rotation_blocks="rx",
entanglement_blocks="cx",
entanglement=entangler_map,
reps=num_reps,
).decompose()
ansatz.draw("mpl", fold=-1)

In order to verify the quality of the outcome using circuit cutting, we need to know the ideal outcome. The current circuit of choice is beyond brute force classical simulation. Therefore, we fix the parameters to the circuit carefully to make it clifford.
We shall assign the parameter value for the first two layers of Rx gates, and the value for the last layer. This ensures that the ideal outcome of this circuit is , being the number of qubits. Therefore, the expectation values of and , where is the index of the qubit, are and respectively.
params_last_layer = [np.pi] * ansatz.num_qubits
params = [0] * (ansatz.num_parameters - ansatz.num_qubits)
params.extend(params_last_layer)
ansatz.assign_parameters(params, inplace=True)
Select observables
To quantify the benefits of gate cutting we measure the expectation values of the observables