La découpe de Gate pour réduire la largeur du Circuit
Dans ce notebook, tu vas parcourir les étapes d'un patron Qiskit en utilisant la découpe de circuit pour réduire le nombre de Qubits dans un Circuit. Tu vas découper des Gates pour pouvoir reconstruire la valeur d'espérance d'un Circuit à quatre Qubits en utilisant uniquement des expériences à deux Qubits.
Voici les étapes que tu vas suivre :
- Étape 1 : Mapper le problème en circuits quantiques et en opérateurs :
- Mapper le hamiltonien sur un circuit quantique.
- Étape 2 : Optimiser pour le matériel cible [Utilise l'addon de découpe] :
- Découper le Circuit et l'observable.
- Transpiler les sous-expériences pour le matériel.
- Étape 3 : Exécuter sur le matériel cible :
- Lancer les sous-expériences obtenues à l'Étape 2 avec la primitive
Sampler.
- Lancer les sous-expériences obtenues à l'Étape 2 avec la primitive
- Étape 4 : Post-traiter les résultats [Utilise l'addon de découpe] :
- Combiner les résultats de l'Étape 3 pour reconstruire la valeur d'espérance de l'observable en question.
Étape 1 : Mapper
Créer un Circuit à découper
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
from qiskit.circuit.library import efficient_su2
qc = efficient_su2(4, entanglement="linear", reps=2)
qc.assign_parameters([0.4] * len(qc.parameters), inplace=True)
qc.draw("mpl", scale=0.8)

Spécifier un observable
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp(["ZZII", "IZZI", "-IIZZ", "XIXI", "ZIZZ", "IXIX"])
Étape 2 : Optimiser
Séparer le Circuit et l'observable selon un partitionnement de Qubits spécifié
Chaque étiquette dans partition_labels correspond au Qubit du circuit au même index. Les Qubits partageant une étiquette de partition commune seront regroupés, et les Gates non locales couvrant plus d'une partition seront découpées.
Remarque : L'argument observables de partition_problem est de type PauliList. Les coefficients et les phases des termes d'observable sont ignorés lors de la décomposition du problème et de l'exécution des sous-expériences. Ils peuvent être réappliqués lors de la reconstruction de la valeur d'espérance.
from qiskit_addon_cutting import partition_problem
partitioned_problem = partition_problem(
circuit=qc, partition_labels="AABB", observables=observable.paulis
)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
bases = partitioned_problem.bases
Visualiser le problème décomposé
subobservables
{'A': PauliList(['II', 'ZI', 'ZZ', 'XI', 'ZZ', 'IX']),
'B': PauliList(['ZZ', 'IZ', 'II', 'XI', 'ZI', 'IX'])}
subcircuits["A"].draw("mpl", scale=0.8)

subcircuits["B"].draw("mpl", scale=0.8)

Calculer le surcoût d'échantillonnage pour les découpes choisies
Ici tu découpes deux Gates CNOT, ce qui entraîne un surcoût d'échantillonnage de .
Pour en savoir plus sur le surcoût d'échantillonnage engendré par la découpe de circuit, consulte le matériel explicatif.
import numpy as np
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 81.0
Générer les sous-expériences à exécuter sur le Backend
generate_cutting_experiments accepte des arguments circuits/observables sous forme de dictionnaires associant les étiquettes de partition de Qubits aux subcircuit/subobservables respectifs.
Pour simuler la valeur d'espérance du Circuit complet, de nombreuses sous-expériences sont générées à partir de la distribution quasi-probabiliste jointe des Gates décomposées, puis exécutées sur un ou plusieurs Backends. Le nombre d'échantillons prélevés sur la distribution est contrôlé par num_samples, et un coefficient combiné est donné pour chaque échantillon unique. Pour plus d'informations sur la façon dont les coefficients sont calculés, consulte le matériel explicatif.
from qiskit_addon_cutting import generate_cutting_experiments
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits, observables=subobservables, num_samples=np.inf
)
Choisir un Backend
Ici tu utilises un faux Backend, ce qui fera tourner Qiskit Runtime en mode local (c'est-à-dire sur un simulateur local).
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
backend = FakeManilaV2()
Préparer les sous-expériences pour le Backend
Tu dois Transpiler les circuits avec ton Backend comme cible avant de les soumettre à Qiskit Runtime.
from qiskit.transpiler import generate_preset_pass_manager
# Transpile the subexperiments to ISA circuits
pass_manager = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_subexperiments = {
label: pass_manager.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}
Étape 3 : Exécuter
Lancer les sous-expériences avec la primitive Sampler de Qiskit Runtime
from qiskit_ibm_runtime import SamplerV2, Batch
# Submit each partition's subexperiments to the Qiskit Runtime Sampler
# primitive, in a single batch so that the jobs will run back-to-back.
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
/home/garrison/Qiskit/qiskit-ibm-runtime/qiskit_ibm_runtime/session.py:157: UserWarning: Session is not supported in local testing mode or when using a simulator.
warnings.warn(
# Retrieve results
results = {label: job.result() for label, job in jobs.items()}
Étape 4 : Post-traiter
Reconstruire la valeur d'espérance
Reconstruire les valeurs d'espérance pour chaque terme de l'observable et les combiner pour reconstruire la valeur d'espérance de l'observable d'origine.
from qiskit_addon_cutting import reconstruct_expectation_values
# Get expectation values for each observable term
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
# Reconstruct final expectation value
reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)
Comparer la valeur d'espérance reconstruite avec la valeur d'espérance exacte issue du Circuit et de l'observable d'origine
from qiskit_aer.primitives import EstimatorV2
estimator = EstimatorV2()
exact_expval = estimator.run([(qc, observable)]).result()[0].data.evs
print(f"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}")
print(f"Exact expectation value: {np.round(exact_expval, 8)}")
print(f"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}")
print(
f"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}"
)
Reconstructed expectation value: 0.6991539
Exact expectation value: 0.56254612
Error in estimation: 0.13660778
Relative error in estimation: 0.24283836