Skip to main content
IBM Quantum Platform

Présentation du transcompilateur Qiskit basé sur l'IA

Durée estimée : 5 minutes sur IBM Heron (REMARQUE : il s'agit uniquement d'une estimation.) (La durée d'exécution peut varier.)


Acquis d'apprentissage

À l'issue de ce tutoriel, les utilisateurs devraient avoir compris :

  • Comment utiliser le transpileur basé sur l'IA (generate_ai_pass_manager) en remplacement direct du transpileur standard
  • Comparaison entre le transpileur basé sur l'IA et le transpileur par défaut en termes de profondeur en deux qubits, de nombre de portes et de temps de transpilation
  • Comment utiliser les circuits miroirs pour évaluer la qualité de la transpilation lors de l'exécution matérielle

Prérequis

Nous recommandons aux utilisateurs de se familiariser avec les sujets suivants avant de suivre ce tutoriel :


Arrière-plan

Le transpileur Qiskit, optimisé par l'IA, intègre des passes de transpilation basées sur l'apprentissage automatique, capables de générer des circuits plus courts et plus efficaces sur le plan matériel que les méthodes heuristiques traditionnelles telles que SABRE. Les circuits plus courts accumulent moins de bruit, ce qui améliore directement la qualité des résultats sur du matériel quantique réel.

Dans ce tutoriel, nous comparons deux stratégies de transpilation :

Stratégie
interface de programme d"application
Par défautgenerate_preset_pass_manager(optimization_level=3, ...)
IAgenerate_ai_pass_manager(optimization_level=1, ai_optimization_level=3, ...)

Nous mesurons trois indicateurs pour chaque stratégie : la profondeur des portes à deux qubits, le nombre total de portes et le temps d'exécution de la transpilation.

Tests de performance des transpilers basés sur l'IA

Lors des tests de performance, le transpileur basé sur l'IA a systématiquement généré des circuits moins profonds et de meilleure qualité que le transpileur Qiskit standard. Pour ces tests, nous avons utilisé la stratégie par défaut du gestionnaire de passes de Qiskit, configurée avec generate_preset_pass_manager. Bien que cette stratégie par défaut soit souvent efficace, elle peut rencontrer des difficultés avec des circuits plus grands ou plus complexes. En revanche, les passes générées par l’IA ont permis de réduire en moyenne de 24 % le nombre de portes à deux qubits et de 36 % la profondeur des circuits pour les circuits de grande taille (plus de 100 qubits) lors de la transcompilation vers la topologie « heavy-hex » du matériel d’ IBM Quantum®. Pour plus d'informations sur ces tests de performance, consultez cet article de blog.

Tests de performance des transpilers basés sur l'IA

Ce tutoriel présente les principaux avantages des passes d'IA et les compare aux méthodes traditionnelles.


Exigences

Avant de commencer ce tutoriel, assurez-vous d'avoir installé les éléments suivants :

  • Qiskit SDK v2.0 ou version ultérieure, avec prise en charge de la visualisation
  • Qiskit Runtime (pip install qiskit-ibm-runtime) v0.22 ou version ultérieure
  • Transpileur Qiskit IBM avec mode local IA (pip install 'qiskit-ibm-transpiler[ai-local-mode]')
  • Qiskit Aer (pip install qiskit-aer)

Configuration

from qiskit import QuantumCircuit
from qiskit.circuit.random import random_circuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit_ibm_transpiler import generate_ai_pass_manager
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error
import matplotlib.pyplot as plt
from statistics import mean, stdev
import time
import logging

seed = 42


def transpile_with_metrics(pass_manager, circuit):
    """Transpile a circuit and return the result along with key metrics."""
    start = time.time()
    qc_out = pass_manager.run(circuit)
    elapsed = time.time() - start

    depth_2q = qc_out.depth(lambda x: x.operation.num_qubits == 2)
    gate_count = qc_out.size()

    return qc_out, {
        "depth_2q": depth_2q,
        "gate_count": gate_count,
        "time_s": round(elapsed, 3),
    }


def remap_to_contiguous(tqc):
    """Remap a transpiled circuit to use contiguous qubit indices.

    Transpiled circuits target specific physical qubits (e.g., qubit 45, 67)
    on a large backend. This remaps them to 0, 1, 2, ... so Aer only
    simulates the active qubits."""
    active = sorted(
        {tqc.find_bit(q).index for inst in tqc.data for q in inst.qubits}
    )
    qubit_map = {old: new for new, old in enumerate(active)}
    new_qc = QuantumCircuit(len(active))
    for inst in tqc.data:
        old_indices = [tqc.find_bit(q).index for q in inst.qubits]
        new_qc.append(inst.operation, [qubit_map[i] for i in old_indices])
    return new_qc


def build_mirror_circuit(tqc, simulate=True):
    """Build a mirror circuit: U followed by U-dagger, with measurements.

    The expected output is always |0...0>, so measuring the survival
    probability reveals how much noise each transpilation strategy adds.

    Args:
        tqc: A transpiled circuit.
        simulate: If True (default), remap to contiguous qubits so Aer
            only simulates the active qubits. If False, keep the full
            physical layout for hardware execution."""
    if simulate:
        tqc = remap_to_contiguous(tqc)
    mirror = tqc.compose(tqc.inverse())
    mirror.measure_all()
    return mirror


def print_summary(results):
    """Print a summary of each metric as mean +/- stdev across all circuits,
    along with the mean percentage improvement of AI over Default."""
    metrics = [
        ("Depth 2Q", "Depth 2Q (Default)", "Depth 2Q (AI)"),
        ("Gate Count", "Gate Count (Default)", "Gate Count (AI)"),
        ("Time (s)", "Time (Default)", "Time (AI)"),
    ]
    header = (
        f"{'Metric':<12}{'Default (mean +/- std)':>24}"
        f"{'AI (mean +/- std)':>22}{'AI % improvement':>22}"
    )
    print(header)
    print("-" * len(header))
    for label, col_def, col_ai in metrics:
        defaults = [r[col_def] for r in results]
        ais = [r[col_ai] for r in results]
        pct = [(d - a) / d * 100 for d, a in zip(defaults, ais)]
        default_str = f"{mean(defaults):.1f} +/- {stdev(defaults):.1f}"
        ai_str = f"{mean(ais):.1f} +/- {stdev(ais):.1f}"
        pct_str = f"{mean(pct):+.1f}% +/- {stdev(pct):.1f}%"
        print(f"{label:<12}{default_str:>24}{ai_str:>22}{pct_str:>22}")


def plot_metrics_and_pct(results, title_prefix):
    """Plot metric comparisons and percentage improvement of AI over Default."""
    qubits = [r["Qubits"] for r in results]
    metrics = [
        ("Depth 2Q (Default)", "Depth 2Q (AI)", "Two-Qubit Depth"),
        ("Gate Count (Default)", "Gate Count (AI)", "Gate Count"),
        ("Time (Default)", "Time (AI)", "Transpilation Time"),
    ]

    # Row 1: raw metric comparison
    fig, axs = plt.subplots(1, 3, figsize=(21, 5))
    fig.suptitle(
        f"{title_prefix}: Metric Comparison",
        fontsize=15,
        fontweight="bold",
        y=1.02,
    )
    for ax, (col_def, col_ai, label) in zip(axs, metrics):
        ax.plot(qubits, [r[col_def] for r in results], "o-", label="Default")
        ax.plot(qubits, [r[col_ai] for r in results], "s-", label="AI")
        ax.set_title(label)
        ax.set_xlabel("Number of Qubits")
        ax.set_ylabel(label)
        ax.legend()
    plt.tight_layout()
    plt.show()

    # Row 2: percentage improvement
    fig, axs = plt.subplots(1, 3, figsize=(21, 5))
    fig.suptitle(
        f"{title_prefix}: % Improvement of AI over Default",
        fontsize=15,
        fontweight="bold",
        y=1.02,
    )
    for ax, (col_def, col_ai, label) in zip(axs, metrics):
        pct = [(r[col_def] - r[col_ai]) / r[col_def] * 100 for r in results]
        ax.axhline(
            0, color="#1f77b4", linewidth=2, label="Default (baseline)"
        )
        ax.plot(qubits, pct, "s-", color="#ff7f0e", label="AI")
        ax.fill_between(qubits, 0, pct, alpha=0.15, color="#ff7f0e")
        ax.set_title(label)
        ax.set_xlabel("Number of Qubits")
        ax.set_ylabel("% Improvement")
        ax.legend()
    plt.tight_layout()
    plt.show()


# Suppress verbose AI-powered transpiler logs
logging.getLogger(
    "qiskit_ibm_transpiler.wrappers.ai_local_synthesis"
).setLevel(logging.WARNING)

Exemple de simulateur à petite échelle

Étape 1 : Mettre en correspondance les entrées classiques avec un problème quantique

Nous générons 20 circuits aléatoires de profondeur 4, dont le nombre de qubits varie entre six et 25. Ces circuits nous serviront de cas de test pour comparer les stratégies de transpilation.

num_circuits_sim = 20
depth_sim = 4
qubit_range_sim = list(range(6, 26))

circuits_sim = [
    # We have only two qubit gates, as those test how well the
    # transpiler can optimize the circuit.
    random_circuit(
        num_qubits=n,
        depth=depth_sim,
        max_operands=2,
        num_operand_distribution={2: 1},
        seed=seed + i,
    )
    for i, n in enumerate(qubit_range_sim)
]

print(
    f"Created {len(circuits_sim)} circuits with qubit counts "
    f"from {qubit_range_sim[0]} to {qubit_range_sim[-1]}"
)
circuits_sim[0].draw(output="mpl", fold=-1)

Output:

Created 20 circuits with qubit counts from 6 to 25
Output of the previous code cell

Étape 2 : Optimiser le problème pour l'exécution sur du matériel quantique

Nous créons le gestionnaire de passes par défaut (SABRE) pour le backend sélectionné. Ces deux stratégies de transpilation ciblent la carte de couplage complète du backend. La simulation locale reste ensuite gérable, car l'étape de simulation utilise remap_to_contiguous pour réétiqueter chaque circuit transpilé en ne conservant que ses qubits actifs; ainsi, Aer ne simule que ces qubits plutôt que l'ensemble du dispositif.

service = QiskitRuntimeService()
backend = service.least_busy(
    min_num_qubits=100, operational=True, simulator=False
)


pm_default_sim = generate_preset_pass_manager(
    optimization_level=3,
    backend=backend,
    seed_transpiler=seed,
)
results_sim = []

for i, qc in enumerate(circuits_sim):
    n = qubit_range_sim[i]

    qc_default, m_default = transpile_with_metrics(pm_default_sim, qc)

    # Create a fresh AI pass manager each iteration to avoid stale layout state
    pm_ai = generate_ai_pass_manager(
        optimization_level=1,
        ai_optimization_level=3,
        backend=backend,
    )
    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)

    results_sim.append(
        {
            "Qubits": n,
            "Depth 2Q (Default)": m_default["depth_2q"],
            "Depth 2Q (AI)": m_ai["depth_2q"],
            "Gate Count (Default)": m_default["gate_count"],
            "Gate Count (AI)": m_ai["gate_count"],
            "Time (Default)": m_default["time_s"],
            "Time (AI)": m_ai["time_s"],
        }
    )

