Découpe de circuits pour conditions aux limites périodiques
Estimation de l'utilisation : Deux minutes sur un processeur Eagle (NOTE : Il s'agit uniquement d'une estimation. Votre durée d'exécution peut varier.)
Arrière-plan
Dans ce cahier, nous considérons la simulation d'une chaîne périodique de qubits où il y a une opération de deux qubits entre tous les deux qubits adjacents, y compris le premier et le dernier. Les chaînes périodiques sont souvent présentes dans les problèmes de physique et de chimie tels que les modèles d'Ising et la simulation moléculaire.
Les dispositifs actuels IBM Quantum® sont planaires. Il est possible d'intégrer directement dans la topologie des chaînes périodiques où le premier et le dernier qubit sont voisins. Cependant, pour des problèmes suffisamment importants, le premier et le dernier qubit peuvent être éloignés l'un de l'autre, ce qui nécessite de nombreuses portes SWAP pour l'opération à deux qubits entre ces deux qubits. Un tel problème de frontière périodique a été étudié à l'adresse ce document.
Dans ce cahier, nous montrons l'utilisation du découpage de circuits pour traiter un tel problème de chaîne périodique à l'échelle de l'utilité où le premier et le dernier qubits ne sont pas voisins. En coupant cette connectivité à longue distance, on évite les portes SWAP supplémentaires au prix de l'exécution de plusieurs instances du circuit et d'un post-traitement classique. En résumé, le découpage peut être incorporé pour calculer logiquement les opérations à 2 qubits sur de longues distances. En d'autres termes, cette approche conduit à une augmentation effective de la connectivité de la carte de couplage, ce qui permet de réduire le nombre de portes SWAP.
Notez qu'il existe deux types de coupures : couper le fil d'un circuit (appelé wire cutting) ou remplacer une porte à 2 qubits par plusieurs opérations à un qubit (appelé gate cutting). Dans ce cahier, nous nous concentrerons sur le découpage des portes. Pour plus de détails sur le découpage des portillons, consultez le site matériel explicatif à l'adresse qiskit-addon-cutting et les références correspondantes. Pour plus de détails sur le découpage du fil, reportez-vous au didacticiel sur le découpage du fil pour l'estimation des valeurs espérées ou aux didacticiels disponibles sur le site qiskit-addon-cutting.
Exigences
Avant de commencer ce tutoriel, assurez-vous que les éléments suivants sont installés :
- Qiskit SDK v1.2 ou plus tard (
pip install qiskit) - Qiskit Runtime v0.3 ou plus tard (
pip install qiskit-ibm-runtime) - Add-on Qiskit pour la découpe de circuits v.9.0 ou version ultérieure (
pip install qiskit-addon-cutting)
Configuration
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Étape 1 : Mettre en correspondance les entrées classiques avec un problème quantique
Ici, nous allons générer un circuit TwoLocal et définir quelques observables.
- Entrée : Paramètres pour créer un circuit
- Sortie : Circuit abstrait et observables
Nous considérons une solution matérielle efficace entangler map pour le circuit TwoLocal avec une connectivité périodique entre le dernier et le premier qubit du circuit entangler map. Cette interaction à longue portée peut conduire à des portes SWAP supplémentaires lors de la transpilation, augmentant ainsi la profondeur du circuit.
Sélectionnez le backend et la mise en page initiale
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=127
)Pour ce cahier, nous considérerons une chaîne 1D périodique de 109 qubits, qui est la plus longue chaîne 1D dans la topologie d'un dispositif IBM Quantum de 127 qubits. Il n'est pas possible d'organiser une chaîne périodique de 109 qubits sur un dispositif de 127 qubits de manière à ce que le premier et le dernier qubit soient voisins sans incorporer des portes SWAP supplémentaires.
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_qubitsOutput:
109
Construire la carte d'entrelacement pour le circuit d' TwoLocal
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 connectivityTwoLocal permet de répéter plusieurs fois le rotation_blocks et le entangler map . Dans ce cas, le nombre de répétitions détermine le nombre de portes périodiques à couper. Étant donné que la charge d'échantillonnage augmente de façon exponentielle avec le nombre de coupes (pour plus de détails, voir le tutoriel sur la coupe de fil pour l'estimation des valeurs espérées ), nous fixerons le nombre de répétitions à 2 dans le présent manuel.
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)Output:
Pour vérifier la qualité du résultat par découpage, il faut connaître le résultat idéal. Le circuit actuel de choix va au-delà de la simulation classique par force brute. C'est pourquoi nous fixons soigneusement les paramètres du circuit pour le rendre conforme à la norme "clifford".
Nous attribuerons la valeur de paramètre aux deux premières couches de portes Rx et la valeur à la dernière couche. Cela garantit que le résultat idéal de ce circuit est , étant le nombre de qubits. Par conséquent, les valeurs espérées de et , où est l'indice du qubit, sont respectivement et .
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)Sélectionner les observables
Pour quantifier les avantages de l'ouverture des vannes, nous mesurons les valeurs d'espérance des observables et . Comme nous l'avons vu précédemment, les valeurs idéales sont respectivement et .
observables = []
for i in range(num_qubits):
obs = "I" * (i) + "Z" + "I" * (num_qubits - i - 1)
observables.append(obs)
for i in range(num_qubits):
if i == num_qubits - 1:
obs = "Z" + "I" * (num_qubits - 2) + "Z"
else:
obs = "I" * i + "ZZ" + "I" * (num_qubits - i - 2)
observables.append(obs)
observables = SparsePauliOp(observables)
paulis = observables.paulis
coeffs = observables.coeffsÉtape 2 : Optimiser le problème pour l'exécution sur du matériel quantique
- Entrée : Circuit abstrait et observables
- Résultat : Circuit cible et observables produits par le découpage des portes à longue portée
Transpiler le circuit
Il est à noter que le circuit peut être transposé à ce stade, ou après la découpe. Si nous transposons après le découpage, nous devrons transposer chacune des sous-expériences générées en raison de la surcharge d'échantillonnage. Il est donc plus prudent de transposer à ce stade afin de réduire la charge de travail liée à la transposition.
Toutefois, si la transpilation est effectuée à ce stade avec la connectivité matérielle native, le transpileur ajoutera plusieurs portes SWAP pour placer l'opération périodique à 2 qubits, ce qui obscurcira les avantages de la coupure de circuit. Pour éviter ce problème, nous pouvons nous appuyer sur le fait que nous connaissons les portes exactes qui doivent être coupées. Plus précisément, nous pouvons créer une carte de couplage virtuel en ajoutant des connexions virtuelles entre des qubits éloignés pour accommoder ces portes périodiques à 2 qubits. Ainsi, le circuit pourra être transposé à ce stade sans incorporer les portes SWAP supplémentaires.
coupling_map = backend.configuration().coupling_map
# create a virtual coupling map with long range connectivity
virtual_coupling_map = coupling_map.copy()
virtual_coupling_map.append([init_layout[-1], init_layout[0]])
virtual_coupling_map.append([init_layout[0], init_layout[-1]])pm_virtual = generate_preset_pass_manager(
optimization_level=1,
coupling_map=virtual_coupling_map,
initial_layout=init_layout,
basis_gates=backend.configuration().basis_gates,
)
virtual_mapped_circuit = pm_virtual.run(ansatz)
virtual_mapped_circuit.draw("mpl", fold=-1, idle_wires=False)Output:
Couper les connexions périodiques à longue portée
Nous coupons maintenant les portes du circuit transposé. Notez que les portes à 2 qubits qui doivent être coupées sont celles qui relient le dernier et le premier qubit du schéma.
# Find the indices of the distant gates
cut_indices = [
i
for i, instruction in enumerate(virtual_mapped_circuit.data)
if {virtual_mapped_circuit.find_bit(q)[0] for q in instruction.qubits}
== {init_layout[-1], init_layout[0]}
]Nous allons appliquer le schéma du circuit transposé à l'observable.
trans_observables = observables.apply_layout(virtual_mapped_circuit.layout)Enfin, les sous-expériences sont générées par échantillonnage sur différentes bases de mesure et de préparation.
qpd_circuit, bases = cut_gates(virtual_mapped_circuit, cut_indices)
subexperiments, coefficients = generate_cutting_experiments(
circuits=qpd_circuit,
observables=trans_observables.paulis,
num_samples=np.inf,
)Il convient de noter que la suppression des interactions à longue distance conduit à l'exécution de plusieurs échantillons du circuit qui diffèrent dans les bases de mesure et de préparation. De plus amples informations à ce sujet sont disponibles sur les sites Construction d'une porte virtuelle à deux qubits par échantillonnage d'opérations à un qubit et Couper des circuits avec des unités multiples à deux qubits.
Le nombre de portes périodiques à découper est égal au nombre de répétitions de la couche TwoLocal , définie comme num_reps ci-dessus. Le surdébit d'échantillonnage du découpage des portes est de 6. Par conséquent, le nombre total de sous-expériences sera de .
print(f"Number of subexperiments is {len(subexperiments)} = 6**{num_reps}")Output:
Number of subexperiments is 36 = 6**2
Transpiler les sous-expériences
À ce stade, les sous-expériences contiennent des circuits comportant des portes à 1 qubit qui ne font pas partie de l'ensemble de portes de base. En effet, les qubits coupés sont mesurés dans des bases différentes, et les portes de rotation utilisées à cet effet n'appartiennent pas nécessairement à l'ensemble des portes de base. Par exemple, la mesure dans la base X implique l'application d'une porte de Hadamard avant la mesure habituelle dans la base Z. Mais Hadamard ne fait pas partie de l'ensemble de portes de base.
Au lieu d'appliquer l'ensemble du processus de transpilation à chacun des circuits des sous-expériences, nous pouvons utiliser des passes de transpilation spécifiques. Pour une description détaillée de toutes les passes de transpilation disponibles, consultez le site cette documentation.
Nous appliquerons les passes BasisTranslator puis Optimize1qGatesDecomposition pour nous assurer que toutes les portes de ces circuits appartiennent à l'ensemble de portes de base. L'utilisation de ces deux passages est plus rapide que l'ensemble du processus de transpilation, puisque d'autres étapes telles que le routage et la sélection initiale de l'agencement ne sont pas exécutées à nouveau.
pass_ = PassManager(
[Optimize1qGatesDecomposition(basis=backend.configuration().basis_gates)]
)
subexperiments = pass_.run(
[
dag_to_circuit(
BasisTranslator(sel, target_basis=backend.basis_gates).run(
circuit_to_dag(circ)
)
)
for circ in subexperiments
]
)Étape 3 : Exécutez à l'aide d' Qiskit primitives
- Entrée : Circuits cibles
- Sortie : Distributions de quasi-probabilité
Nous utilisons une primitive SamplerV2 pour l'exécution des circuits de coupe. Nous désactivons dynamical decoupling et twirling afin que toute amélioration que nous obtenons dans le résultat soit uniquement due à l'application efficace de la coupure de porte pour ce type de circuit.
options = SamplerOptions()
options.default_shots = 10000
options.dynamical_decoupling.enable = False
options.twirling.enable_gates = False
options.twirling.enable_measure = FalseNous allons maintenant soumettre les travaux en mode batch.
with Batch(backend=backend) as batch:
sampler = SamplerV2(options=options)
cut_job = sampler.run(subexperiments)
print(f"Job ID {cut_job.job_id()}")Output:
Job ID cwxf7wq60bqg008pvt8g
result = cut_job.result()Étape 4 : Post-traitement et restitution du résultat dans le format classique souhaité
- Entrée : Distributions de quasi-probabilité
- Résultat : Valeurs attendues reconstruites
reconstructed_expvals = reconstruct_expectation_values(
result,
coefficients,
paulis,
)Nous calculons maintenant la moyenne des observables de type Z de weight-1 et weight-2.
cut_weight_1 = np.mean(reconstructed_expvals[:num_qubits])
cut_weight_2 = np.mean(reconstructed_expvals[num_qubits:])
print(f"Average of weight-1 expectation values is {cut_weight_1}")
print(f"Average of weight-2 expectation values is {cut_weight_2}")Output:
Average of weight-1 expectation values is -0.741733944954063
Average of weight-2 expectation values is 0.6968862385320495
Vérification croisée : obtenir la valeur attendue non ajustée
Il est utile de vérifier l'avantage de la technique de découpage du circuit par rapport à la technique non découpée. Nous calculerons ici les valeurs espérées sans couper le circuit. Notez qu'un tel circuit non coupé souffrira d'un grand nombre de portes SWAP nécessaires pour mettre en œuvre l'opération à 2 qubits entre le premier et le dernier qubit. Nous utiliserons la fonction sampled_expectation_value pour obtenir les valeurs d'espérance du circuit non coupé après avoir obtenu la distribution de probabilité via SamplerV2. Cela permet une utilisation homogène de la primitive dans toutes les instances. Toutefois, il convient de noter que nous aurions pu utiliser EstimatorV2 pour calculer directement les valeurs d'espérance.
if ansatz.num_clbits == 0:
ansatz.measure_all()
pm_uncut = generate_preset_pass_manager(
optimization_level=1, backend=backend, initial_layout=init_layout
)
transpiled_circuit = pm_uncut.run(ansatz)sampler = SamplerV2(mode=backend, options=options)
uncut_job = sampler.run([transpiled_circuit])uncut_job_id = uncut_job.job_id()
print(f"The job id for the uncut clifford circuit is {uncut_job_id}")Output:
The job id for the uncut clifford circuit is cwxfads2ac5g008jhe7g
uncut_result = uncut_job.result()[0]
uncut_counts = uncut_result.data.meas.get_counts()Nous allons maintenant calculer les valeurs moyennes de l'espérance de toutes les observables de type Z weight-1 et weight-2 sans découpage.
uncut_expvals = [
sampled_expectation_value(uncut_counts, obs) for obs in paulis
]
uncut_weight_1 = np.mean(uncut_expvals[:num_qubits])
uncut_weight_2 = np.mean(uncut_expvals[num_qubits:])
print(f"Average of weight-1 expectation values is {uncut_weight_1}")
print(f"Average of weight-2 expectation values is {uncut_weight_2}")Output:
Average of weight-1 expectation values is -0.32494128440366965
Average of weight-2 expectation values is 0.32340917431192656
Visualiser
Visualisons maintenant l'amélioration obtenue pour les observables weight-1 et weight-2 lorsque l'on utilise le découpage de porte pour le circuit en chaîne périodique
mpl.rcParams.update(mpl.rcParamsDefault)
fig = plt.subplots(figsize=(12, 8), dpi=200)
width = 0.25
labels = ["Weight-1", "Weight-2"]
x = np.arange(len(labels))
ideal = [-1, 1]
cut = [cut_weight_1, cut_weight_2]
uncut = [uncut_weight_1, uncut_weight_2]
br1 = np.arange(len(ideal))
br2 = [x + width for x in br1]
br3 = [x + width for x in br2]
plt.bar(
br1, ideal, width=width, edgecolor="k", label="Ideal", color="#4589ff"
)
plt.bar(br2, cut, width=width, edgecolor="k", label="Cut", color="#a56eff")
plt.bar(
br3, uncut, width=width, edgecolor="k", label="Uncut", color="#009d9a"
)
plt.axhline(y=0, color="k", linestyle="-")
plt.xticks([r + width for r in range(len(ideal))], labels, fontsize=14)
plt.yticks(fontsize=14)
plt.legend(fontsize=14)
plt.show()Output:
Récapitulatif
En résumé, nous avons calculé les valeurs moyennes de l'espérance des observables des types weight-1 et weight-2 Z pour une chaîne 1D périodique de 109 qubits. Pour ce faire, nous
- créé une carte de couplage virtuelle en ajoutant une connectivité à longue portée entre le premier et le dernier qubit de la chaîne 1D, et transposé le circuit.
- la transpilation à ce stade nous a permis d'éviter les frais généraux liés à la transpilation de chaque sous-expérience séparément après le découpage,
- l'utilisation de la carte de couplage virtuel nous a permis d'éviter les portes SWAP supplémentaires pour l'opération à 2 qubits entre le premier et le dernier qubit.
- a supprimé la connectivité à longue portée du circuit transpilé en coupant les portes.
- converti les circuits coupés en jeux de portes de base en appliquant les passes de transpilation appropriées.
- a exécuté les circuits de coupe sur IBM Quantum device en utilisant une primitive
SamplerV2. - a obtenu la valeur espérée en reconstruisant les résultats des circuits coupés.
Inférence
Les résultats montrent que la moyenne des observables de type weight-1 et weight-2 est nettement améliorée par la suppression des portes périodiques. Il convient de noter que cette étude n'inclut aucune technique de suppression ou d'atténuation des erreurs. L'amélioration observée est uniquement due à l'utilisation correcte du découpage de la porte pour ce problème. Les résultats auraient pu être encore améliorés en utilisant les techniques d'atténuation et de suppression.
Cette étude montre un exemple d'utilisation efficace du gate cutting pour améliorer les performances de calcul.
Enquête tutorielle
Veuillez répondre à cette courte enquête pour nous faire part de vos commentaires sur ce didacticiel. Vos commentaires nous aideront à améliorer nos offres de contenu et l'expérience des utilisateurs.