Skip to main content
IBM Quantum Platform

Esegui il tuo primo circuito sull'hardware

  • Il codice presente in questa pagina è stato sviluppato utilizzando i seguenti requisiti. Si consiglia di utilizzare queste versioni o versioni più recenti.

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

Questo esempio è composto da due parti. Per prima cosa creerai un semplice programma quantistico "Hello world" e lo eseguirai su un'unità di elaborazione quantistica (QPU). Poiché la ricerca quantistica effettiva richiede programmi molto più robusti, nella seconda sezione ( Scalabilità a un numero elevato di qubit ) si procederà a scalare il programma semplice fino al livello di utilità.


Installa e autentica

  1. Se non hai ancora installato Qiskit, consulta le istruzioni nella guida Quickstart.

    • Installa il client Compute di IBM Quantum per eseguire i lavori sull'hardware quantistico:

      pip install qiskit-ibm-runtime
    • Configurare un ambiente per eseguire i notebook Jupyter in locale:

      pip install jupyter
  2. Configura la tua autenticazione per accedere all'hardware quantistico tramite il piano Open gratuito.

    (Se hai ricevuto un invito via e-mail per partecipare a un account, segui invece la procedura per gli utenti invitati.)

    • Vai su IBM Quantum Platform per accedere o creare un account.

      Importante

      Se ti connetti tramite un server proxy, devi utilizzare v0.44.0qiskit-ibm-runtime o una versione successiva.

    • Genera la tua chiave API (chiamata anche token API) sulla dashboard, quindi copiala in un luogo sicuro.

    • Vai alla pagina Istanze e trova l'istanza che desideri utilizzare. Passa con il mouse sul suo CRN e clicca per copiarlo.

    • Salva le tue credenziali localmente con questo codice:

      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. Ora puoi utilizzare questo codice Python ogni volta che desideri effettuare l'autenticazione al servizio IBM Quantum Compute:

    from qiskit_ibm_runtime import QiskitRuntimeService
    
    # Run every time you need the service
    service = QiskitRuntimeService()
Non utilizzi un ambiente Python affidabile?

Se stai utilizzando un computer pubblico o un altro ambiente non protetto, segui invece le istruzioni per l'autenticazione manuale per mantenere al sicuro le tue credenziali di autenticazione.


Creare ed eseguire un semplice programma quantistico

I quattro passaggi per scrivere un programma quantistico utilizzando i modelli Qiskit sono:

  1. Mappare il problema in un formato nativo quantistico.

  2. Ottimizzare i circuiti e gli operatori.

  3. Eseguire utilizzando una funzione primitiva quantistica.

  4. Analizza i risultati.

Passo 1. Mappare il problema in un formato nativo quantistico

In un programma quantistico, i circuiti quantistici sono il formato nativo in cui rappresentare le istruzioni quantistiche, mentre gli operatori rappresentano gli osservabili da misurare. Quando si crea un circuito, di solito si crea un nuovo QuantumCircuit oggetto, quindi si aggiungono le istruzioni in sequenza.

La seguente cella di codice crea un circuito che produce uno stato di Bell, ovvero uno stato in cui due qubit sono completamente intrecciati tra loro.

Nota: ordine dei bit

Il Qiskit SDK utilizza la numerazione LSb 0 bit in cui la nthn^{th} cifra ha valore 1n1 \ll n o 2n2^n. Per ulteriori dettagli, consultare l' argomento Ordinamento dei bit in Qiskit SDK.

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 qiskit_ibm_runtime import EstimatorOptions
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from matplotlib import pyplot as plt
# 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

Vedi QuantumCircuit nella documentazione tutte le operazioni disponibili.

Quando si creano circuiti quantistici, è necessario considerare anche quale tipo di dati si desidera ottenere al termine dell'esecuzione. Qiskit offre due modi per ottenere i dati: è possibile ottenere un output campionario per un insieme di qubit che si sceglie di misurare, oppure è possibile ottenere il valore atteso di un osservabile. Prepara il tuo carico di lavoro per misurare il circuito in uno di questi due modi utilizzando le primitive (spiegate in dettaglio nel Passo 3 ).

