Skip to main content
IBM Quantum Platform

Führen Sie Ihren ersten Schaltkreis auf Hardware aus

  • Der Code auf dieser Seite wurde unter Berücksichtigung der folgenden Anforderungen entwickelt. Wir empfehlen die Verwendung dieser oder neuerer Versionen.

    qiskit[all]~=2.5.2
    qiskit-ibm-runtime~=0.47.0
    

Dieses Beispiel besteht aus zwei Teilen. Zunächst erstellen Sie ein einfaches „Hello World“-Quantenprogramm und führen es auf einer Quantenprozessoreinheit (QPU) aus. Da die tatsächliche Quantenforschung wesentlich robustere Programme erfordert, werden Sie im zweiten Abschnitt ( Skalierung auf eine große Anzahl von Qubits ) das einfache Programm auf ein nutzbares Niveau skalieren.


Installieren und authentifizieren

  1. Wenn Sie Qiskit noch nicht installiert haben, finden Sie Anweisungen dazu im Schnellstart -Handbuch.

    • Installieren Sie den „ IBM Quantum “-Compute-Client, um Jobs auf Quantenhardware auszuführen:

      pip install qiskit-ibm-runtime
    • Richten Sie eine Umgebung ein, um Jupyter-Notebooks lokal auszuführen:

      pip install jupyter
  2. Richten Sie Ihre Authentifizierung für den Zugriff auf Quantenhardware über den kostenlosen Open Plan ein.

    (Wenn Sie eine E-Mail-Einladung zum Beitritt zu einem Konto erhalten haben, befolgen Sie stattdessen die Schritte für eingeladene Benutzer.)

    • Gehen Sie zu, IBM Quantum Platform um sich anzumelden oder ein Konto zu erstellen.

      Wichtig

      Wenn Sie die Verbindung über einen Proxy-Server herstellen, müssen Sie v0.44.0qiskit-ibm-runtime oder eine neuere Version verwenden.

    • Generieren Sie Ihren API-Schlüssel (auch API-Token genannt) im Dashboard und kopieren Sie ihn an einen sicheren Ort.

    • Gehen Sie zur Seite „Instanzen“ und suchen Sie die Instanz, die Sie verwenden möchten. Bewegen Sie den Mauszeiger über die CRN und klicken Sie, um sie zu kopieren.

    • Speichern Sie Ihre Anmeldedaten lokal mit diesem Code:

      from qiskit_ibm_runtime import QiskitRuntimeService
      
      QiskitRuntimeService.save_account(
          # For `token`, use the 44-character API_KEY you created
          # and saved from the IBM Quantum Platform Home dashboard
          token="<your-api-key>",
          instance="<CRN>", # Optional
      )
  3. Jetzt können Sie diesen Code „ Python “ jederzeit verwenden, wenn Sie sich beim Compute-Service unter IBM Quantum authentifizieren möchten:

    from qiskit_ibm_runtime import QiskitRuntimeService
    
    # Run every time you need the service
    service = QiskitRuntimeService()
Sie verwenden keine vertrauenswürdige Python Umgebung?

Wenn Sie einen öffentlichen Computer oder eine andere ungesicherte Umgebung verwenden, befolgen Sie stattdessen die Anweisungen zur manuellen Authentifizierung, um Ihre Anmeldedaten zu schützen.


Erstellen und Ausführen eines einfachen Quantenprogramms

Die vier Schritte zum Schreiben eines Quantenprogramms mit Qiskit-Mustern sind:

  1. Das Problem in ein quantenbasiertes Format übertragen.

  2. Optimieren Sie die Schaltkreise und Operatoren.

  3. Ausführen mit einer Quantenprimitivfunktion.

  4. Analysieren Sie die Ergebnisse.

Schritt 1. Das Problem in ein quantennatives Format abbilden

In einem Quantenprogramm sind Quantenschaltungen das native Format, in dem Quantenanweisungen dargestellt werden, und Operatoren stellen die zu messenden Observablen dar. Beim Erstellen einer Schaltung erstellen Sie in der Regel ein neues QuantumCircuit Objekt und fügen dann nacheinander Anweisungen hinzu.

