Skip to main content
IBM Quantum Platform

QESEM : une fonction Qiskit par Qedma

Consultez la documentation de l'API

  • Le code présenté sur cette page a été développé conformément aux exigences suivantes. Nous vous recommandons d'utiliser ces versions ou des versions plus récentes.

    qiskit[all]~=2.5.0
    qiskit-ibm-runtime~=0.47.0
    
Remarque

Les fonctions Qiskit sont une fonctionnalité expérimentale disponible uniquement pour IBM Quantum® Premium Plan, Flex Plan, et On-Prem (via IBM Quantum Platform API). Elles sont en cours de publication et peuvent être modifiées.


Aperçu

Bien que les unités de traitement quantique se soient considérablement améliorées ces dernières années, les erreurs dues au bruit et aux imperfections du matériel existant restent un défi majeur pour les développeurs d'algorithmes quantiques. À mesure que le domaine se rapproche des calculs quantiques à l'échelle industrielle qui ne peuvent être vérifiés de manière classique, les solutions permettant d'annuler le bruit avec une précision garantie deviennent de plus en plus importantes. Pour relever ce défi, Qedma a développé Quantum Error Mitigation (QESEM), intégré de manière transparente sur IBM Quantum Platform en tant que fonction Qiskit.

Grâce à QESEM, les utilisateurs peuvent exécuter leurs circuits quantiques sur des QPU sujets au bruit afin d'obtenir des résultats d'une grande précision et exempts d'erreurs, avec des surcoûts en temps de calcul sur QPU très efficaces, proches des limites fondamentales. Pour y parvenir, QESEM s'appuie sur un ensemble de méthodes exclusives mises au point par Qedma, destinées à la caractérisation et à la réduction des erreurs. Les techniques de réduction des erreurs comprennent l'optimisation des portes logiques, la transpilation tenant compte du bruit, la suppression des erreurs (ES) et l'atténuation non biaisée des erreurs (EM). Grâce à cette combinaison de méthodes fondées sur la caractérisation, les utilisateurs peuvent obtenir des résultats fiables et sans erreur pour des circuits quantiques génériques de grand volume, ouvrant ainsi la voie à des applications qui ne pourraient être réalisées autrement.

Pour une description complète des composants sous-jacents, ainsi qu'une démonstration à l'échelle industrielle, consultez l'article « Reliable high-accuracy error mitigation for utility-scale quantum circuits » (Atténuation fiable et hautement précise des erreurs pour les circuits quantiques à l'échelle industrielle).


Description

Vous pouvez utiliser la fonction QESEM de Qedma pour estimer et exécuter facilement vos circuits en supprimant et en atténuant les erreurs, ce qui permet d'obtenir des volumes de circuits plus importants et des précisions plus élevées. Pour utiliser QESEM, vous devez fournir un circuit quantique, un ensemble d'observables à mesurer, une précision statistique cible pour chaque observable et une QPU choisie. Avant d'exécuter le circuit avec la précision souhaitée, vous pouvez estimer le temps nécessaire à la QPU sur la base d'un calcul analytique qui ne nécessite pas l'exécution du circuit. Une fois que vous êtes satisfait de l'estimation du temps de QPU, vous pouvez exécuter le circuit avec QESEM.

Lorsque vous exécutez un circuit, QESEM exécute un protocole de caractérisation des dispositifs adapté à votre circuit, ce qui permet d'obtenir un modèle de bruit fiable pour les erreurs qui se produisent dans le circuit. Sur la base de cette caractérisation, QESEM met d'abord en œuvre une transpilation sensible au bruit afin de mapper le circuit d'entrée sur un ensemble de qubits et de portes physiques, ce qui minimise le bruit affectant l'observable cible. Il s'agit notamment des portes disponibles en natif (CX/CZ sur les dispositifs d' IBM® ), ainsi que des portes supplémentaires optimisées par QESEM, formant l'ensemble de portes étendu de QESEM. QESEM exécute ensuite un ensemble de circuits ES et EM basés sur la caractérisation sur le QPU et recueille les résultats des mesures. Ces données sont ensuite traitées de manière classique afin d'obtenir une valeur attendue non biaisée et une barre d'erreur pour chaque observable, correspondant à la précision requise.

Qedma QESEM vue d'ensemble

