Skip to main content
IBM Quantum Platform

Tronquer les termes de Pauli pendant la rétropropagation

Ce guide explique comment configurer le mécanisme de troncature des termes de Pauli fourni par le module « qiskit_addon_obp.utils.truncating ».

La rétropropagation des opérateurs (OBP) peut être utilisée pour réduire la profondeur des circuits quantiques, au prix d'une observable plus complexe. Pour obtenir des résultats pertinents avec l'OBP, il faut généralement tronquer certains termes de la variable observable afin d'éviter qu'elle ne devienne trop volumineuse. Une façon de permettre une rétropropagation plus profonde dans le circuit, tout en évitant que l'opérateur ne devienne trop volumineux, consiste à tronquer les termes dont les coefficients sont faibles, plutôt que de les ajouter à l'opérateur. La troncature de termes peut permettre de réduire le nombre de circuits quantiques à exécuter, mais elle entraîne une certaine erreur dans le calcul final de la valeur d'espérance, proportionnelle à la valeur absolue des coefficients des termes tronqués.

La méthode de rétropropagation accepte un paramètre facultatif, TruncationErrorBudget, qui permet de configurer la troncature des termes de Pauli de faible poids pour chaque observable après la rétropropagation réussie de chaque tranche. Le nombre de termes tronqués dépend de divers paramètres de configuration définis par l'utilisateur. À l'heure actuelle, une seule stratégie de troncature est disponible : la méthode truncate_binary_search. À partir d'un observable et d'un budget donné, l'algorithme effectuera une recherche binaire sur les termes de Pauli et les coefficients de cet observable afin de déterminer le seuil optimal, de sorte que la somme des coefficients tronqués soit maximale tout en restant inférieure au budget.

Remarque : par défaut, la norme L1 est utilisée pour évaluer et bornes l'erreur de troncature; toutefois, le paramètre p_norm permet de spécifier la norme Lp à utiliser. Pour plus d'informations sur l'utilisation de ce paramètre, consultez le guide intitulé « Utilisation de différentes normes Lp pour la troncature du terme de Pauli ».

Les exemples suivants illustrent différentes façons de créer un « TruncationErrorBudget » à l'aide de la fonction setup_budget fournie.


Construisez un circuit type

Ce guide utilise les tranches de circuit suivantes :