Die folgende Codezelle erstellt einen Schaltkreis*,* der einen Bell-Zustand erzeugt, bei dem zwei Qubits vollständig miteinander verschränkt sind.

Hinweis: Bit-Reihenfolge

Das Qiskit SDK verwendet die LSb 0-Bit-Nummerierung, wobei die nthn^{th} Ziffer den Wert 1≪n1 \ll n oder hat 2n2^n. Weitere Informationen finden Sie unter „Bit-Reihenfolge” im Qiskit SDK Abschnitt.

from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
from matplotlib import pyplot as plt

# Legacy server-side primitive before qiskit-ibm-runtime 0.50.0
# from qiskit_ibm_runtime import EstimatorOptions
# from qiskit_ibm_runtime import Estimator

# Client-side primitive introduced in qiskit-ibm-runtime 0.50.0
from qiskit_ibm_runtime.options_models import EstimatorOptions
from qiskit_ibm_runtime.executor_estimator import Estimator

# Uncomment the next line if you want to use a simulator:
# from qiskit_ibm_runtime.fake_provider import FakeBelemV2


# Create a new circuit with two qubits
qc = QuantumCircuit(2)

# Add a Hadamard gate to qubit 0
qc.h(0)

# Perform a controlled-X gate on qubit 1, controlled by qubit 0
qc.cx(0, 1)

# Return a drawing of the circuit using MatPlotLib ("mpl").
# These guides are written by using Jupyter notebooks, which
# display the output of the last line of each cell.
# If you're running this in a script, use `print(qc.draw())` to
# print a text drawing.
qc.draw("mpl")

Output:

Output of the previous code cell

Siehe QuantumCircuit in der Dokumentation für alle verfügbaren Operationen.

Bei der Erstellung von Quantenschaltungen müssen Sie auch berücksichtigen, welche Art von Daten nach der Ausführung zurückgegeben werden sollen. Qiskit bietet zwei Möglichkeiten, Daten zurückzugeben: Sie können entweder Stichprobenausgaben für eine von Ihnen zur Messung ausgewählte Gruppe von Qubits abrufen oder den Erwartungswert einer Observablen ermitteln. Bereiten Sie Ihre Arbeitsumgebung so vor, dass Sie Ihre Schaltung auf eine der beiden folgenden Arten mithilfe von Primitiven messen können (ausführlich erläutert in Schritt 3 ).

Dieses Beispiel misst Erwartungswerte mithilfe des qiskit.quantum_info Submoduls, das durch Operatoren (mathematische Objekte, die eine Aktion oder einen Prozess darstellen, der einen Quantenzustand verändert) spezifiziert wird. Die folgende Codezelle erstellt sechs Zwei-Qubit-Pauli-Operatoren: IZ, IX, ZI XI, ZZ, und XX.

# Set up six different observables.

observables_labels = ["IZ", "IX", "ZI", "XI", "ZZ", "XX"]
observables = [SparsePauliOp(label) for label in observables_labels]
Operator-Notation

Hier ist etwas wie der ZZ Operator eine Kurzform für das Tensorprodukt Z⊗ZZ\otimes Z, was bedeutet, dass Z auf Qubit 1 und Z auf Qubit 0 zusammen gemessen werden und Informationen über die Korrelation zwischen Qubit 1 und Qubit 0 gewonnen werden. Erwartungswerte wie dieser werden in der Regel auch als geschrieben ⟨Z1Z0⟩\langle Z_1 Z_0 \rangle.

Wenn der Zustand verschränkt ist, sollte die ⟨Z1Z0⟩\langle Z_1 Z_0 \rangle Messung von sich von der Messung von ⟨I1⊗Z0⟩⟨Z1⊗I0⟩\langle I_1 \otimes Z_0 \rangle \langle Z_1 \otimes I_0 \rangle unterscheiden. Für den spezifischen verschränkten Zustand, der durch unsere oben beschriebene Schaltung erzeugt wird, sollte die ⟨Z1Z0⟩\langle Z_1 Z_0 \rangle Messung von 1 und die Messung von Null ergeben ⟨I1⊗Z0⟩⟨Z1⊗I0⟩\langle I_1 \otimes Z_0 \rangle \langle Z_1 \otimes I_0 \rangle.