Il a été démontré que le QESEM fournit des résultats de haute précision pour une variété d'applications quantiques et sur les plus grands volumes de circuits réalisables aujourd'hui. QESEM offre les fonctionnalités suivantes, démontrées dans la section "benchmarks" ci-dessous :

  • Précision garantie : QESEM fournit des estimations non biaisées pour les valeurs attendues des observables. Sa méthode EM est dotée de garanties théoriques qui, associées à la caractérisation de pointe de Qedma, garantissent que l'atténuation converge vers la sortie du circuit sans bruit jusqu'à la précision spécifiée par l'utilisateur. Contrairement à de nombreuses méthodes EM heuristiques sujettes à des erreurs systématiques ou à des biais, la précision garantie de QESEM est essentielle pour assurer des résultats fiables dans les circuits quantiques génériques et les observables.
  • Évolutivité vers des QPU de grande taille : le temps de QPU de QESEM dépend du volume des circuits, mais est par ailleurs indépendant du nombre de qubits. Qedma a fait la démonstration de QESEM sur les plus grands dispositifs quantiques disponibles à ce jour, notamment les dispositifs Eagle à 127 qubits et Heron à 133 qubits de l' IBM Quantum.
  • Indépendant de l'application : Le QESEM a fait ses preuves dans diverses applications, notamment la simulation hamiltonienne, le VQE, le QAOA et l'estimation d'amplitude. Les utilisateurs peuvent saisir n'importe quel circuit quantique et observable à mesurer, et obtenir des résultats précis et sans erreur. Les seules limitations sont dictées par les spécifications du matériel et le temps alloué à la QPU, qui déterminent les volumes de circuits accessibles et les précisions de sortie. En revanche, de nombreuses solutions de réduction des erreurs sont spécifiques à une application ou impliquent des heuristiques non contrôlées, ce qui les rend inapplicables aux circuits et applications quantiques génériques.
  • Ensemble de portes étendu : QESEM prend en charge les portes à angle fractionnaire et fournit des portes d' Rzz(θ)Rzz(\theta) s à angle fractionnaire optimisées pour Qedma sur les dispositifs IBM Quantum Heron et Eagle. Cet ensemble de portes étendu permet une compilation plus efficace et débloque des volumes de circuits jusqu'à deux fois plus importants par rapport à la compilation CX/CZ par défaut.
  • Observables multibases : QESEM prend en charge les observables d'entrée composées de nombreuses chaînes de Pauli non commutatives, telles que les hamiltoniens génériques. Le choix des bases de mesure et l'optimisation de l'allocation des ressources QPU (plans et circuits) sont alors effectués automatiquement par QESEM afin de minimiser le temps requis par QPU pour la précision demandée. Cette optimisation, qui prend en compte la fidélité du matériel et les taux d'exécution, vous permet d'exécuter des circuits plus profonds et d'obtenir des précisions plus élevées.

Tests de performances

QESEM a été testé sur une grande variété de cas d'utilisation et d'applications. Les exemples suivants peuvent vous aider à évaluer les types de charges de travail que vous pouvez exécuter avec QESEM.

Un chiffre clé pour quantifier la difficulté de l'atténuation des erreurs et de la simulation classique pour un circuit et un observable donnés est le volume actif : le nombre de portes CNOT affectant l'observable dans le circuit. Le volume actif dépend de la profondeur et de la largeur du circuit, du poids de l'observable et de la structure du circuit, qui détermine le cône lumineux de l'observable. Pour plus de détails, voir l'exposé du 2024 IBM Quantum Summit. QESEM fournit une valeur particulièrement importante dans le régime des grands volumes, donnant des résultats fiables pour des circuits et des observables génériques.

Volume actif
Application
Nombre de qubits
Dispositif
Description du circuit
Exactitude
Temps total
Utilisation de l'environnement d'exécution
Circuit VQE8Aigle ( r3 )21 couches au total, 9 bases de mesure, 1D chaîne98 %35 min14 min
Kicked Ising28Aigle ( r3 )3 couches uniques x 3 étapes, 2D heavy-hex topology97 %22 min4 min
Kicked Ising28Aigle ( r3 )3 couches uniques x 8 étapes, 2D heavy-hex topology97 %116 min23 min
Simulation d'un hamiltonien trotteur40Aigle ( r3 )2 couches uniques x 10 marches de Trotter, 1D chaîne97 %3 heures25 min
Simulation d'un hamiltonien trotteur119Aigle ( r3 )3 couches uniques x 9 étapes de Trotter, 2D heavy-hex topology95 %6.5 heures45 minutes
Kicked Ising136Héron ( r2 )3 couches uniques x 15 étapes, 2D heavy-hex topologyDans 99 %52 min9 minutes

