Aller au contenu principal
Ce projet est en phase bêta. Tu as trouvé un bug ? Ouvrir un ticket GitHub. Une question ou une idée ? Pose ta question dans Discussions — tes retours sont les bienvenus !
Quelle est la qualité de la traduction sur cette page ?

Découpe de portes pour réduire la largeur d'un 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 portes 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.
  • É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 — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_cutting": "qiskit-addon-cutting", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
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)

Diagramme de circuit quantique

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 portes 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)

Diagramme de circuit quantique

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

Diagramme de circuit quantique

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 929^2.

Pour en savoir plus sur le surcoût d'échantillonnage engendré par la découpe de circuit, consulte la documentation explicative.

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 portes 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