Schritt 2. Optimieren Sie die Schaltkreise und Operatoren

Bei der Ausführung von Schaltkreisen auf einem Gerät ist es wichtig, den Befehlssatz des Schaltkreises zu optimieren und die Gesamttiefe (grob gesagt die Anzahl der Befehle) des Schaltkreises zu minimieren. Dadurch werden Fehler und Störgeräusche reduziert, sodass Sie die bestmöglichen Ergebnisse erzielen. Darüber hinaus müssen die Befehle der Schaltung mit der Befehlssatzarchitektur (ISA) eines Backend-Geräts übereinstimmen und die Basisgatter und die Qubit-Konnektivität des Geräts berücksichtigen.

Der folgende Code instanziiert ein reales Gerät, an das ein Auftrag gesendet werden soll, und transformiert die Schaltung und die Beobachtungsgrößen so, dass sie mit der ISA dieses Backends übereinstimmen. Es ist erforderlich, dass Sie Ihre Anmeldedaten bereits gespeichert haben

service = QiskitRuntimeService()

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

# Convert to an ISA circuit and layout-mapped observables.
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

isa_circuit.draw("mpl", idle_wires=False)

Output:

Output of the previous code cell

Schritt 3. Ausführung unter Verwendung der Quantenprimitiven

Quantencomputer können zufällige Ergebnisse liefern, daher sammelt man in der Regel eine Stichprobe der Ausgabewerte, indem man den Schaltkreis mehrfach durchläuft. Sie können den Wert der Observable mithilfe der Estimator Klasse schätzen. Estimator ist eine von zwei Primitiven; die andere ist Sampler, mit der Daten von einem Quantencomputer abgerufen werden können. Diese Objekte verfügen über eine run() Methode, die die Auswahl von Schaltkreisen, Observablen und Parametern (sofern zutreffend) mithilfe eines primitiven einheitlichen Blocks ( PUB ) durchführt.

# Construct the Estimator instance.

estimator = Estimator(mode=backend)
estimator.options.resilience_level = 1
estimator.options.default_shots = 5000

mapped_observables = [
    observable.apply_layout(isa_circuit.layout) for observable in observables
]

# One pub, with one circuit to run against five different observables.
job = estimator.run([(isa_circuit, mapped_observables)])

# Use the job ID to retrieve your job data later
print(f">>> Job ID: {job.job_id()}")

Output:

>>> Job ID: dab7lm9l216s739p6q10

Nachdem ein Auftrag übermittelt wurde, können Sie entweder warten, bis der Auftrag in Ihrer aktuellen Python-Instanz abgeschlossen ist, oder die Daten zu einem späteren Zeitpunkt mit dem job_id abrufen. (Weitere Informationen finden Sie im Abschnitt zum Abrufen von Aufträgen.)

Nachdem der Job abgeschlossen ist, überprüfen Sie dessen Ausgabe über das Attribut result() des Jobs.

# This is the result of the entire submission.  You submitted one Pub,
# so this contains one inner result (and some metadata of its own).
job_result = job.result()

# This is the result from our single pub, which had six observables,
# so contains information on all six.
pub_result = job.result()[0]
Alternative: Führen Sie das Beispiel mit einem Simulator aus

Wenn Sie Ihr Quantenprogramm auf einem realen Gerät ausführen, muss Ihre Arbeitslast zunächst in einer Warteschlange warten, bevor sie ausgeführt wird. Um Zeit zu sparen, können Sie stattdessen den folgenden Code verwenden, um diese kleine Arbeitslast im fake_provider lokalen qiskit-ibm-runtime Testmodus auszuführen. Beachten Sie, dass dies nur bei einer kleinen Schaltung möglich ist. Wenn Sie im nächsten Abschnitt die Skalierung vornehmen, müssen Sie ein echtes Gerät verwenden.