Questo esempio misura i valori attesi utilizzando il qiskit.quantum_info sottomodulo, specificato tramite operatori (oggetti matematici utilizzati per rappresentare un'azione o un processo che modifica uno stato quantistico). La seguente cella di codice crea sei operatori di Pauli a due qubit: IZ, IX, ZI, XI ZZ, e XX.

# Set up six different observables.

observables_labels = ["IZ", "IX", "ZI", "XI", "ZZ", "XX"]
observables = [SparsePauliOp(label) for label in observables_labels]
Notazione dell'operatore

Qui, qualcosa come ZZ l'operatore è una scorciatoia per il prodotto ZZZ\otimes Z tensoriale, che significa misurare Z sul qubit 1 e Z sul qubit 0 insieme, e ottenere informazioni sulla correlazione tra il qubit 1 e il qubit 0. Valori attesi come questo sono solitamente scritti anche come Z1Z0\langle Z_1 Z_0 \rangle.

Se lo stato è intrecciato, allora la misurazione di Z1Z0\langle Z_1 Z_0 \rangle dovrebbe essere diversa dalla misurazione di I1Z0Z1I0\langle I_1 \otimes Z_0 \rangle \langle Z_1 \otimes I_0 \rangle. Per lo stato intrecciato specifico creato dal nostro circuito descritto sopra, la misurazione di Z1Z0\langle Z_1 Z_0 \rangle dovrebbe essere 1 e la misurazione di I1Z0Z1I0\langle I_1 \otimes Z_0 \rangle \langle Z_1 \otimes I_0 \rangle dovrebbe essere zero.

Passo 2. Ottimizzare i circuiti e gli operatori

Quando si eseguono circuiti su un dispositivo, è importante ottimizzare l'insieme di istruzioni contenute nel circuito e ridurre al minimo la profondità complessiva (approssimativamente il numero di istruzioni) del circuito. Ciò garantisce i migliori risultati possibili riducendo gli effetti di errori e rumori. Inoltre, le istruzioni del circuito devono essere conformi all'architettura del set di istruzioni (ISA) del dispositivo backend e devono tenere conto dei gate di base e della connettività dei qubit del dispositivo.

Il codice seguente istanzia un dispositivo reale a cui inviare un lavoro e trasforma il circuito e gli osservabili in modo che corrispondano all'ISA di quel backend. È necessario che tu abbia già salvato le tue credenziali.

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

Passaggio 3. Eseguire utilizzando le primitive quantistiche

I computer quantistici possono produrre risultati casuali, quindi solitamente si raccoglie un campione dei risultati eseguendo il circuito più volte. È possibile stimare il valore dell'osservabile utilizzando la Estimator classe. Estimator è una delle due primitive; l'altra è Sampler, che può essere utilizzata per ottenere dati da un computer quantistico. Questi oggetti dispongono di un run() metodo che esegue la selezione di circuiti, grandezze osservabili e parametri (se applicabile), utilizzando un blocco unificato primitivo ( PUB ).

# 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: d9mqa588csec73fagtu0

Dopo aver inviato un lavoro, puoi attendere che venga completato nell'istanza Python corrente oppure utilizzare job_id per recuperare i dati in un secondo momento. (Per ulteriori dettagli, consultare la sezione relativa al recupero dei lavori )

Al termine dell'operazione, esaminare il risultato tramite l'attributo result() dell'operazione.

# 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]
Alternativa: eseguire l'esempio utilizzando un simulatore

Quando si esegue un programma quantistico su un dispositivo reale, il carico di lavoro deve attendere in coda prima di essere eseguito. Per risparmiare tempo, puoi invece utilizzare il codice seguente per eseguire questo piccolo carico di lavoro in modalità di fake_provider test locale qiskit-ibm-runtime . Si noti che ciò è possibile solo per un circuito di piccole dimensioni. Quando passerai alla sezione successiva, dovrai utilizzare un dispositivo reale.


# 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]

Passaggio 4. Analizza i risultati

La fase di analisi è in genere quella in cui è possibile elaborare i risultati utilizzando, ad esempio, la mitigazione dell'errore di misurazione o l'estrapolazione del rumore zero (ZNE). È possibile inserire questi risultati in un altro flusso di lavoro per ulteriori analisi o preparare un grafico dei valori e dei dati chiave. In generale, questo passaggio è specifico per il tuo problema. Per questo esempio, traccia ciascuno dei valori attesi misurati per il nostro circuito.

I valori attesi e le deviazioni standard relativi alle grandezze osservabili specificate in Estimator sono accessibili tramite gli attributi e PubResult.data.stds del PubResult.data.evs risultato del processo. Per ottenere i risultati da Sampler, utilizzare la PubResult.data.meas.get_counts() funzione, che restituirà un dict di misurazioni sotto forma di stringhe di bit come chiavi e conteggi come valori corrispondenti. Per ulteriori informazioni, consulta la guida introduttiva a 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

Si noti che per i qubit 0 e 1, i valori attesi indipendenti sia di X che di Z sono 0, mentre le correlazioni (XX e ZZ) sono 1. Questo è un tratto distintivo dell'entanglement quantistico.


Scalabilità a un numero elevato di qubit

Nel campo dell'informatica quantistica, il lavoro su scala industriale è fondamentale per compiere progressi in questo settore. Questo tipo di lavoro richiede calcoli su scala molto più ampia, con circuiti che possono utilizzare oltre 100 qubit e oltre 1000 gate. Questo esempio mostra come è possibile eseguire operazioni su scala industriale su IBM® QPU creando e analizzando uno stato GHZ a 100 qubit. Utilizza il flusso di lavoro dei modelli Qiskit e termina misurando il valore atteso Z0Zi\langle Z_0 Z_i \rangle per ciascun qubit.

Passo 1. Mappare il problema

Scrivi una funzione che restituisca un QuantumCircuit che prepara uno nn stato GHZ a -qubit (essenzialmente uno stato di Bell esteso), quindi utilizza tale funzione per preparare uno stato GHZ a 100-qubit e raccogliere gli osservabili da misurare.

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)