import rustworkx.generators
from qiskit.synthesis import LieTrotter
from qiskit_addon_utils.problem_generators import (
    PauliOrderStrategy,
    generate_time_evolution_circuit,
    generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types

# Generate a linear chain of 10 qubits
linear_chain = rustworkx.generators.path_graph(10)

# Use an arbitrary XY model
hamiltonian = generate_xyz_hamiltonian(
    linear_chain,
    coupling_constants=(0.05, 0.02, 0.0),
    ext_magnetic_field=(0.02, 0.08, 0.0),
    pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,
)
# Evolve for some time
circuit = generate_time_evolution_circuit(
    hamiltonian, synthesis=LieTrotter(reps=3), time=2.0
)
# slice the circuit by gate type
slices = slice_by_gate_types(circuit)

# for visualization purposes, recombine the slices with barriers between them and draw the resulting circuit
combine_slices(slices, include_barriers=True).draw("mpl", fold=50, scale=0.6)

Output:

Output of the previous code cell

Nous allons nous intéresser à une seule grandeur observable simple :

from qiskit.quantum_info import SparsePauliOp

obs = SparsePauliOp("IIIIIZIIII")

Le cas le plus simple : un budget de troncature fixe pour chaque tranche

Le budget disponible pour la troncature des termes de Pauli peut varier à chaque étape de la rétropropagation. Pour comprendre comment cela fonctionne, nous allons d'abord examiner le cas le plus simple, celui d'un budget de troncature fixe défini par l'utilisateur.

La manière la plus simple de définir le budget de troncature consiste à utiliser l'argument max_error_per_slice . C'est d'ailleurs ce qui est présenté dans le tutoriel « Réduire la profondeur des circuits grâce à la rétropropagation des opérateurs ». Si float l'on définit max_error_per_slice sur une valeur, chaque tranche se voit alors attribuer un budget égal à cette valeur. Dans l'exemple ci-dessous, nous avons défini cette valeur à, ce qui 0.001 garantit une erreur de troncature implicite inférieure ou égale à 0.018, si les 18 tranches faisaient l'objet d'une rétropropagation.

Notez que tout budget d'erreur restant après la rétropropagation d'une tranche et la suppression des termes à faibles coefficients sera toujours ajouté au budget d'erreur de la tranche suivante.

from qiskit_addon_obp.utils.truncating import setup_budget

truncation_error_budget = setup_budget(max_error_per_slice=0.001)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget

op_budget = OperatorBudget(max_qwc_groups=10)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 11 circuit slices.
New observable contains 29 terms and 10 commuting groups.

Nous utilisons ensuite l'instance OBPMetadata et les outils fournis par le module de visualisation pour visualiser le processus de rétropropagation.

  • Le graphique en haut à gauche montre que nous disposons d'un budget suffisant pour commencer à tronquer les termes observables après la rétropropagation de la troisième tranche. À partir de la troisième tranche, nous savons que nous tronquons au moins un terme de chaque tranche que nous soumettons à la rétropropagation, car nous subissons une certaine erreur de troncature après chaque tranche.
  • Le graphique en haut à droite montre que le budget d'erreur atteint pour .003 la troisième tranche. Nous constatons une forte baisse du budget restant, ce qui signifie que certaines périodes ont été omises des données observables. Cela correspond à ce que nous avions déduit du graphique en haut à gauche.
  • Le graphique en bas à gauche montre que, à mesure que nous supprimons des termes de notre observable, notre erreur cumulée globale augmente de manière monotone. Ce graphique confirme également qu'aucun terme n'a été tronqué avant que la troisième tranche n'ait fait l'objet d'une rétropropagation.
  • Le graphique en bas à droite montre que le nombre de groupes de Pauli commutatifs dans notre observable a atteint la limite spécifiée de 10. Ce graphique montre également comment la propagation en arrière d'une couche supplémentaire entraînerait un dépassement de la limite spécifiée par notre observable, comme le montre le croisement des lignes noire et rouge.

Notez que sur tous ces graphiques, l'axe des x répertorie les tranches soumises à la rétropropagation; toutefois, comme l'OBP intervient à la fin du circuit, slice 1 correspond à la toute dernière tranche, slice 2 à celle qui la précède, et ainsi de suite.

from matplotlib import pyplot as plt
from qiskit_addon_obp.utils.visualization import (
    plot_accumulated_error,
    plot_left_over_error_budget,
    plot_num_qwc_groups,
    plot_slice_errors,
)

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

Spécifier explicitement le budget par tranche

Si vous savez comment attribuer un budget à chaque tranche de manière à optimiser les performances de la rétropropagation, vous pouvez choisir d'attribuer explicitement un budget à chaque tranche. À titre d'illustration, nous attribuerons un budget nul aux trois premières tranches et utiliserons un budget de .001 par tranche pour les tranches restantes.

# Zero out the first 3 slices' budgets
max_error_per_slice = [0.0] * 3 + [0.001] * (len(slices) - 3)

truncation_error_budget = setup_budget(
    max_error_per_slice=max_error_per_slice
)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001], max_error_total=inf, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 11 circuit slices.
New observable contains 32 terms and 10 commuting groups.

La suppression du budget des trois premières couches a eu pour conséquence qu'aucun terme n'a été tronqué avant la quatrième couche, comme le confirment trois de ces graphiques. Ce qui peut paraître quelque peu surprenant, c'est que, bien qu'aucun budget résiduel n'ait été transféré à la quatrième tranche, une période a tout de même été tronquée en utilisant le budget .001 alloué. Cela ressort clairement du graphique du haut à gauche, mais on peut également le constater sur celui du haut à droite, où la courbe du budget restant s'aplatit entre les tranches trois et quatre. Autre détail à noter : à partir de ce moment-là, au moins un terme a été tronqué, comme dans l'exemple précédent.

La conclusion principale est que, bien que nous ayons subi moins d'erreurs de troncature dans le deuxième exemple en remettant à zéro les budgets de certaines tranches, nous avons pu effectuer la rétropropagation sur le même nombre de tranches, et notre observable contient le même nombre de groupes de Pauli commutatifs. Nous pouvons confirmer que la borne de notre erreur est plus petite dans le deuxième exemple en examinant le graphique en bas à gauche.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

Définir le budget de manière périodique