# Use the following code instead if you want to run on a simulator:

from qiskit_ibm_runtime.fake_provider import FakeBelemV2
backend = FakeBelemV2()
estimator = Estimator(backend)

# Convert to an ISA circuit and layout-mapped observables.

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)
mapped_observables = [
    observable.apply_layout(isa_circuit.layout) for observable in observables
]

job = estimator.run([(isa_circuit, mapped_observables)])
result = job.result()

# This is the result of the entire submission. You submitted one Pub,
# so this contains one inner result (and some metadata of its own).

job_result = job.result()

# This is the result from our single pub, which had five observables,
# so contains information on all five.

pub_result = job.result()[0]

Schritt 4. Analysieren Sie die Ergebnisse

Der Analyseschritt ist in der Regel der Schritt, in dem Sie Ihre Ergebnisse nachbearbeiten können, beispielsweise durch Messfehlerkorrektur oder Zero Noise Extrapolation (ZNE). Sie können diese Ergebnisse zur weiteren Analyse in einen anderen Workflow einspeisen oder eine Darstellung der Schlüsselwerte und Daten erstellen. Im Allgemeinen ist dieser Schritt spezifisch für Ihr Problem. Zeichnen Sie für dieses Beispiel jeden der Erwartungswerte auf, die für unsere Schaltung gemessen wurden.

Die Erwartungswerte und Standardabweichungen für die von Ihnen in Estimator angegebenen Observablen können über die Attribute und PubResult.data.stds des PubResult.data.evs Job-Ergebnisses abgerufen werden. Um die Ergebnisse von Sampler abzurufen, verwenden Sie die PubResult.data.meas.get_counts() Funktion, die ein dict Array mit Messwerten zurückgibt, wobei die Bitstrings als Schlüssel und die Zählwerte als entsprechende Werte dienen. Weitere Informationen finden Sie in der Schnell startanleitung für den Sampler.

# Plot the result

values = pub_result.data.evs

errors = pub_result.data.stds

# plotting graph
plt.plot(observables_labels, values, "-o")
plt.xlabel("Observables")
plt.ylabel("Values")
plt.show()

Output:

Output of the previous code cell

Beachten Sie, dass für die Qubits 0 und 1 die unabhängigen Erwartungswerte von X und Z beide 0 sind, während die Korrelationen (XX und ZZ) 1 sind. Dies ist ein Kennzeichen der Quantenverschränkung.


Skalierung auf eine große Anzahl von Qubits

Im Bereich des Quantencomputings sind Arbeiten im Versorgungsmaßstab entscheidend für den Fortschritt auf diesem Gebiet. Solche Arbeiten erfordern Berechnungen in viel größerem Umfang, wobei Schaltkreise mit über 100 Qubits und über 1000 Gattern zum Einsatz kommen können. Dieses Beispiel zeigt, wie Sie mit IBM® QPUs Arbeiten im Versorgungsmaßstab durchführen können, indem Sie einen 100-Qubit-GHZ-Zustand erstellen und analysieren. Es verwendet den Qiskit-Patterns-Workflow und misst am Ende den Erwartungswert ⟨Z0Zi⟩\langle Z_0 Z_i \rangle für jedes Qubit.

Schritt 1. Das Problem abbilden

Schreiben Sie eine Funktion, die einen QuantumCircuit zurückgibt, der einen nn -Qubit-GHZ-Zustand (im Wesentlichen einen erweiterten Bell-Zustand) vorbereitet, und verwenden Sie diese Funktion dann, um einen 100-Qubit-GHZ-Zustand vorzubereiten und die zu messenden Observablen zu sammeln.

def get_qc_for_n_qubit_GHZ_state(n: int) -> QuantumCircuit:
    """This function will create a qiskit.QuantumCircuit (qc)
        for an n-qubit GHZ state.

    Args:
        n (int): Number of qubits in the n-qubit GHZ state

    Returns:
        QuantumCircuit: Quantum circuit that generate the n-qubit GHZ state,
            assuming all qubits start in the 0 state
    """
    if isinstance(n, int) and n >= 2:
        qc = QuantumCircuit(n)
        qc.h(0)
        for i in range(n - 1):
            qc.cx(i, i + 1)
    else:
        raise Exception("n is not a valid input")
    return qc