La précision est mesurée ici par rapport à la valeur idéale de l'observable : OidealϵOideal\frac{\langle O \rangle_{ideal} - \epsilon}{\langle O \rangle_{ideal}} où " ϵ\epsilon " est la précision absolue de l'atténuation (fixée par l'entrée de l'utilisateur), et Oideal\langle O \rangle_{ideal} est l'observable dans le circuit sans bruit. l'utilisation du temps d'exécution mesure l'utilisation du benchmark en mode batch (somme de l'utilisation des tâches individuelles), tandis que le temps total mesure l'utilisation en mode session (temps de l'expérience), qui comprend des temps classiques et de communication supplémentaires. QESEM peut être exécuté dans les deux modes, afin que les utilisateurs puissent utiliser au mieux les ressources dont ils disposent.

Les circuits Kicked Ising à 28 qubits simulent le Quasicrystal à temps discret étudié par Shinjo et al. (voir arXiv 2403.16718 et Q2B24 Tokyo ) sur trois boucles connectées de ibm_kawasaki. Les paramètres du circuit pris ici sont (θx,θz)=(0.9π,0)(\theta_x, \theta_z) = (0.9 \pi, 0), avec un état initial ferromagnétique ψ0=0n| \psi_0 \rangle = | 0 \rangle ^{\otimes n}. L'observable mesuré est la valeur absolue de l'aimantation M=128i=027ZiM = |\frac{1}{28} \sum_{i=0}^{27} \langle Z_i \rangle|. L'expérience Kicked Ising à l'échelle de l'utilitaire a été réalisée sur les 136 meilleurs qubits de ibm_fez; ce benchmark particulier a été réalisé à l'angle de Clifford (θx,θz)=(π,0)(\theta_x, \theta_z) = (\pi, 0), auquel le volume actif croît lentement avec la profondeur du circuit, ce qui - avec les fidélités élevées du dispositif - permet une grande précision avec un temps d'exécution court.

Les circuits de simulation du hamiltonien trottérisé concernent un modèle d'Ising à champ transversal à des angles fractionnaires : (θzz,θx)=(π/4,π/8)(\theta_{zz}, \theta_x) = (\pi / 4, \pi /8) et (θzz,θx)=(π/6,π/8)(\theta_{zz}, \theta_x) = (\pi / 6, \pi / 8) respectivement (voir Q2B24 Tokyo ). Le circuit à grande échelle a été exécuté sur les 119 meilleurs qubits de ibm_brisbane, tandis que l'expérience à 40 qubits a été exécutée sur la meilleure chaîne disponible. La précision est indiquée pour l'aimantation; des résultats très précis ont également été obtenus pour des observables de poids plus élevé.

Le circuit VQE a été développé en collaboration avec des chercheurs du Center for Quantum Technology and Applications du Deutsches Elektronen-Synchrotron (DESY). L'observable cible ici était un hamiltonien composé d'un grand nombre de cordes de Pauli non commutatives, ce qui met en évidence les performances optimisées de QESEM pour les observables à bases multiples. L'atténuation a été appliquée à un ansatz optimisé de manière classique; bien que ces résultats ne soient pas encore publiés, des résultats de même qualité seront obtenus pour différents circuits ayant des propriétés structurelles similaires.


Premiers pas

Authentifiez-vous à l'aide de votre clé API IBM Quantum Platform et sélectionnez la fonction QESEM Qiskit comme suit. (Cet extrait de code part du principe que vous avez déjà enregistré votre compte dans votre environnement local.)

import qiskit
from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# verify that you have access to the function
catalog.list()
# load the function
qesem_function = catalog.load("qedma/qesem")

Exemples

Exemple de mission d'estimation du temps nécessaire

backend_nameLa tâche d'estimation du temps est utile pour estimer le temps de calcul requis par le QPU pour un et un donnés pub . backend_name peut également être configuré pour n'importe quel moteur de simulation; par exemple, fake_fez.

QESEM utilise une méthode EM quasi-probabiliste fondée sur la caractérisation. Cette méthode entraîne un surcoût en temps de traitement par le QPU qui évolue approximativement selon la formule suivante :

TQPU=aeαIFVaϵ2+bT_{QPU} = a \frac{e^{\alpha IF\cdot V_a}}{\epsilon^2} + b

VaV_a représente le volume actif du circuit, ϵ\epsilon la précision visée, et IFIF l'infidélité des portes natives.

Notez que "estimate_time_only": "empirical" cette opération utilise quelques minutes de temps de QPU pour estimer la durée nécessaire à l'exécution de la tâche (si le backend est un périphérique réel; s'il s'agit d'un simulateur, aucun temps de QPU n'est utilisé). Cela prendra généralement environ 5 minutes, mais pas plus de 10 minutes. Si l'infidélité varie considérablement entre la tâche d'estimation empirique du temps et la tâche d'atténuation, le temps de traitement par QPU variera également considérablement.

Pour commencer, essayez cet exemple de base qui consiste à estimer le temps nécessaire à la QPU pour exécuter QESEM pour une adresse pub:

backend_name = "fake_fez"

circ = qiskit.QuantumCircuit(5)
circ.cx(0, 1)
circ.cx(2, 3)
circ.cx(1, 2)
circ.cx(3, 4)

avg_magnetization = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
    [("Z", [q], 1 / 5) for q in range(5)], num_qubits=5
)
other_observable = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
    [("ZZ", [0, 1], 1.0), ("XZ", [1, 4], 0.5)], num_qubits=5
)

time_estimation_job = qesem_function.run(
    pubs=[(circ, [avg_magnetization, other_observable])],
    options={
        "estimate_time_only": "empirical",
    },
    backend_name=backend_name,  # example: "fake_fez", "ibm_fez"
)
time_estimate_result = (
    time_estimation_job.result()
)  # a list of results per pub (circuit)

L'extrait de code suivant explique comment récupérer différentes métriques d'exécution à partir de la tâche d'estimation de la durée (estimate_time_only lorsque est activée) :

pub_result = time_estimate_result[0]

print(
    f"The estimated QPU time for mitigation for this PUB is: {pub_result.metadata['time_estimation_sec']}"
)
print(
    f"The QPU time that this time estimation job took is (here it is 0 because we used fake_fez): {pub_result.metadata['total_qpu_time']}"
)
print(
    f"Gates fidelity measured during the experiment: {pub_result.metadata['gate_fidelities']}"
)
print(f"Total shots: {pub_result.metadata['total_shots']}")
print(f"Resource usage breakdown: {pub_result.metadata['resource_usage']}")

Output:

The estimated QPU time for mitigation for this PUB is: 300
The QPU time that this time estimation job took is (here it is 0 because we used fake_fez): 0
Gates fidelity measured during the experiment: {'CZ': 0.9951354916722668, 'ID1Q': 0.9991246627329172}
Total shots: 220000
Resource usage breakdown: {'RUNNING: MAPPING': {'CPU_TIME': 33.6066133165732, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU_TIME': 184.53575124032795, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: WAITING_FOR_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: EXECUTING_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: POST_PROCESSING': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}}

Lorsque l'estimation empirique du temps est activée, QESEM exécute un petit nombre de circuits afin de fournir une estimation plus précise du temps de calcul du QPU. Les résultats des mesures d'atténuation relatives à ces circuits sont disponibles dans la empirical_estimation_mitigation_results section « sur site » de la mission.

Ces résultats étant issus d'un échantillon limité de circuits, leur précision est nettement inférieure à celle des résultats finaux obtenus à l'issue de l'ensemble du processus d'atténuation par QESEM. Toutefois, lorsque le circuit est de petite taille ou que la précision visée est faible, les mesures d'atténuation mises en œuvre lors de l'estimation empirique du temps peuvent s'avérer suffisantes pour atteindre la précision souhaitée, et il n'est alors pas forcément nécessaire de procéder à l'ensemble des mesures d'atténuation.

