Wire cutting untuk estimasi nilai ekspektasi
Estimasi penggunaan: 22 detik pada prosesor Heron (CATATAN: Ini hanya perkiraan. Waktu eksekusi kamu mungkin berbeda.)
Hasil belajar
Setelah mengikuti tutorial ini, pengguna diharapkan memahami:
- Cara menggunakan
qiskit-addon-cuttinguntuk mempartisi circuit besar menjadi subcircuit yang lebih kecil, sehingga mengurangi efek noise
Prasyarat
Kami menyarankan pengguna untuk sudah familiar dengan topik berikut sebelum mengikuti tutorial ini:
- Menggunakan primitive Sampler, yang digunakan dalam alur kerja ini
Latar Belakang
Circuit-knitting adalah istilah umum yang mencakup berbagai metode untuk mempartisi sebuah circuit menjadi beberapa subcircuit yang lebih kecil dengan lebih sedikit gate atau qubit. Setiap subcircuit dapat dieksekusi secara independen dan hasil akhirnya diperoleh melalui proses pasca-pemrosesan klasik atas hasil dari setiap subcircuit. Teknik ini dapat diakses melalui Circuit cutting Qiskit addon; lihat dokumentasi beserta materi pengantar lainnya untuk penjelasan rinci tentang teknik ini.
Tutorial ini berfokus pada metode yang disebut wire cutting, di mana circuit dipartisi sepanjang wire [1], [2]. Perlu dicatat bahwa partisi pada circuit klasik cukup sederhana karena keluaran di titik partisi dapat ditentukan secara deterministik, yaitu 0 atau 1. Namun, keadaan qubit di titik pemotongan umumnya berupa mixed state. Oleh karena itu, setiap subcircuit perlu diukur beberapa kali dalam basis yang berbeda (biasanya basis yang lengkap secara tomografi, seperti basis Pauli [3], [4]) dan disiapkan di eigenstate-nya yang sesuai. Gambar di bawah (courtesy: [7]) menunjukkan contoh wire cutting untuk GHZ state empat qubit menjadi tiga subcircuit. Di sini menunjukkan sekumpulan basis (biasanya Pauli X, Y, dan Z), dan menunjukkan sekumpulan eigenstate (biasanya , , , dan ).
Karena setiap subcircuit memiliki lebih sedikit qubit dan Gate, mereka diharapkan lebih tahan terhadap noise. Tutorial ini menunjukkan contoh di mana metode ini dapat digunakan untuk secara efektif menekan noise dalam sistem.
Persyaratan
Sebelum memulai tutorial ini, pastikan kamu sudah menginstal hal-hal berikut:
- Qiskit SDK v2.0 atau lebih baru, dengan dukungan visualisasi
- Qiskit Runtime v0.22 atau lebih baru (
pip install qiskit-ibm-runtime) - Circuit cutting Qiskit addon v0.10.0 atau lebih baru (
pip install qiskit-addon-cutting) - Qiskit addon utils 0.3 atau lebih baru (
pip install qiskit-addon-utils) - Qiskit Aer (
pip install qiskit-aer)
Setup
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
import numpy as np
import matplotlib.pyplot as plt
from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit
from qiskit.quantum_info import PauliList, SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_aer import AerSimulator
from qiskit.result import sampled_expectation_value
from qiskit_addon_cutting.instructions import CutWire
from qiskit_addon_cutting import (
cut_wires,
expand_observables,
partition_problem,
generate_cutting_experiments,
reconstruct_expectation_values,
)
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2, Batch
Contoh simulator skala kecil
Tutorial ini mengimplementasikan pola Qiskit untuk mensimulasikan circuit Many-Body Localization (MBL) satu dimensi (1D). Circuit MBL adalah circuit yang efisien secara hardware dan diparameterisasi oleh dua parameter dan . Ketika diset ke dan keadaan awal disiapkan di untuk semua qubit, nilai ekspektasi ideal dari adalah untuk setiap qubit site terlepas dari nilai-nilai . Detail lebih lanjut tentang circuit ini tersedia di artikel ini.
Perlu dicatat bahwa pada simulator tanpa noise, nilai ekspektasi yang diperoleh dengan dan tanpa circuit cutting akan sama.
Langkah 1: Petakan input klasik ke masalah kuantum
Konstruksi circuit MBL 1D
Pertama, kita menyajikan fungsi untuk membangun circuit MBL 1D.
class MBLChainCircuit(QuantumCircuit):
def __init__(
self, num_qubits: int, depth: int, use_cut: bool = False
) -> None:
super().__init__(
num_qubits, name=f"MBLChainCircuit<{num_qubits}, {depth}>"
)
evolution = MBLChainEvolution(num_qubits, depth, use_cut)
self.compose(evolution, inplace=True)
class MBLChainEvolution(QuantumCircuit):
def __init__(self, num_qubits: int, depth: int, use_cut) -> None:
super().__init__(
num_qubits, name=f"MBLChainEvolution<{num_qubits}, {depth}>"
)
theta = Parameter("θ")
phis = ParameterVector("φ", num_qubits)
for layer in range(depth):
layer_parity = layer % 2
# print("layer parity", layer_parity)
for qubit in range(layer_parity, num_qubits - 1, 2):
# print(qubit)
self.cz(qubit, qubit + 1)
self.u(theta, 0, np.pi, qubit)
self.u(theta, 0, np.pi, qubit + 1)
if (
use_cut
and layer_parity == 0
and (
qubit == num_qubits // 2 - 1
or qubit == num_qubits // 2
)
):
self.append(CutWire(), [num_qubits // 2])
if use_cut and layer < depth - 1 and layer_parity == 1:
if qubit == num_qubits // 2:
self.append(CutWire(), [qubit])
for qubit in range(num_qubits):
self.p(phis[qubit], qubit)
num_qubits = 10
depth = 2
mbl = MBLChainCircuit(num_qubits, depth)
mbl.draw("mpl", fold=-1)
Kita menghitung nilai ekspektasi rata-rata atas semua qubit untuk . Karena nilai ekspektasi ideal dari , nilai ekspektasi ideal dari juga . Parameter dipilih secara acak.
np.random.seed(42)
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis
Circuit perlu diberi anotasi dengan menyisipkan CutWire di lokasi yang diinginkan untuk mempartisinya. Untuk tutorial ini, kita memilih partisi yang sama rata. Circuit MBL dirancang sedemikian rupa sehingga menetapkan use_cut=True dalam fungsi akan menyisipkan anotasi dengan benar setelah qubit, dengan adalah jumlah qubit dalam circuit asli. Kita juga menetapkan parameter yang dihasilkan secara acak ke circuit.
mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_cut.draw("mpl", fold=-1)
Langkah 2: Optimalkan masalah untuk eksekusi hardware kuantum
Potong circuit menjadi subcircuit yang lebih kecil
Sekarang kita mempartisi circuit menjadi dua subcircuit yang lebih kecil menggunakan qiskit-addon-cutting. qiskit-addon-cutting menambahkan gate virtual Move untuk membagi lokasi wire cut dengan menyesuaikan jumlah qubit secara tepat. Sekarang kita membuat circuit dengan gate virtual ini. Karena ada satu wire cut, jumlah qubit terkait akan bertambah 1.
mbl_move = cut_wires(mbl_cut)
mbl_move.draw("mpl", fold=-1)
Konstruksi dan perluas observable
Observable, sebagaimana didefinisikan sebelumnya, akan menjadi rata-rata pada setiap qubit. Namun, setelah menyisipkan gate virtual Move, jumlah qubit efektif dalam circuit bertambah. Observable juga harus diperluas untuk memperhitungkan perubahan jumlah qubit ini. Perlu dicatat bahwa observable selalu bekerja secara trivial (sebagai ) pada qubit ekstra yang ditambahkan untuk gate virtual Move.
observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
observable
PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',
'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',
'IIIIIIIIZI', 'IIIIIIIIIZ'])
new_obs = expand_observables(observable, mbl, mbl_move)
new_obs
PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',
'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',
'IIIIIIIIIZI', 'IIIIIIIIIIZ'])
Sekarang circuit dapat dipartisi sepanjang gate Move dan kita memperoleh subcircuit beserta subobservable, yaitu bagian dari observable asli yang terkait dengan setiap subcircuit.
partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
Berikut kita visualisasikan dua subcircuit:
subcircuits[0].draw("mpl", fold=-1)
subcircuits[1].draw("mpl", fold=-1)
Memperluas observable menggunakan operasi Move memerlukan struktur data PauliList. Untuk merekonstruksi nilai ekspektasi circuit asli, kita membutuhkan observable dalam format SparsePauliOp.
M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)
Seperti yang dibahas sebelumnya, untuk setiap potongan circuit upstream harus diukur dalam basis Pauli, dan circuit downstream harus disiapkan dalam eigenstate dari basis tersebut. Fungsi generate_cutting_experiments membuat semua circuit yang diperlukan beserta koefisien yang terkait dengan setiap circuit untuk rekonstruksi. Temukan detail lebih lanjut di paper ini.
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits,
observables=subobservables,
num_samples=np.inf,
)
Transpilasi circuit ke backend
Untuk contoh pertama yang hanya melibatkan simulasi, kita mentranspilasi circuit ke set gate basis dari backend:
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
)
print(backend)
<IBMBackend('ibm_fez')>
Langkah 3: Eksekusi menggunakan Qiskit primitives
Sekarang, eksekusi setiap subexperiment:
pm_basis = generate_preset_pass_manager(
optimization_level=2, basis_gates=backend.configuration().basis_gates
)
basis_subexperiments = {
label: pm_basis.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}
sampler = SamplerV2(mode=AerSimulator())
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in basis_subexperiments.items()
}
Langkah 4: Pasca-proses dan kembalikan hasil dalam format klasik yang diinginkan
Sekarang kita mengambil hasil setiap subexperiment yang dijalankan dan merekonstruksi nilai ekspektasi circuit yang tidak dipotong:
# Retrieve results
results = {label: job.result() for label, job in jobs.items()}
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real
reconstructed_expval
np.float64(0.9953821063041687)
methods = [
"Uncut",
"Wire cut",
]
values = [
1,
reconstructed_expval,
] # since the ideal expectation value in noiseless simulation is +1
ax = plt.gca()
plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
ax.set_ylabel(r"$M_Z$", fontsize=12)
Text(0, 0.5, '$M_Z$')
Contoh hardware skala besar
Sekarang kita mendemonstrasikan wire cutting untuk circuit MBL 60-Qubit. Circuit yang tidak dipotong maupun yang dipotong akan dieksekusi pada hardware IBM Quantum®:
num_qubits = 60
depth = 2
# construct the circuit
mbl = MBLChainCircuit(num_qubits, depth)
# create parameters
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis
# construct the cut circuit
mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_move = cut_wires(mbl_cut)
# Define observable and expand to account for the wire cut
observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
new_obs = expand_observables(observable, mbl, mbl_move)
# Construct a SparsePauliOp version of the observable for later use in reconstruction
M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)
# Partition the circuit and get subcircuits and subobservables
partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
# Obtain subexperiments and coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits,
observables=subobservables,
num_samples=np.inf,
)
# Transpile the subexperiments to the backend
pm = generate_preset_pass_manager(optimization_level=2, backend=backend)
isa_subexperiments = {
label: pm.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}
# Execute the subexperiments and retrieve results
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
sampler.options.environment.job_tags = ["TUT_WC"]
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
results = {label: job.result() for label, job in jobs.items()}
# Reconstruct the expectation value of the original observable
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real
# Compute the uncut circuit to obtain the noisy expectation value for comparison
sampler = SamplerV2(mode=backend)
sampler.options.environment.job_tags = ["TUT_WC"]
if mbl.num_clbits == 0:
mbl.measure_all()
isa_mbl = pm.run(mbl)
pub = (isa_mbl, params)
uncut_job = sampler.run([pub])
uncut_counts = uncut_job.result()[0].data.meas.get_counts()
uncut_expval = sampled_expectation_value(uncut_counts, M_z)
# visualize the results
ax = plt.gca()
methods = ["uncut", "cut"]
values = [uncut_expval, reconstructed_expval]
plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
plt.axhline(y=1, color="k", linestyle="--")
plt.text(0.3, 0.95, "Exact result")
plt.show()
uncut_expval
0.9202473958333336
Langkah selanjutnya
Jika kamu merasa karya ini menarik, kamu mungkin tertarik dengan materi berikut:
Referensi
[1] Peng, T., Harrow, A. W., Ozols, M., & Wu, X. (2020). Simulating large quantum circuits on a small quantum computer. Physical review letters, 125(15), 150504.
[2] Tang, W., Tomesh, T., Suchara, M., Larson, J., & Martonosi, M. (2021, April). Cutqc: using small quantum computers for large quantum circuit evaluations. In Proceedings of the 26th ACM International conference on architectural support for programming languages and operating systems (pp. 473-486).
[3] Perlin, M. A., Saleem, Z. H., Suchara, M., & Osborn, J. C. (2021). Quantum circuit cutting with maximum-likelihood tomography. npj Quantum Information, 7(1), 64.
[4] Majumdar, R., & Wood, C. J. (2022). Error mitigated quantum circuit cutting. arXiv preprint arXiv:2211.13431.
[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., Seshadri, P. V., & Simmhan, Y. (2023). Parallelizing Quantum-Classical Workloads: Profiling the Impact of Splitting Techniques. In 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE.
[6] Bhoumik, D., Majumdar, R., Saha, A., & Sur-Kolay, S. (2023). Distributed Scheduling of Quantum Circuits with Noise and Time Optimization. arXiv preprint arXiv:2309.06005.
[7] Majumdar, R. (2024). Efficient Reduction of Resources and Noise in Discrete Quantum Computing Circuits (Doctoral dissertation, Indian Statistical Institute - Kolkata). https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar&cbl=2026366&diss=y