Successivamente, mappare gli operatori di interesse. Questo esempio utilizza gli ZZ operatori tra i qubit per esaminare il comportamento man mano che si allontanano. Valori di aspettativa sempre più imprecisi (corrotti) tra qubit distanti rivelerebbero il livello di rumore presente.

# 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]

Passo 2. Ottimizzare il problema per l'esecuzione su hardware quantistico

Il codice seguente trasforma il circuito e gli osservabili per adattarli all'ISA del backend. È necessario che tu abbia già salvato le tue credenziali.

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]

Passaggio 3. Eseguire su hardware

Invia il lavoro e attiva la soppressione degli errori utilizzando una tecnica volta a ridurre gli errori denominata “disaccoppiamento dinamico ”. Il livello di resilienza indica il grado di resilienza da implementare per far fronte agli errori. Livelli più elevati producono risultati più accurati, a scapito di tempi di elaborazione più lunghi. Per ulteriori spiegazioni sulle opzioni impostate nel codice riportato di seguito, consultare la sezione “Tecniche di mitigazione e soppressione degli errori ”.

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:

d9mqnjmh4e6s738vioqg

Passaggio 4. Risultati della post-elaborazione

Una volta completato il lavoro, traccia i risultati e nota che Z0Zi\langle Z_0 Z_i \rangle diminuisce all'aumentare di ii, anche se in una simulazione ideale tutti i Z0Zi\langle Z_0 Z_i \rangle dovrebbero essere pari a 1.

# 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

Il grafico precedente mostra che all'aumentare della distanza tra i qubit, il segnale decade a causa della presenza di rumore.


Passi successivi

Suggerimenti
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.