Le empirical_estimation_mitigation_results champ est une liste PrimitiveResult d'objets par paramètre d'entrée. Si le circuit n'est pas paramétré, la liste comportera un élément.

empirical_estimation_mitigation_results = time_estimate_result[0].metadata[
    "empirical_estimation_mitigation_results"
][0]  # a list per parameter
print("Partial results for the observables:")

print(
    f"    Mitigated expectation values: {empirical_estimation_mitigation_results.data.evs}"
)
print(
    f"    Mitigated error bars: {empirical_estimation_mitigation_results.data.stds}"
)
print(
    f"    Number of shots used for mitigation: {empirical_estimation_mitigation_results.metadata['mitigation_shots']}"
)
transpiled_circ = empirical_estimation_mitigation_results.metadata[
    "transpiled_circ"
]
print(f"    Qubit mapping: {transpiled_circ['qubit_maps']}")
print(
    f"    Number of measurement bases: {transpiled_circ['num_measurement_bases']}\n"
)

# results per obs
emp_obs_results = empirical_estimation_mitigation_results.metadata["results"][
    0
]
# print(f"Results for each observable: {results}")
print("Results for each observable:")

for i, (obs_array, result_dict) in enumerate(emp_obs_results):
    # obs_array, result_dict = results
    print(f"Observable {i+1}: {obs_array}")
    print(
        f"    QESEM mitigated value: {result_dict['qesem']['value']} \u00b1 {result_dict['qesem']['error_bar']}"
    )

Output:

Partial results for the observables:
    Mitigated expectation values: [1.00347302 1.00693905]
    Mitigated error bars: [0.00304061 0.00714276]
    Number of shots used for mitigation: 180000
    Qubit mapping: [[[0, 136], [1, 143], [2, 142], [3, 141], [4, 140]]]
    Number of measurement bases: 2

Results for each observable:
Observable 1: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
    QESEM mitigated value: 1.003473015776871 ± 0.0030406128032204015
Observable 2: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
    QESEM mitigated value: 1.0069390542613554 ± 0.0071427606736885925

Exemple de tâche d'atténuation dans QESEM

L'exemple suivant exécute un travail QESEM :

sample_job = qesem_function.run(
    pubs=[(circ, [avg_magnetization, other_observable])],
    backend_name=backend_name,  # example: "ibm_fez"
    # options = {
    #     "estimate_time_only": "empirical",
    #     "default_precision": 0.2,  # Default precision is applied to all pubs that don't have a precision specified, see API reference for more details
    #     "max_execution_time": 3600,  # You can specify a maximum QPU time in seconds, see API reference for more details
    #     "transpilation_level": "standard",  # "minimal_with_layout_opt" for minimal transpilation, see API reference for more details
    #     "parallel_execution": True,  # True for parallel execution, see API reference for more details
    # },
)

Pour une description détaillée de chaque champ de saisie et de chaque option, consultez la documentation de référence de l'API QESEM.

Vous pouvez utiliser les API Qiskit Serverless familières pour vérifier l'état de votre charge de travail Qiskit Function ou renvoyer des résultats :

# Print the ID so you can use it later, if necessary
print(sample_job.job_id)
print(sample_job.status())
sample_result = sample_job.result()

Output:

3ac6b2df-15b0-4dc0-8f48-cf14bd20a1c8
DONE

L'extrait de code suivant montre comment récupérer les résultats des mesures d'atténuation et les indicateurs d'exécution. Ces données contiennent des informations essentielles qui permettent de mieux comprendre l'influence des différents paramètres sur le déroulement de l'analyse QESEM. Cela peut également s'avérer utile lorsque vous rédigez un article à partir de vos travaux de recherche.

for pub_idx, pub_result in enumerate(
    sample_result
):  # each element in the list is a result for a different pub, here we sent only one pub
    print(f"\nPUB {pub_idx}:")
    print(
        f"  The QPU time that this job took is (here it is 0 because we used fake_fez): {pub_result.metadata['total_qpu_time']}"
    )
    print(
        f"  Gates fidelity measured during the experiment: {pub_result.metadata['gate_fidelities']}"
    )
    print(f"  Total shots: {pub_result.metadata['total_shots']}")
    print(
        f"  Number of shots used for mitigation: {pub_result.metadata['mitigation_shots']}"
    )
    print(
        f"  Resource usage breakdown: {pub_result.metadata['resource_usage']}"
    )

Output:


PUB 0:
  The QPU time that this job took is (here it is 0 because we used fake_fez): 0.0
  Gates fidelity measured during the experiment: {'CZ': 0.9953704216147041, 'ID1Q': 0.9991834123567518}
  Total shots: 446000
  Number of shots used for mitigation: 194000
  Resource usage breakdown: {'RUNNING: MAPPING': {'CPU_TIME': 32.52745003718883, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU_TIME': 257.850521848537, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: WAITING_FOR_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: EXECUTING_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: POST_PROCESSING': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}}

Dans metadata["results"], les résultats sont d'abord regroupés par instance de circuit, puis par grandeur observable.

print("Full QESEM mitigation results:")