Si vous disposez d'un circuit présentant un motif répétitif, tel qu'un circuit de Trotter, vous pouvez définir un budget pour ce sous-ensemble de tranches qui se répète et faire en sorte que ce budget soit utilisé pour toutes les répétitions suivantes de ces tranches.

Plus précisément, le circuit d'exemple que nous utilisons comporte six tranches qui se répètent trois fois, soit un total de 18 tranches. Nous attribuerons arbitrairement un budget nul aux couches RYY à un seul qubit et, et un budget .003 à chacune des couches RXX . Nous verrons comment le fait de définir le budget sous la forme d'une séquence de type « length-6 » entraîne son application cyclique à l'ensemble des 18 tranches.

Une fois encore, notez que les tranches sont propagées en arrière dans l'ordre inverse (c'est-à-dire en commençant par la fin). Par conséquent, la première entrée de notre budget cyclique est en réalité utilisée pour la dernière tranche, la deuxième entrée pour la tranche précédente, et ainsi de suite.

# Specify a length-6 per-slice budget.
# This will be cycled over three times to be applied to the 18 slices
max_error_per_slice = [0.0] * 4 + [0.003] * 2

truncation_error_budget = setup_budget(
    max_error_per_slice=max_error_per_slice
)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=inf, p_norm=1)
op_budget = OperatorBudget(max_qwc_groups=20)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 13 circuit slices.
New observable contains 49 terms and 14 commuting groups.

Comme le montrent les images en haut à gauche et en bas à gauche, aucune troncature n'a été effectuée sur les quatre premières coupes, car aucune marge d'erreur ne leur avait été attribuée. Les tranches cinq et six ont vu certains de leurs postes budgétaires réduits à mesure que des crédits étaient débloqués.

Comme le montre l'image en haut à gauche, une erreur relativement importante s'est produite après la rétropropagation de la tranche sept, alors même qu'aucun budget n'avait été alloué à cette tranche. En effet, seule une partie d'environ .002 du budget total .006 alloué aux tranches cinq et six a été utilisée; le reste a donc été transféré à la tranche sept et a été en grande partie dépensé, comme le montre l'image en haut à droite.

Les images en haut à gauche et en haut à droite montrent que le faible solde budgétaire restant est épuisé entre les tranches 8 et 10, et qu'un nouveau budget devient disponible à partir de la tranche 11, comme prévu. Les images en haut à gauche, en haut à droite et en bas à gauche illustrent toutes le comportement cyclique de l'argument max_error_per_slice lorsque sa longueur est inférieure au nombre de tranches. Ce comportement cyclique se serait poursuivi sur toutes les tranches, mais le critère d'arrêt max_qwc_groups a été atteint après la rétropropagation de 13 tranches, comme le montre l'image en bas à droite.

Il est également intéressant de noter que le nombre de groupes de Pauli diminue en réalité après la mise à disposition de la deuxième tranche du budget, à la tranche 11. En effet, certains groupes présentant de faibles coefficients n'ont pas pu être tronqués tant que le budget disponible n'était pas suffisant après la rétropropagation de la tranche 11; ils se sont donc accumulés dans l'observable pendant plusieurs itérations. Ce cas montre également que max_qwc_groups doit être dépassée pour que l'algorithme s'arrête.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

Limiter l'erreur totale

Outre la définition du budget d'erreur par tranche, il est possible de définir le niveau maximal d'erreur pouvant résulter de la troncature. Une fois cette limite atteinte, aucune troncature ne sera plus effectuée; toutefois, la rétropropagation se poursuivra jusqu’à ce que l’observable devienne trop grande et que l’un des critères d’arrêt soit rempli.

Nous répétons ensuite l'expérience ci-dessus en fixant une limite maximale pour max_error_total de telle sorte que le budget d'erreur soit épuisé après la rétropropagation de la septième tranche.

# Specify a length-6 per-slice budget.
# This will be cycled over 3 times to be applied to the 18 slices
max_error_per_slice = [0.0] * 4 + [0.003] * 2

truncation_error_budget = setup_budget(
    max_error_per_slice=max_error_per_slice, max_error_total=0.006
)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.0, 0.0, 0.0, 0.0, 0.003, 0.003], max_error_total=0.006, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 10 circuit slices.
New observable contains 67 terms and 20 commuting groups.