# Create a new circuit with 100 qubits in the GHZ state
n = 100
qc = get_qc_for_n_qubit_GHZ_state(n)

Als Nächstes ordnen Sie die relevanten Operatoren zu. In diesem Beispiel werden die ZZ Operatoren zwischen Qubits verwendet, um das Verhalten zu untersuchen, wenn sie sich weiter voneinander entfernen. Zunehmend ungenaue (verfälschte) Erwartungswerte zwischen weit entfernten Qubits würden das Ausmaß des vorhandenen Rauschens offenbaren.

# ZZII...II, ZIZI...II, ... , ZIII...IZ
operator_strings = [
    "Z" + "I" * i + "Z" + "I" * (n - 2 - i) for i in range(n - 1)
]

operators = [SparsePauliOp(operator) for operator in operator_strings]

Schritt 2. Optimieren Sie das Problem für die Ausführung auf Quantenhardware

Der folgende Code transformiert die Schaltung und die Observablen, damit sie mit der ISA des Backends übereinstimmen. Es ist erforderlich, dass Sie Ihre Anmeldedaten bereits gespeichert haben

service = QiskitRuntimeService()

backend = service.least_busy(
    simulator=False, operational=True, min_num_qubits=100
)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)

isa_circuit = pm.run(qc)
isa_operators_list = [op.apply_layout(isa_circuit.layout) for op in operators]

Schritt 3. Auf Hardware ausführen

Übermitteln Sie den Auftrag und aktivieren Sie die Fehlerunterdrückung mithilfe einer Technik zur Fehlerreduzierung, die als „dynamische Entkopplung“ bezeichnet wird. Der Resilienzgrad gibt an, in welchem Umfang eine Fehlertoleranz aufgebaut werden soll. Höhere Werte führen zu genaueren Ergebnissen, allerdings auf Kosten längerer Verarbeitungszeiten. Weitere Erläuterungen zu den im folgenden Code festgelegten Optionen finden Sie unter „Techniken zur Fehlerminderung und -unterdrückung “.

options = EstimatorOptions()
options.resilience_level = 1
options.dynamical_decoupling.enable = True
options.dynamical_decoupling.sequence_type = "XY4"

# Create an Estimator object
estimator = Estimator(backend, options=options)
# Submit the circuit to Estimator
job = estimator.run([(isa_circuit, isa_operators_list)])
job_id = job.job_id()
print(job_id)

Output:

dab84omrrl7c7386h52g

Schritt 4. Nachbearbeitungsergebnisse

Nach Abschluss des Auftrags zeichnen Sie die Ergebnisse auf und stellen Sie fest, dass mit ii ⟨Z0Zi⟩\langle Z_0 Z_i \rangle steigendem abnimmt, obwohl in einer idealen Simulation alle 1 sein ⟨Z0Zi⟩\langle Z_0 Z_i \rangle sollten.

# data
data = list(range(1, len(operators) + 1))  # Distance between the Z operators
result = job.result()[0]
values = result.data.evs  # Expectation value at each Z operator.
values = [
    v / values[0] for v in values
]  # Normalize the expectation values to evaluate how they decay with distance.

# plotting graph
plt.plot(data, values, marker="o", label="100-qubit GHZ state")
plt.xlabel("Distance between qubits $i$")
plt.ylabel(r"$\langle Z_i Z_0 \rangle / \langle Z_1 Z_0 \rangle $")
plt.legend()
plt.show()

Output:

Output of the previous code cell

Die vorherige Grafik zeigt, dass das Signal mit zunehmendem Abstand zwischen den Qubits aufgrund von Rauschen abklingt.


Nächste Schritte

Empfehlungen
War diese Seite hilfreich?
Melden Sie auf GitHub einen Fehler, einen Tippfehler oder fordern Sie Inhalte an.