for pub_idx, pub_result in enumerate(sample_result):
    print(f"\nPUB {pub_idx}:")

    print(f"  Mitigated expectation values: {pub_result.data.evs}")
    print(f"  Mitigated error bars: {pub_result.data.stds}")
    noisy_results = pub_result.metadata.get("noisy_results")
    print(f"  Unmitigated expectation values: {noisy_results.evs}")
    print(f"  Unmitigated error bars: {noisy_results.stds}")

    print("  Transpiled circuits:")
    for circ_idx, transpiled_circ in enumerate(
        pub_result.metadata["transpiled_circs"]
    ):
        print(f"    Circuit {circ_idx}:")
        # print(f"      Circuit: \n {transpiled_circ['circuit']}") # not printing it because it's long but you can see the transpiled circuit itself
        print(f"      Qubit mapping: {transpiled_circ['qubit_maps']}")
        print(
            f"      Measurement bases: {transpiled_circ['num_measurement_bases']}"
        )

    print("  Results for each observable:")
    for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
        print(f"    Circuit {circ_idx}:")
        for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
            print(f"      Observable {obs_idx}: {obs_array}")
            print(
                f"        QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
            )
            print(
                f"        Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
            )

Output:

Full QESEM mitigation results:

PUB 0:
  Mitigated expectation values: [1.00648343 1.00636289]
  Mitigated error bars: [0.00253812 0.00693586]
  Unmitigated expectation values: [0.98031429 0.96357143]
  Unmitigated error bars: [0.00124128 0.00578812]
  Transpiled circuits:
    Circuit 0:
      Qubit mapping: [[[0, 140], [1, 141], [2, 142], [3, 143], [4, 136]]]
      Measurement bases: 2
  Results for each observable:
    Circuit 0:
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        QESEM mitigated value: 1.0064834305181962 ± 0.002538119914534849
        Unmitigated value: 0.9803142857142859 ± 0.0012412835813609938
      Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        QESEM mitigated value: 1.0063628870614818 ± 0.006935859820870656
        Unmitigated value: 0.9635714285714285 ± 0.005788121870526659

Répartition des principaux résultats :

  • mitigated: la valeur attendue du QESEM après atténuation complète.
  • unmitigated: le résultat brut du bruit physique, sans correction des erreurs.

Résultats de l'extrapolation heuristique QESEM

Dans une session QESEM standard avec un seul precision flotteur, les résultats comprennent également des points de mise à l'échelle du bruit disponibles automatiquement, qui sont utilisés pour l'heuristique QESEM. Ces points sont calculés sans recourir à des ressources QPU supplémentaires.

L'échelle 1.0 représente le niveau de bruit physique de l'appareil avec atténuation de la lecture (REM), tandis que l'échelle 2.0 correspond au point complémentaire amplifié par le bruit, également avec REM. Ces points servent à obtenir le qesem_heuristic résultat.

  • qesem_heuristic: une estimation de type ZNE calculée à partir des données disponibles, pondérées en fonction du bruit. Actuellement, cette méthode utilise une extrapolation exponentielle.
  • noise_scaling.results_with_REM: valeurs attendues à différentes échelles de bruit, toutes avec atténuation de la lecture (REM).

Un détail subtil mais important réside dans le fait que le résultat de la mise à l'échelle 1.0 n'est pas le même que le unmitigated résultat. Ces deux valeurs correspondent au niveau de bruit physique de l'appareil, mais la valeur de l'échelle 1.0 tient compte de l'atténuation liée à la lecture, contrairement unmitigated à.

print("QESEM heuristic results:")

for pub_idx, pub_result in enumerate(sample_result):
    print(f"\nPUB {pub_idx}:")
    for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
        print(f"  Circuit {circ_idx}:")
        for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
            print(f"    Observable {obs_idx}: {obs_array}")
            qesem_heuristic = result_dict["qesem_heuristic"][0]
            print("      QESEM heuristic:")
            print(
                f"        Value: {qesem_heuristic['value']} ± {qesem_heuristic['error_bar']}"
            )
            print(
                f"        Extrapolation: {qesem_heuristic['extrapolation']}"
            )
            print(
                f"        Scale factors: {qesem_heuristic['scale_factors']}"
            )
            noise_scaling = result_dict["noise_scaling"]
            print("      Noise scaling results:")
            print(
                f"        Scaling method: {noise_scaling['scaling_method']}"
            )
            print("        Results with Readout mitigation (REM):")
            for rem_result in sorted(
                (
                    item
                    for item in noise_scaling["results_with_REM"]
                    if item["scale"] != 0.0
                ),
                key=lambda item: item["scale"],
            ):
                print(
                    f"          Scale factor {rem_result['scale']}: {rem_result['value']} ± {rem_result['error_bar']}"
                )

Output:

QESEM heuristic results:

PUB 0:
  Circuit 0:
    Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
      QESEM heuristic:
        Value: 1.0008161638888535 ± 0.0038859458884403964
        Extrapolation: exponential
        Scale factors: [1.0, 2.0]
      Noise scaling results:
        Scaling method: QESEM
        Results with Readout mitigation (REM):
          Scale factor 1.0: 0.9918395459270772 ± 0.0012565417579355634
          Scale factor 2.0: 0.982943441922748 ± 0.0028919278067695018
    Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
      QESEM heuristic:
        Value: 0.9960853148925298 ± 0.013811635038961175
        Extrapolation: exponential
        Scale factors: [1.0, 2.0]
      Noise scaling results:
        Scaling method: QESEM
        Results with Readout mitigation (REM):
          Scale factor 1.0: 0.9902860583785115 ± 0.005921236914723409
          Scale factor 2.0: 0.984520565414274 ± 0.006909522262347639
Note

Les exemples suivants se concentrent sur les données d'entrée et les résultats spécifiques à certaines fonctionnalités; ils n'affichent donc pas à chaque fois l'intégralité des métriques d'exécution. Les métadonnées de premier niveau présentées précédemment, telles que temps_total_qpu, gate_fidelities, nombre_de_tirs, mesures_d'atténuation et utilisation_des_ressources, sont également disponibles pour ces missions.