Comme prévu, notre erreur de troncature atteint son maximum à .006 (graphique en bas à gauche ). Il convient de noter que, lors de cette exécution, nous n'avons pas pu effectuer la rétropropagation de la tranche 11. Cela s'explique par le fait que nous ne disposions pas d'un budget suffisant pour tronquer les termes et que le nombre de groupes de Pauli en va-et-vient a dépassé la limite de 20, comme le montre l'image en bas à droite.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

Il peut être souhaitable de simplement plafonner le budget global sans max_error_total préciser de montant max_error_per_slice.

truncation_error_budget = setup_budget(max_error_total=0.018)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.018], max_error_total=0.018, p_norm=1)

Le résultat affiché dans la cellule ci-dessus peut paraître un peu surprenant, car la valeur est per_slice_budget définie sur max_error_total. Cela signifie que la totalité du budget disponible sera dépensée sans compter. On peut voir les choses ainsi : chaque tranche dispose de la totalité du budget (puisque nous parcourons toutes les per_slice_budget). Toutefois, tout budget déjà utilisé sera déduit du budget disponible à ce stade précis de l'algorithme.

op_budget = OperatorBudget(max_qwc_groups=10)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 9 circuit slices.
New observable contains 25 terms and 9 commuting groups.

L'image en haut à droite montre comment l'intégralité du budget d'erreur .018 est mise à la disposition de la première tranche. Aucun tronquage n'est effectué avant la troisième tranche; le budget restant reste donc constant. Le budget diminue de manière monotone, car la totalité du budget est mise à la disposition de chaque tranche issue de la rétropropagation jusqu'à épuisement de celui-ci.

Il est à noter que cette expérience a donné deux tranches de rétropropagation de moins que la première expérience de ce cahier, qui est pourtant presque identique. Cela montre que, pour certains problèmes, une répartition équitable du budget pourrait s'avérer optimale. Pour d'autres problèmes, le fait de laisser les tranches utiliser sans restriction la totalité du budget pourrait permettre d'obtenir de meilleures performances.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

Limiter simultanément le nombre de tranches soumises à la rétropropagation et l'erreur totale

Si vous ne souhaitez pas répartir le budget d'erreur sur l'ensemble du circuit, mais que vous ne voulez pas non plus l'épuiser de manière excessive, vous pouvez spécifier le nombre de tranches que vous comptez soumettre à la rétropropagation (num_slices), ainsi qu'un budget d'erreur total (max_error_total). Cela permettra de répartir le budget d'erreur de manière uniforme (conformément à p_norm) entre les tranches d'entrée.

Ici, nous limiterons à le nombre de tranches sur lesquelles nous pourrions effectuer la rétropropagation 12, tout en conservant le même budget d'erreur total.

num_slices = 12

truncation_error_budget = setup_budget(
    max_error_total=0.018, num_slices=num_slices, p_norm=1
)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.0014999999999999998], max_error_total=0.018, p_norm=1)

Nous allons maintenant tenter d'effectuer la rétropropagation des 12 tranches auxquelles nous avons alloué un certain budget à l'étape précédente. Pour ce faire, il suffit d'intégrer les 12 dernières tranches dans notre circuit. Avec p_norm=1, chacune des 12 tranches dispose d'un budget de 0.018 / num_slices = 0.0015.

bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices[-num_slices:],
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)

Comme nous avons transmis un sous-ensemble de nos tranches (dans le code slices[-num_slices:]) à backpropagate, nous devons combiner les tranches restantes après la rétropropagation avec celles qui n'ont jamais été envoyées pour la rétropropagation (dans le code slices[:-num_slices]).

Une fois que nous avons combiné toutes les tranches restantes, nous pouvons utiliser la fonction combine\_slices pour générer l'QuantumCircuit à profondeur réduite. Nous allons ensuite examiner le nombre de tranches ayant fait l'objet d'une rétropropagation par rapport à l'augmentation de la taille de notre observable.

# Recombine the slices remaining after backprop with the rest of the original circuit
reduced_circuit = combine_slices(slices[:-num_slices] + remaining_slices)

