Aller au contenu principal

Définir le niveau d'optimisation du transpileur

Versions des packages

Le code de cette page a été développé avec les dépendances suivantes. Nous recommandons d'utiliser ces versions ou des versions plus récentes.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1

Les appareils quantiques réels sont soumis au bruit et aux erreurs de portes, c'est pourquoi optimiser les circuits pour réduire leur profondeur et leur nombre de portes peut améliorer significativement les résultats obtenus lors de l'exécution de ces circuits. La fonction generate_preset_pass_manager possède un argument positionnel obligatoire, optimization_level, qui contrôle l'effort que le transpileur consacre à l'optimisation des circuits. Cet argument est un entier pouvant prendre les valeurs 0, 1, 2 ou 3. Plus le niveau d'optimisation est élevé, plus les circuits générés sont optimisés, au prix de temps de compilation plus longs. Le tableau suivant décrit les optimisations effectuées pour chaque valeur.

Niveau d'optimisationDescription
0

Aucune optimisation : généralement utilisé pour la caractérisation matérielle

  • Traduction de base
  • Layout/Routage : TrivialLayout, qui sélectionne les mêmes numéros de qubits physiques que virtuels et insère des SWAPs pour que cela fonctionne (en utilisant SabreSwap)
1

Optimisation légère :

  • Layout/Routage : le layout est d'abord tenté avec TrivialLayout. Si des SWAPs supplémentaires sont nécessaires, un layout avec un nombre minimal de SWAPs est trouvé en utilisant SabreSwap, puis VF2LayoutPostLayout est utilisé pour tenter de sélectionner les meilleurs qubits du graphe.
  • InverseCancellation
  • Optimisation des portes à 1 qubit
2

Optimisation moyenne :

  • Layout/Routage : niveau d'optimisation 1 (sans trivial) + heuristique optimisée avec une profondeur de recherche et un nombre d'essais plus importants pour la fonction d'optimisation. Comme TrivialLayout n'est pas utilisé, il n'y a pas de tentative d'utiliser les mêmes numéros de qubits physiques et virtuels.
  • CommutativeCancellation
3

Optimisation élevée :

  • Niveau d'optimisation 2 + heuristique davantage optimisée sur le layout/routage avec plus d'effort et d'essais
  • Resynthèse des blocs à deux qubits en utilisant la décomposition KAK de Cartan.
  • Passes rompant l'unitarité :
    • OptimizeSwapBeforeMeasure : déplace les mesures pour éviter les SWAPs
    • RemoveDiagonalGatesBeforeMeasure : supprime les portes avant les mesures qui n'auraient pas d'effet sur celles-ci

Le niveau d'optimisation en pratique

Les portes à deux qubits étant généralement la principale source d'erreurs, on peut quantifier approximativement l'« efficacité matérielle » de la transpilation en comptant le nombre de portes à deux qubits dans le circuit résultant. Ici, nous allons essayer les différents niveaux d'optimisation sur un circuit d'entrée composé d'une unitaire aléatoire suivie d'une porte SWAP.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime
from qiskit import QuantumCircuit
from qiskit.circuit.library import UnitaryGate
from qiskit.quantum_info import Operator, random_unitary

UU = random_unitary(4, seed=12345)
rand_U = UnitaryGate(UU)

qc = QuantumCircuit(2)
qc.append(rand_U, range(2))
qc.swap(0, 1)
qc.draw("mpl", style="iqp")

Output of the previous code cell

Nous utiliserons le backend simulé FakeSherbrooke dans nos exemples. Commençons par transpiler avec le niveau d'optimisation 0.

from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke

backend = FakeSherbrooke()

pass_manager = generate_preset_pass_manager(
optimization_level=0, backend=backend, seed_transpiler=12345
)
qc_t1_exact = pass_manager.run(qc)
qc_t1_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Le circuit transpilé comporte six portes ECR à deux qubits.

Recommençons avec le niveau d'optimisation 1 :

from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke

backend = FakeSherbrooke()

pass_manager = generate_preset_pass_manager(
optimization_level=1, backend=backend, seed_transpiler=12345
)
qc_t1_exact = pass_manager.run(qc)
qc_t1_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Le circuit transpilé contient toujours six portes ECR, mais le nombre de portes à un qubit a diminué.

Recommençons avec le niveau d'optimisation 2 :

pass_manager = generate_preset_pass_manager(
optimization_level=2, backend=backend, seed_transpiler=12345
)
qc_t2_exact = pass_manager.run(qc)
qc_t2_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

On obtient les mêmes résultats qu'avec le niveau d'optimisation 1. À noter qu'augmenter le niveau d'optimisation ne fait pas toujours une différence.

Recommençons, cette fois avec le niveau d'optimisation 3 :

pass_manager = generate_preset_pass_manager(
optimization_level=3, backend=backend, seed_transpiler=12345
)
qc_t3_exact = pass_manager.run(qc)
qc_t3_exact.draw("mpl", idle_wires=False)

Output of the previous code cell

Cette fois, il n'y a plus que trois portes ECR. Ce résultat s'explique par le fait qu'au niveau d'optimisation 3, Qiskit tente de resynthétiser les blocs de portes à deux qubits, et toute porte à deux qubits peut être implémentée avec au plus trois portes ECR. On peut obtenir encore moins de portes ECR en fixant approximation_degree à une valeur inférieure à 1, ce qui permet au transpileur de faire des approximations pouvant introduire une légère erreur dans la décomposition des portes (voir Paramètres couramment utilisés pour la transpilation) :

pass_manager = generate_preset_pass_manager(
optimization_level=3,
approximation_degree=0.99,
backend=backend,
seed_transpiler=12345,
)
qc_t3_approx = pass_manager.run(qc)
qc_t3_approx.draw("mpl", idle_wires=False)

Output of the previous code cell

Ce circuit ne comporte que deux portes ECR, mais il s'agit d'un circuit approximatif. Pour comprendre en quoi son effet diffère du circuit exact, on peut calculer la fidélité entre l'opérateur unitaire implémenté par ce circuit et l'unitaire exact. Avant d'effectuer le calcul, on réduit d'abord le circuit transpilé, qui contient 127 qubits, à un circuit ne contenant que les qubits actifs, soit deux.

import numpy as np

def trace_to_fidelity_2q(trace: float) -> float:
return (4.0 + trace * trace.conjugate()) / 20.0

# Reduce circuits down to 2 qubits so they are easy to simulate
qc_t3_exact_small = QuantumCircuit.from_instructions(qc_t3_exact)
qc_t3_approx_small = QuantumCircuit.from_instructions(qc_t3_approx)

# Compute the fidelity
exact_fid = trace_to_fidelity_2q(
np.trace(np.dot(Operator(qc_t3_exact_small).adjoint().data, UU))
)
approx_fid = trace_to_fidelity_2q(
np.trace(np.dot(Operator(qc_t3_approx_small).adjoint().data, UU))
)
print(
f"Synthesis fidelity\nExact: {exact_fid:.3f}\nApproximate: {approx_fid:.3f}"
)
Synthesis fidelity
Exact: 1.000+0.000j
Approximate: 0.992+0.000j

Ajuster le niveau d'optimisation peut modifier d'autres aspects du circuit, pas seulement le nombre de portes ECR. Pour des exemples illustrant comment le niveau d'optimisation influe sur le layout, consulte Représenter les ordinateurs quantiques.

Prochaines étapes

Recommandations