Par souci de concision, certaines variables des exemples précédents, notamment le backend, les observables et les paramètres du circuit de base, sont réutilisées ci-dessous.

Note

Tous les exemples suivants peuvent également être exécutés avec une estimation empirique du temps. Pour l'activer, passez « "estimate_time_only" : "empirique" » dans les options de la fonction.

Exemple de circuit paramétré

De nombreux algorithmes évaluent le même circuit pour plusieurs valeurs de paramètres. L'envoi du circuit paramétré sous la forme d'une seule tâche QESEM permet à QESEM de réutiliser les résultats de caractérisation et d'étalonnage pour toutes les instances du circuit, ce qui peut réduire la charge en temps de calcul par rapport à l'exécution de tâches distinctes.

Pour soumettre un circuit paramétré, il faut utiliser le "minimal_with_layout_opt" niveau de transpilation. Les circuits soumis à ce niveau doivent déjà être exprimés à l'aide des portes de base du backend, en fonction de ce dernier. À ce niveau, QESEM conserve la structure soumise aussi proche que possible du circuit d'entrée, respecte les barrières lors de la stratification (regroupement des opérations en couches de portes parallèles à deux qubits) et gère toujours automatiquement le mappage matériel vers des qubits haute fidélité ainsi que la connectivité des dispositifs.

Concrètement, cela signifie que vous devez transcompiler les circuits en portes de base du backend cible avant de les soumettre. Vous trouverez ci-dessous un exemple simple de transpilation « basis-gate ».

Actuellement, QESEM ne prend en charge qu'une seule grandeur observable par jeu de paramètres. Les deux lignes de paramètres ci-dessous sont associées aux deux observables : la première ligne est mesurée avec avg_magnetization, et la deuxième ligne est mesurée avec other_observable.

# Transpile to the backend basis gates only. With minimal_with_layout_opt, QESEM handles hardware mapping/connectivity and observable layout internally.
from qiskit_ibm_runtime.fake_provider import FakeFez

backend = FakeFez()
basis = backend.operation_names
print(basis)

param0 = qiskit.circuit.Parameter("param0")
param1 = qiskit.circuit.Parameter("param1")
parametrized_circ = qiskit.QuantumCircuit(5)
parametrized_circ.rx(param0, 0)
parametrized_circ.rx(param1, 1)
parametrized_circ.cx(0, 1)
parametrized_circ.cx(2, 3)
parametrized_circ.cx(1, 2)
parametrized_circ.cx(3, 4)

parametrized_circ = qiskit.transpile(
    parametrized_circ, basis_gates=basis, optimization_level=1
)
parametrized_parameter_values = [[0.5, 0.1], [0.0, 0.6]]
parametrized_observables = [avg_magnetization, other_observable]

parametrized_job = qesem_function.run(
    pubs=[
        (
            parametrized_circ,
            parametrized_observables,
            parametrized_parameter_values,
            0.1,
        )
    ],
    backend_name=backend_name,
    options={
        "max_execution_time": 300,
        "transpilation_level": "minimal_with_layout_opt",
    },
)
print(parametrized_job.job_id)
print(parametrized_job.status())

Output:

d1b0e29b-196c-4896-aec6-44a268ebd874
DONE
parametrized_result = parametrized_job.result()
print("Parameterized circuit QESEM results:")

for pub_idx, pub_result in enumerate(parametrized_result):
    print(f"\nPUB {pub_idx}:")
    print(f"  Mitigated expectation values: {pub_result.data.evs}")
    print(f"  Mitigated error bars: {pub_result.data.stds}")
    noisy_results = pub_result.metadata["noisy_results"]
    print(f"  Unmitigated expectation values: {noisy_results.evs}")
    print(f"  Unmitigated error bars: {noisy_results.stds}")
    print("  Results for each parameter value:")
    for param_idx, param_results in enumerate(pub_result.metadata["results"]):
        print(
            f"    Parameter set {param_idx}: {parametrized_parameter_values[param_idx]}"
        )
        for obs_idx, (obs_array, result_dict) in enumerate(param_results):
            print(f"      Observable {obs_idx}: {obs_array}")
            print(
                f"        QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
            )
            print(
                f"        Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
            )

Output:

Parameterized circuit QESEM results:

PUB 0:
  Mitigated expectation values: [0.92392021 0.82517653]
  Mitigated error bars: [0.00565281 0.00616016]
  Unmitigated expectation values: [0.9028     0.78771429]
  Unmitigated error bars: [0.00335142 0.00925413]
  Results for each parameter value:
    Parameter set 0: [0.5, 0.1]
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        QESEM mitigated value: 0.923920210615709 ± 0.005652811570890183
        Unmitigated value: 0.9028 ± 0.0033514176105045447
    Parameter set 1: [0.0, 0.6]
      Observable 0: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        QESEM mitigated value: 0.8251765289285893 ± 0.006160161743353999
        Unmitigated value: 0.7877142857142858 ± 0.009254130564027902

Exemple « Multi-pub »

L'exécution multi-pub est utile lorsque vous souhaitez exécuter plusieurs circuits liés les uns aux autres au sein d'un même travail QESEM. Tout comme l'exécution paramétrée, cela permet de réduire la charge de travail, car QESEM peut réutiliser les résultats de caractérisation et d'étalonnage d'une instance de circuit à l'autre, au lieu de les répéter dans des tâches distinctes.

Cela s'avère particulièrement utile pour les circuits dotés d'une structure à couches partagées, comme les charges de travail de type Trotter, dans lesquelles différents circuits réutilisent les mêmes couches uniques. Dans ce cas, les exécuter simultanément peut permettre de réduire les coûts de caractérisation par rapport à des sessions QESEM menées séparément.