print_summary(results_sim)

Output:

Fetching 4 files:   0%|          | 0/4 [00:00<?, ?it/s]
Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement
--------------------------------------------------------------------------------
Depth 2Q               33.0 +/- 12.9          26.4 +/- 8.0      +15.8% +/- 17.6%
Gate Count           522.0 +/- 266.0       560.5 +/- 279.1        -9.0% +/- 9.0%
Time (s)                 0.0 +/- 0.0           0.2 +/- 0.1    -893.6% +/- 362.9%

Le tableau récapitulatif présente la moyenne et l'écart-type de chaque indicateur pour l'ensemble des 20 circuits, ainsi que le pourcentage moyen d'amélioration obtenu par le transpiler basé sur l'IA par rapport au transpiler par défaut. Les valeurs positives indiquent que le transpileur basé sur l'IA a produit de meilleurs résultats; les valeurs négatives indiquent que la solution par défaut était meilleure.

Dans cet exemple à petite échelle, le transpileur basé sur l'IA permet d'obtenir une profondeur de deux qubits inférieure d'environ 16 % en moyenne, mais au prix d'une augmentation d'environ 9 % du nombre de portes. Cela met en évidence un compromis essentiel lors du choix entre ces deux stratégies : le transpileur basé sur l'IA privilégie la réduction de la profondeur (moins de couches séquentielles de portes à deux qubits), tandis que le transpileur par défaut (SABRE) privilégie la minimisation du nombre total de portes (moins d'insertions de portes SWAP). Selon votre application, un indicateur peut s'avérer plus important que l'autre.

plot_metrics_and_pct(results_sim, "Small-Scale Random Circuits")

Output:

Output of the previous code cell Output of the previous code cell

Profondeur à deux qubits : le transpileur basé sur l'IA génère généralement des circuits présentant une profondeur à deux qubits plus faible. La profondeur est l'un des principaux indicateurs que le modèle de routage basé sur l'IA a été entraîné à optimiser, et l'amélioration est visible pour la plupart des tailles de circuits, même si SABRE parvient à l'égaler ou à la dépasser sur certains circuits.

Nombre de portes : Les résultats sont très serrés à cette échelle, SABRE conservant un léger avantage dans l'ensemble. L'algorithme de routage de SABRE est conçu pour minimiser le nombre de portes SWAP insérées, ce qui réduit directement le nombre total de portes. Lorsque les circuits sont de petite taille, la différence est minime.

Temps de transpilation : le temps d'exécution de SABRE est pratiquement constant quel que soit le nombre de qubits; la taille du circuit n'a donc qu'un impact négligeable sur son temps de transpilation à cette échelle. La logique de routage principale de SABRE est hautement optimisée (elle est en grande partie implémentée en Rust). Le transpileur basé sur l'IA prend nettement plus de temps et sa durée d'exécution augmente proportionnellement à la taille du circuit, même si les temps absolus restent raisonnables pour une utilisation interactive.

Étape 3 : Exécutez à l'aide d' Qiskit primitives

Pour évaluer l'impact de la transpilation sur la fidélité du circuit, construisez des circuits miroirs à partir du cas à 10 qubits et exécutez-les sur le simulateur Aer avec un modèle de bruit simple. La sortie attendue d'un circuit miroir est toujours une chaîne de bits composée exclusivement de zéros; ainsi, la probabilité de mesurer « 0n|0\rangle^{\otimes n} » permet d'évaluer dans quelle mesure chaque stratégie de transpilation préserve la fidélité.

# Use the 10-qubit circuit (index where qubits == 10)
idx_10q = qubit_range_sim.index(10)

qc_10q = circuits_sim[idx_10q]
qc_default_10q, _ = transpile_with_metrics(pm_default_sim, qc_10q)

pm_ai = generate_ai_pass_manager(
    optimization_level=1,
    ai_optimization_level=3,
    backend=backend,
)
qc_ai_10q, _ = transpile_with_metrics(pm_ai, qc_10q)

tqc_methods = {
    "Default": qc_default_10q,
    "AI": qc_ai_10q,
}

print(
    f"Default: depth {qc_default_10q.depth()}, gates {qc_default_10q.size()}"
)
print(f"AI:      depth {qc_ai_10q.depth()}, gates {qc_ai_10q.size()}")

Output:

Default: depth 84, gates 280
AI:      depth 91, gates 343
# Build a simple depolarizing noise model
noise_model = NoiseModel()
noise_model.add_all_qubit_quantum_error(
    depolarizing_error(0.001, 1),
    ["sx", "x", "rz"],  # ~0.1% per 1Q gate
)
noise_model.add_all_qubit_quantum_error(
    depolarizing_error(0.01, 2),
    ["cx", "ecr"],  # ~1% per 2Q gate
)

aer_sim = AerSimulator(noise_model=noise_model)

shots = 10000
survival_probs = {}

for method, tqc in tqc_methods.items():
    mirror = build_mirror_circuit(tqc, simulate=True)

    sampler = SamplerV2(mode=aer_sim)
    job = sampler.run([mirror], shots=shots)
    counts = job.result()[0].data.meas.get_counts()

    all_zeros = "0" * mirror.num_qubits
    survival = counts.get(all_zeros, 0) / shots
    survival_probs[method] = survival
    print(
        f"{method:8s}  P(|00...0>) = {survival:.4f}  "
        f"({counts.get(all_zeros, 0)}/{shots})"
    )

Output:

Default   P(|00...0>) = 0.8460  (8460/10000)
AI        P(|00...0>) = 0.8121  (8121/10000)

Nous avons simulé les deux circuits à miroirs à l'aide du simulateur Aer, en utilisant un modèle de bruit dépolarisant simple. La probabilité de survie, définie comme la fraction de tirages qui renvoient une chaîne binaire composée uniquement de zéros, permet de quantifier le niveau de bruit introduit par chaque stratégie de transpilation.

Étape 4 : Post-traitement et restitution du résultat dans le format classique souhaité

Nous calculons la probabilité de mesurer la chaîne de bits composée uniquement de zéros à partir des deux séries. Une probabilité de survie plus élevée indique une meilleure fidélité, ce qui signifie que la transpilation a introduit moins de bruit. Le graphique ci-dessous représente le complément, soit 1 - P(|0...0>), de sorte qu'une barre plus basse indique une meilleure fidélité et que les petites différences d'erreur sont plus faciles à repérer.

# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.
# A lower bar means the transpilation introduced less noise.
error_probs = {method: 1 - p for method, p in survival_probs.items()}

fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(
    error_probs.keys(),
    error_probs.values(),
    color=["steelblue", "coral"],
)
ax.set_ylabel("1 - P(|0...0>)")
ax.set_title("Mirror Circuit Error (10-qubit, Aer Simulator)")
ax.set_ylim(0, 1)
plt.tight_layout()
plt.show()

Output:

Output of the previous code cell

Dans ce cas précis, le transpileur par défaut a généré un circuit à la fois moins profond et plus petit pour cette instance particulière à 10 qubits; on peut donc s’attendre à ce qu’il offre une meilleure fidélité. Les résultats varient d'un circuit à l'autre : comme le montre le tableau récapitulatif ci-dessus, l'avantage du transpileur basé sur l'IA réside dans une profondeur moyenne de deux qubits plus faible, et non sur chaque circuit pris individuellement. La stratégie qui offre la meilleure fidélité dépend de l'ampleur de la différence pour chaque paramètre, des caractéristiques de bruit du matériel et de la structure du circuit. Dans le cadre d'un modèle de bruit dépolarisant uniforme, le nombre total de grilles a souvent un impact plus direct sur l'erreur cumulée que la profondeur seule.


Exemple de matériel à grande échelle

Étapes 1 à 4

Ici, tous ces éléments sont intégrés dans un flux de travail clair à plus grande échelle, qui est ensuite exécuté sur du matériel quantique réel.

Le code ci-dessous génère 25 circuits aléatoires d'une profondeur de 8, pour lesquels le nombre de qubits varie entre 26 et 50. Ces circuits sont ensuite transpilés à l'aide des deux stratégies, et les mêmes métriques sont collectées. Ensuite, nous construisons des circuits miroirs à partir du cas à 26 qubits et les soumettons au backend réel.

# -------------------------Step 1-------------------------
num_circuits_hw = 25
depth_hw = 8
qubit_range_hw = list(range(26, 51))

circuits_hw = [
    # We have only two qubit gates, as those test how well the
    # transpiler can optimize the circuit.
    random_circuit(
        num_qubits=n,
        depth=depth_hw,
        max_operands=2,
        num_operand_distribution={2: 1},
        seed=seed + i,
    )
    for i, n in enumerate(qubit_range_hw)
]

print(
    f"Created {len(circuits_hw)} circuits with qubit counts "
    f"from {qubit_range_hw[0]} to {qubit_range_hw[-1]}"
)

Output:

Created 25 circuits with qubit counts from 26 to 50
# -------------------------Step 2-------------------------
pm_default = generate_preset_pass_manager(
    optimization_level=3,
    backend=backend,
    seed_transpiler=seed,
)

results_hw = []

for i, qc in enumerate(circuits_hw):
    n = qubit_range_hw[i]

    qc_default, m_default = transpile_with_metrics(pm_default, qc)

    # Create a fresh AI pass manager each iteration to avoid stale layout state
    pm_ai = generate_ai_pass_manager(
        optimization_level=1,
        ai_optimization_level=3,
        backend=backend,
    )
    qc_ai, m_ai = transpile_with_metrics(pm_ai, qc)

    results_hw.append(
        {
            "Qubits": n,
            "Depth 2Q (Default)": m_default["depth_2q"],
            "Depth 2Q (AI)": m_ai["depth_2q"],
            "Gate Count (Default)": m_default["gate_count"],
            "Gate Count (AI)": m_ai["gate_count"],
            "Time (Default)": m_default["time_s"],
            "Time (AI)": m_ai["time_s"],
        }
    )

print_summary(results_hw)

Output:

Metric        Default (mean +/- std)     AI (mean +/- std)      AI % improvement
--------------------------------------------------------------------------------
Depth 2Q              217.4 +/- 50.4        191.0 +/- 35.6      +10.9% +/- 10.7%
Gate Count         4513.3 +/- 1394.3     5227.1 +/- 1536.4       -16.4% +/- 5.8%
Time (s)                 0.1 +/- 0.0           3.5 +/- 1.5   -3588.2% +/- 643.6%
plot_metrics_and_pct(results_hw, "Large-Scale Random Circuits")

Output:

Output of the previous code cell Output of the previous code cell
# -------------------------Step 3-------------------------
# Build mirror circuits from the 26-qubit case
idx_26q = qubit_range_hw.index(26)

qc_26q = circuits_hw[idx_26q]
qc_default_26q, _ = transpile_with_metrics(pm_default, qc_26q)

pm_ai = generate_ai_pass_manager(
    optimization_level=1,
    ai_optimization_level=3,
    backend=backend,
)
qc_ai_26q, _ = transpile_with_metrics(pm_ai, qc_26q)

mirror_default_hw = build_mirror_circuit(qc_default_26q, simulate=False)
mirror_ai_hw = build_mirror_circuit(qc_ai_26q, simulate=False)

# Re-transpile to basis gates (the inverse can introduce gates like sxdg)
pm_basis = generate_preset_pass_manager(
    optimization_level=0,
    backend=backend,
)
mirror_default_hw = pm_basis.run(mirror_default_hw)
mirror_ai_hw = pm_basis.run(mirror_ai_hw)

print(
    f"Mirror circuit (Default): depth {mirror_default_hw.depth()}, "
    f"gates {mirror_default_hw.size()}"
)
print(
    f"Mirror circuit (AI):      depth {mirror_ai_hw.depth()}, "
    f"gates {mirror_ai_hw.size()}"
)

# Submit to real hardware
sampler_hw = SamplerV2(mode=backend)
sampler_hw.options.environment.job_tags = ["TUT_AITI"]

shots_hw = 500000
job_hw = sampler_hw.run([mirror_default_hw, mirror_ai_hw], shots=shots_hw)
print(f"Job submitted: {job_hw.job_id()}")

Output:

Mirror circuit (Default): depth 1577, gates 9672
Mirror circuit (AI):      depth 1235, gates 11092
Job submitted: d8gt7vm6983c73dqbg0g
# -------------------------Step 4-------------------------
result_hw = job_hw.result()

survival_probs_hw = {}
for i, method in enumerate(["Default", "AI"]):
    counts = result_hw[i].data.meas.get_counts()
    mirror = [mirror_default_hw, mirror_ai_hw][i]
    all_zeros = "0" * mirror.num_qubits
    survival = counts.get(all_zeros, 0) / shots_hw
    survival_probs_hw[method] = survival
    print(
        f"{method:8s}  P(|00...0>) = {survival:.4f}  "
        f"({counts.get(all_zeros, 0)}/{shots_hw})"
    )

# Plot 1 - P(|0...0>), the probability of an erroneous (non-zero) outcome.
# A lower bar means the transpilation introduced less noise.
error_probs_hw = {method: 1 - p for method, p in survival_probs_hw.items()}

fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(
    error_probs_hw.keys(),
    error_probs_hw.values(),
    color=["steelblue", "coral"],
)
ax.set_ylabel("1 - P(|0...0>)")
ax.set_title(f"Mirror Circuit Error (26-qubit, {backend.name})")
ax.set_ylim(0, 1)
plt.tight_layout()
plt.show()

Output:

Default   P(|00...0>) = 0.0005  (239/500000)
AI        P(|00...0>) = 0.0050  (2516/500000)
Output of the previous code cell

Analyse des résultats

Les résultats à grande échelle confirment les tendances observées dans l'exemple à petite échelle, mais cette fois à une échelle plus exigeante.

Profondeur à deux qubits : le transpileur basé sur l'IA continue d'offrir une profondeur à deux qubits nettement inférieure sur toute la gamme de tailles de circuits. L'optimisation de la profondeur est l'un des principaux objectifs pour lesquels le modèle de routage basé sur l'IA a été entraîné, et cet avantage est d'autant plus marqué que le nombre de qubits est élevé, car le problème de routage devient alors plus difficile à résoudre pour les méthodes heuristiques.

Nombre de portes : le transpileur par défaut (SABRE) génère systématiquement des circuits comportant moins de portes, quelle que soit la taille des circuits dans cette fourchette. L'algorithme heuristique de SABRE est spécialement conçu pour réduire au minimum le nombre de portes logiques, et à cette échelle, l'avantage est évident et constant.

Durée de transpilation : l'écart entre les durées de transpilation s'accentue à des échelles plus grandes. SABRE reste pratiquement constant, tandis que la durée d'exécution du transpileur basé sur l'IA augmente de manière plus marquée. Malgré cela, l'environnement d'exécution du transpileur basé sur l'IA reste pratique pour la plupart des flux de travail.

Fidélité du circuit miroir : les deux méthodes donnent des probabilités de survie bien inférieures à 1 % à cette échelle, ce qui ne laisse qu’un signal très faible exploitable. Avec un nombre total de portes avoisinant les 10 000 et des profondeurs de deux qubits dépassant 1 000, le bruit de dépolarisation accumulé dans le circuit à miroirs l'emporte sur la majeure partie du signal. Cela met en évidence une limite majeure de l'approche par circuit miroir : bien qu'elle soit simple et ne nécessite aucune simulation classique, elle ne s'adapte pas bien aux circuits de grande taille ou profonds, où les deux méthodes sont poussées près du seuil de bruit et où le faible signal qui subsiste est dominé par l'erreur accumulée.

Si ces résultats soulignent l'efficacité du transpileur basé sur l'IA, il est important de noter ses limites. La méthode de synthèse par IA n'est actuellement disponible que pour certaines cartes de couplage, ce qui pourrait limiter son champ d'application. Il convient de tenir compte de cette contrainte lors de l'évaluation de son utilisation dans différents scénarios.


Etapes suivantes

Recommandations

Si ce travail vous a paru intéressant, les documents suivants pourraient vous intéresser :

Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.