print(f"Backpropagated {num_slices - len(remaining_slices)} circuit slices.")
print(
    f"New observable contains {len(bp_obs)} terms and {len(bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 12 circuit slices.
New observable contains 29 terms and 9 commuting groups.

Les graphiques montrent que nous avons réussi à effectuer la rétropropagation sur les 12 tranches tout en conservant notre observable dans le cadre de 10 groupes de Pauli commutatifs. On constate également que l'utilisation de num_slices en combinaison avec max_error_total entraîne une répartition des budgets entre les tranches, et que le budget non utilisé est à nouveau reporté sur la tranche suivante. C'est ce qui ressort le plus clairement du graphique en haut à droite, puisque le budget est à la fois dépensé et reconstitué tout au long de la rétropropagation.

Il convient de noter que cette méthode de répartition de l'erreur a donné les meilleurs résultats (un plus grand nombre de tranches soumises à la rétropropagation), par rapport à la première expérience de ce cahier et à l'exemple qui précède immédiatement celui-ci. Dans tous ces exemples, un « budget d'erreur .018 » avait été alloué, mais la rétropropagation fonctionnait différemment selon la manière dont ce budget était réparti.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell

Utilisation de plusieurs observables

Cette méthode qiskit_addon_obp.backpropagate permet de lui transmettre une séquence d'observables. Cela simplifie le déroulement des opérations lorsqu'il s'agit de plusieurs observables cibles.

Nous le mentionnons explicitement ici afin de vous expliquer comment la stratégie de troncature gère ce type de cas. Dans le cadre de cet exemple, nous ajoutons un observable supplémentaire à celui que nous avons utilisé jusqu'à présent :

obs = [SparsePauliOp("IIIIIZIIII"), SparsePauliOp("IIIIIXIIII")]

Pour finir, nous répétons la première expérience de ce tutoriel., mais avec deux observables vers lesquelles effectuer la rétropropagation du circuit.

Dans cet exemple, cela n'a aucune incidence sur le nombre de tranches pouvant faire l'objet d'une rétropropagation. On constate toutefois que ces deux observables ont donné lieu à un nombre différent de termes de Pauli et de groupes commutatifs.

truncation_error_budget = setup_budget(max_error_per_slice=0.001)
print(truncation_error_budget)

Output:

TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
bp_obs, remaining_slices, metadata = backpropagate(
    obs,
    slices,
    operator_budget=op_budget,
    truncation_error_budget=truncation_error_budget,
)
reduced_circuit = combine_slices(remaining_slices)
print(f"Backpropagated {len(slices) - len(remaining_slices)} circuit slices.")
print(
    f"The new first observable contains {len(bp_obs[0])} terms and {len(bp_obs[0].group_commuting(qubit_wise=True))} commuting groups."
)
print(
    f"The new second observable contains {len(bp_obs[1])} terms and {len(bp_obs[1].group_commuting(qubit_wise=True))} commuting groups."
)

Output:

Backpropagated 11 circuit slices.
The new first observable contains 29 terms and 10 commuting groups.
The new second observable contains 23 terms and 8 commuting groups.

Les graphiques ci-dessous permettent d'illustrer la manière dont l'algorithme de rétropropagation gère plusieurs observables.

Tout d'abord, les graphiques en haut à gauche, en haut à droite et en bas à gauche montrent que le budget alloué à la troncature des termes est défini individuellement pour chaque variable observable. En d'autres termes, ces deux observables permettent de tronquer des termes en supposant une erreur de par 0.001 tranche rétropropagée. En raison de la nature différente des observables, cela se traduit par une utilisation différente du budget. Dans cet exemple, on constate de nombreux recoupements, ce qui n'est pas toujours le cas.

Le graphique en bas à droite montre que max_qwc_groups prend en compte toutes les grandeurs observables. Cela signifie que les termes de toutes les observables sont regroupés pour obtenir un nombre final de groupes commutatifs au niveau des qubits, qui est ensuite comparé à max_qwc_paulis. La même procédure s'applique au seuil max_paulis (qui n'est pas abordé dans ce cahier), ce qui permet de fixer une limite au nombre de termes de Pauli pour l'ensemble des observables.

fig, axes = plt.subplots(2, 2, figsize=(20, 10))
plot_slice_errors(metadata, axes[(0, 0)])
plot_left_over_error_budget(metadata, axes[(0, 1)])
plot_accumulated_error(metadata, axes[(1, 0)])
plot_num_qwc_groups(metadata, axes[(1, 1)])

Output:

Output of the previous code cell
Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.