Les missions impliquant plusieurs établissements nécessitent "transpilation_level": "minimal_with_layout_opt". Comme dans l'exemple paramétré, les circuits doivent être transpilés en portes de base du backend cible avant d'être soumis. QESEM gère ensuite en interne la connectivité des dispositifs, leur disposition et leur mise en correspondance avec des qubits haute fidélité.

Chaque PUB ci-dessous contient un circuit et les deux mêmes grandeurs observables utilisées précédemment dans le notebook; ainsi, la valeur renvoyée PrimitiveResult contient une PubResult `` par circuit d'entrée.

L'exemple ci-dessous utilise deux circuits de Trotter simples présentant le même motif de couche : circ_a comporte une couche de Trotter, tandis que circ_b répète deux fois le même motif de couche. Cela permet de rendre explicite la structure partagée.

def make_trotter_circuit(num_qubits, num_layers, zz_angle=0.2, x_angle=0.1):
    trotter_circ = qiskit.QuantumCircuit(num_qubits)
    for _ in range(num_layers):
        for q in range(num_qubits):
            trotter_circ.rx(x_angle, q)
        trotter_circ.barrier()
        for q in range(0, num_qubits - 1, 2):
            trotter_circ.rzz(zz_angle, q, q + 1)
        trotter_circ.barrier()
        for q in range(1, num_qubits - 1, 2):
            trotter_circ.rzz(zz_angle, q, q + 1)
        trotter_circ.barrier()
    return trotter_circ


circ_a = make_trotter_circuit(num_qubits=5, num_layers=1)
circ_b = make_trotter_circuit(num_qubits=5, num_layers=2)
multi_pubs = [
    (
        qiskit.transpile(qci, basis_gates=basis, optimization_level=1),
        [avg_magnetization, other_observable],
    )
    for qci in [circ_a, circ_b]
]

multi_circ_job = qesem_function.run(
    pubs=multi_pubs,
    backend_name=backend_name,
    options={
        "max_execution_time": 300,
        "transpilation_level": "minimal_with_layout_opt",
        "default_precision": 0.1,
    },
)
print(multi_circ_job.job_id)
print(multi_circ_job.status())

Output:

e34565b8-7262-4133-a120-de42ce624a99
DONE
multi_circ_result = multi_circ_job.result()
print("Multi-pub QESEM results:")

for pub_idx, pub_result in enumerate(multi_circ_result):
    print(f"\nPUB {pub_idx}:")
    print(f"  Mitigated expectation values: {pub_result.data.evs}")
    print(f"  Mitigated error bars: {pub_result.data.stds}")
    noisy_results = pub_result.metadata["noisy_results"]
    print(f"  Unmitigated expectation values: {noisy_results.evs}")
    print(f"  Unmitigated error bars: {noisy_results.stds}")
    print("  Results for each observable:")
    for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
        print(f"    Circuit {circ_idx}:")
        for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
            print(f"      Observable {obs_idx}: {obs_array}")
            print(
                f"        QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
            )
            print(
                f"        Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
            )

Output:

Multi-pub QESEM results:

PUB 0:
  Mitigated expectation values: [0.99502406 1.02209332]
  Mitigated error bars: [0.00483819 0.00707488]
  Unmitigated expectation values: [0.96934286 0.97271429]
  Unmitigated error bars: [0.00124855 0.00617294]
  Results for each observable:
    Circuit 0:
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        QESEM mitigated value: 0.9950240647925642 ± 0.004838188086259301
        Unmitigated value: 0.9693428571428573 ± 0.0012485470362492692
      Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        QESEM mitigated value: 1.0220933230604674 ± 0.007074884384355636
        Unmitigated value: 0.9727142857142859 ± 0.006172939375512439

PUB 1:
  Mitigated expectation values: [0.98850017 1.02555188]
  Mitigated error bars: [0.0077912  0.01672652]
  Unmitigated expectation values: [0.93682857 0.95371429]
  Unmitigated error bars: [0.00156245 0.00665735]
  Results for each observable:
    Circuit 0:
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        QESEM mitigated value: 0.988500171577252 ± 0.007791203181151346
        Unmitigated value: 0.9368285714285716 ± 0.001562451883089579
      Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        QESEM mitigated value: 1.02555188098689 ± 0.016726524388086233
        Unmitigated value: 0.9537142857142858 ± 0.006657345655544263

Exemple d'ajustement quasi-probabiliste des erreurs (QET)

Le réglage quasi-probabiliste des erreurs (QET) nécessite des valeurs d'espérance pour certains facteurs d'échelle de bruit. Cela s'avère utile pour les études de mise à l'échelle personnalisée du bruit et les processus d'extrapolation sans bruit. Le paramètre « Scale » 1.0 correspond au niveau de bruit physique; les valeurs comprises entre 0.0 et 1.0 réduisent partiellement le bruit, tandis que les valeurs supérieures à 1.0 l'amplifient.

Pour utiliser QET avec la fonction Qiskit, transmettez un dictionnaire en tant que paramètre « precision » de la fonction « PUB ». Le dictionnaire associe chaque échelle de bruit demandée à sa précision cible. Les résultats du facteur d'échelle renvoyés sont stockés dans noise_scaling.results_with_REM et intègrent une correction de la lecture. La valeur à 1.0 l'échelle n'est donc pas identique à la valeur non atténuée, car 1.0 la première tient compte de l'atténuation lors de la lecture, unmitigated contrairement à la seconde.

Lorsqu'une échelle est demandée, QESEM renvoie également l'échelle complémentaire autour de 1.0 sans consommation supplémentaire de QPU. Par exemple, la requête 0.5 peut également renvoyer 1.5, et la requête 1.3 peut également renvoyer 0.7. La précision de l'échelle complémentaire n'est pas garantie.

noise_scale_precision = {0.5: 0.15, 1.3: 0.2}

qet_job = qesem_function.run(
    pubs=[
        (
            circ,
            [avg_magnetization, other_observable],
            None,
            noise_scale_precision,
        )
    ],
    backend_name=backend_name,
    options={"max_execution_time": 300},
)
print(qet_job.job_id)
print(qet_job.status())

Output:

8195fa58-f037-4651-8715-36ce1cdc5521
DONE
qet_result = qet_job.result()
print("QET noise-scaling results:")

for pub_idx, pub_result in enumerate(qet_result):
    print(f"\nPUB {pub_idx}:")
    noisy_results = pub_result.metadata["noisy_results"]
    print(f"  Unmitigated expectation values: {noisy_results.evs}")
    print(f"  Unmitigated error bars: {noisy_results.stds}")
    print("  Results for each observable:")
    for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
        print(f"    Circuit {circ_idx}:")
        for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
            print(f"      Observable {obs_idx}: {obs_array}")
            noise_scaling = result_dict["noise_scaling"]
            print(
                f"        Scaling method: {noise_scaling['scaling_method']}"
            )
            print("        Results with Readout mitigation (REM):")
            for rem_result in sorted(
                (
                    item
                    for item in noise_scaling["results_with_REM"]
                    if item["scale"] != 0.0
                ),
                key=lambda item: item["scale"],
            ):
                print(
                    f"          Scale factor {rem_result['scale']}: {rem_result['value']} ± {rem_result['error_bar']}"
                )

Output:

QET noise-scaling results:

PUB 0:
  Unmitigated expectation values: [0.97822857 0.96171429]
  Unmitigated error bars: [0.00123812 0.00672958]
  Results for each observable:
    Circuit 0:
      Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
        Scaling method: QESEM
        Results with Readout mitigation (REM):
          Scale factor 0.5: 0.9938730340199383 ± 0.0032116907357568275
          Scale factor 0.7: 0.9963976191853445 ± 0.00036300258869586616
          Scale factor 1.0: 0.9898115079506586 ± 0.0012525947426560995
          Scale factor 1.3: 0.9864667065580341 ± 0.002633221613518526
          Scale factor 1.5: 0.9838755527197551 ± 0.002948417797996015
      Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
        Scaling method: QESEM
        Results with Readout mitigation (REM):
          Scale factor 0.5: 1.0006538544450332 ± 0.002121742014777343
          Scale factor 0.7: 1.0004159801523036 ± 0.0021671375357823794
          Scale factor 1.0: 0.9898058846339917 ± 0.00690183710903159
          Scale factor 1.3: 0.9948946719997267 ± 0.002146532859610311
          Scale factor 1.5: 0.9927220368192772 ± 0.0020875057190323882

Récupérer les messages d'erreur

Si l'état de votre charge de travail est « ERROR », utilisez job.result() pour récupérer le message d'erreur comme suit :

# Get the result and truncate for readability
result = sample_job.result()
result_str = str(result)
max_length = 500  # Adjust this value as necessary

if len(result_str) > max_length:
    truncated = (
        result_str[:max_length]
        + f"... (truncated {len(result_str) - max_length} characters)"
    )
else:
    truncated = result_str

print(truncated)

Output:

PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(2,), dtype=float64>), stds=np.ndarray(<shape=(2,), dtype=float64>), shape=(2,)), metadata={'gate_fidelities': {'CZ': 0.9979444718552628, 'ID1Q': 0.9991994239814883}, 'total_shots': 498600, 'mitigation_shots': 223400, 'transpiled_circs': [{'circuit': 'OPENQASM 3.0;\ninclude "stdgates.inc";\nbit[76] c0;\nqubit[76] q0;\nrx(0) q0[54];\nrx(0) q0[59];\nrx(0) q0[75];\nrz(pi/2) q0[54];\nrz(pi/2) q0[59];\nrz(pi/2) q0[75];\nrx(pi/2) q0[54];\nr... (truncated 3771 characters)

Obtenir de l'aide

L'équipe d'assistance de Qedma est là pour vous aider! Si vous rencontrez des problèmes ou si vous avez des questions sur l'utilisation de la fonction Qiskit de QESEM, n'hésitez pas à nous contacter. Notre personnel d'assistance, compétent et amical, est prêt à vous aider en cas de problèmes techniques ou de questions.

Vous pouvez nous envoyer un courriel à [email protected] pour obtenir de l'aide. Veuillez fournir le plus de détails possible sur le problème que vous rencontrez afin de nous aider à fournir une réponse rapide et précise. Vous pouvez également contacter votre représentant Qedma POC par e-mail ou par téléphone.

Pour nous permettre de vous aider plus efficacement, veuillez fournir les informations suivantes lorsque vous nous contactez :

  • Une description détaillée du problème
  • ID du travail
  • Tout message ou code d'erreur pertinent

Nous nous engageons à vous fournir une assistance rapide et efficace afin de vous garantir la meilleure expérience possible avec notre fonction Qiskit.

Nous cherchons constamment à améliorer notre produit et nous apprécions vos suggestions! Si vous avez des idées pour améliorer nos services ou des fonctionnalités que vous aimeriez voir apparaître, n'hésitez pas à nous envoyer vos suggestions à l'adresse [email protected].


Etapes suivantes

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