Vai al contenuto principale

Il tuo primo esperimento quantistico

Introduzione​

Nel video seguente, Olivia Lanes ti guida attraverso i contenuti di questa lezione. In alternativa, puoi aprire il video YouTube di questa lezione in una finestra separata.

A questo punto, hai già eseguito il tuo primo Circuit quantistico e appreso le basi del quantum computing: come vengono rappresentati gli stati quantistici, come i Gate agiscono su quegli stati, e come le caratteristiche quantistiche come la sovrapposizione e l'entanglement entrano in gioco. È ora di mettere tutto in pratica e risolvere il tuo primo problema su un computer quantistico.

Esploreremo il panorama più ampio dei problemi adatti al quantum computing in una lezione successiva. Per ora, ci concentreremo su un problema nel dominio della simulazione della natura: usare un computer quantistico come sostituto più pulito e controllabile di un sistema quantistico naturale. In effetti, questa è stata la prima applicazione che Richard Feynman immaginò per i computer quantistici negli anni '80. Come disse in modo memorabile: "La natura non è classica, cazzo, e se vuoi fare una simulazione della natura, è meglio che tu la faccia quantomeccanica..."

In questa lezione, seguiremo quel principio per simulare l'interazione tra due spin, che puoi pensare come piccoli magneti. A seconda del segno della loro interazione, potrebbero preferire allinearsi e puntare nella stessa direzione, o anti-allinearsi e puntare in direzioni opposte. Ci concentreremo su quest'ultimo caso perché spesso porta a un comportamento più interessante — e più impegnativo. Una volta compreso questo semplice sistema a due qubit, mostreremo come le stesse idee si scalano, permettendo ai computer quantistici di sfruttare la loro crescita esponenziale nella simulazione di grandi sistemi di spin.

Due magneti in interazione​

Per questo problema, useremo due qubit, uno per ogni spin nel nostro modello. Ogni spin può puntare verso l'alto (stato del Qubit ∣0⟩|0\rangle), verso il basso (stato del Qubit ∣1⟩|1\rangle), o essere in una sovrapposizione dei due stati.

Se gli spin hanno un'interazione antiferromagnetica, significa che vogliono anti-allinearsi: quando uno è in su, l'altro vuole essere in giù, e viceversa.

Supponiamo ora che ci sia anche un campo magnetico che punta da sinistra a destra nel nostro sistema. Poiché questo campo punta nella direzione trasversale rispetto alla direzione su-giù degli spin, è chiamato campo trasversale. Questo campo può capovolgere gli spin, il che fa sì che la configurazione a energia minima sia una specifica sovrapposizione di disposizioni di spin su e giù, piuttosto che un singolo pattern di spin definito.

Possiamo descrivere tutti questi effetti usando un oggetto matematico chiamato Hamiltoniano. L'Hamiltoniano ci dice l'energia del sistema per una data disposizione di spin:

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

dove JJ è un coefficiente che controlla la forza dell'interazione tra gli spin e hxh_x è un coefficiente per la forza del campo magnetico esterno. Z1Z0Z_1 Z_0 premia o penalizza gli spin a seconda che siano allineati o anti-allineati, e X0X_0 e X1X_1 rappresentano l'effetto di capovolgimento degli spin del campo magnetico.

In fisica, i sistemi tendono a stabilizzarsi nello stato con la minima energia possibile, chiamato stato fondamentale. Trovare questo stato a minima energia è un problema comune, ma richiede tecniche di ottimizzazione che esulano dall'ambito di questa lezione.

Invece, porremo una domanda più semplice: se prepariamo gli spin in un particolare stato, qual è l'energia di quello stato?

Per rispondere, seguiremo questi passi:

  1. Preparare gli spin in uno stato di nostra scelta
  2. Misurare l'energia di quello stato usando l'Hamiltoniano sopra

Questo è esattamente il tipo di calcolo che appare all'interno di algoritmi quantistici più grandi, come gli algoritmi variazionali, che potrai esplorare nei corsi successivi.

Implementazione in Qiskit​

Prima di iniziare a scrivere codice, abbiamo bisogno di un po' di contesto. Quando eseguiamo un Circuit quantistico, finiamo sempre misurando i qubit. Ma ci sono due tipi diversi di domande che potremmo voler porre sul risultato di quella misurazione: a volte vogliamo semplicemente sapere qual è lo stato del qubit. Altre volte vogliamo sapere, dato lo stato quantistico, qual è il valore di una grandezza fisica, come l'energia?

In Qiskit, questi due tipi di domande sono gestiti da due strumenti diversi, chiamati primitive.

Sampler risponde al primo tipo di domanda. Esegue il Circuit molte volte e ci dice con quale frequenza misuriamo ogni possibile risultato, come 00, 01, 10 o 11. Il risultato è un istogramma che mostra la probabilità di ogni esito di misurazione.

Estimator risponde al secondo tipo di domanda. Invece di fornirci un istogramma, combina molte misurazioni dietro le quinte per calcolare un singolo numero, come l'energia dello stato secondo un Hamiltoniano che forniamo.

Per aiutarti a capire quando e perché useremmo ciascuno di questi strumenti, percorreremo due flussi di lavoro completi (chiamati "pattern Qiskit") applicati allo stesso sistema a due qubit.

Flusso di lavoro dei pattern Qiskit​

Il flusso di lavoro dei pattern Qiskit è un framework generale che usiamo per risolvere problemi quantistici con Qiskit. Suddivide un'attività di quantum computing in quattro passi:

  1. Mappare il problema su un modello che può essere rappresentato da Circuit quantistici
  2. Ottimizzare il Circuit per essere eseguito su un Backend specifico
  3. Eseguire il Circuit ottimizzato sul Backend selezionato
  4. Post-elaborare i dati di misurazione grezzi

Esperimento 1: usa Sampler per misurare lo stato​

Mappa​

In generale, il passo di mappatura è dove capiremo come rappresentare un problema del mondo reale in termini di qubit, operatori e misurazioni. In molte applicazioni, questa è la parte più delicata e articolata del flusso di lavoro — anche domande semplici, come "cosa rappresenta ogni qubit?" non hanno sempre risposte dirette.

In questo esperimento, tuttavia, la mappatura è deliberatamente semplice. Ogni grado di libertà fisico si mappa direttamente su un singolo qubit. Grazie a questa corrispondenza uno-a-uno, il passo di mappatura si riduce alla scelta dello stato quantistico che vogliamo preparare e alla scrittura di un Circuit che prepara e misura quello stato.

Qui prepareremo uno stato di Bell entangled, simile a quello che abbiamo creato nella prima lezione di questo corso:

∣Ψ⟩=12(∣10⟩−∣01⟩)\vert\Psi\rangle = \frac{1}{\sqrt{2}}(\vert 10\rangle - \vert 01\rangle)
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# Import Qiskit primitives
from qiskit import QuantumCircuit

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)

# Measure state
qc.measure_all()

# Draw circuit
qc.draw("mpl")

Output of the previous code cell

Ottimizza​

Prima di eseguire il nostro Circuit su un computer quantistico (o su un simulatore se hai esaurito il tuo tempo gratuito sui veri computer quantistici per il mese), dobbiamo prepararlo per l'esecuzione. Questo passo si chiama ottimizzazione. (Nota: questo uso del termine "ottimizzazione" può essere fonte di confusione. Nel quantum computing, i problemi di ottimizzazione si riferiscono a una classe specifica di problemi. Qui, usiamo ottimizzazione per descrivere un passo di preparazione obbligatorio che ogni Circuit quantistico deve attraversare prima di poter essere eseguito in modo efficiente sull'hardware.)

Durante l'ottimizzazione:

  1. Scegliamo il Backend — un vero computer quantistico o un simulatore.
  2. Assegniamo i qubit del nostro Circuit ai qubit fisici sul dispositivo.
  3. Riscriviamo il Circuit usando solo i Gate che il computer quantistico può effettivamente eseguire.
  4. Implementiamo opzionalmente tecniche di mitigazione e soppressione degli errori per ridurre gli effetti del rumore.

In Qiskit, questo viene gestito automaticamente dal Transpiler. Una volta scelto il Backend, il Transpiler svolge tutto il lavoro per rendere il tuo Circuit pronto per l'esecuzione, senza che tu debba regolare manualmente i Gate o le assegnazioni dei qubit. Il Transpiler offre anche diversi livelli di ottimizzazione, che possono aiutare a ridurre gli errori se necessario. L'ottimizzazione viene eseguita in fasi chiamate 'pass'. Quindi questa ottimizzazione sarà gestita dal pass_manager nel codice seguente. Per saperne di più sugli errori e sulla mitigazione degli errori, consulta il corso di Olivia Lanes Quantum Computing in Practice.

# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService

## Load the Qiskit Runtime service
# QiskitRuntimeService.save_account(
# channel="ibm_quantum_platform",
# token="YOUR_TOKEN_HERE",
# overwrite=True,
# set_as_default=True,
# )
# service = QiskitRuntimeService(channel="ibm_quantum_platform")

# Or load saved credentials
service = QiskitRuntimeService()

# Use the least busy backend, or uncomment the loading of a specific backend like "ibm_brisbane".
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
# backend = service.backend("ibm_brisbane")
print(backend.name)
ibm_fez
# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)

qc_isa.draw("mpl")

Output of the previous code cell

Esegui​

Ora siamo pronti per l'esecuzione! Caricheremo Sampler, poi invieremo il job al Backend.

# Load the Runtime primitive and session
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(mode=backend)

Oppure, se stai usando un simulatore, puoi decommentare ed eseguire questa cella:

## Load the backend sampler
# from qiskit.primitives import BackendSamplerV2

## Load the Aer simulator and generate a noise model based on the currently-selected backend.
# from qiskit_aer import AerSimulator
# from qiskit_aer.noise import NoiseModel

# noise_model = NoiseModel.from_backend(backend)

## Define a simulator using Aer, and use it in Sampler.
# backend_sim = AerSimulator(noise_model=noise_model)
# sampler_sim = BackendSamplerV2(backend=backend_sim)

## Alternatively, load a fake backend with generic properties and define a simulator.
## backend_gen = GenericBackendV2(num_qubits=18)
## sampler_gen = BackendSamplerV2(backend=backend_gen)
job = sampler.run([qc_isa], shots=100)
# job = sampler_sim.run([qc_isa]) # uncomment if you want to run on a simulator
res = job.result()
counts = res[0].data.meas.get_counts()

Post-elaborazione​

from qiskit.visualization import plot_histogram

print("counts = ", counts)
plot_histogram(counts)
counts =  {'10': 49, '01': 50, '11': 1}

Output of the previous code cell

Vediamo che la maggior parte dei conteggi si trova in 01 o 10, il che significa che quando un qubit veniva misurato come 0, l'altro era 1, e viceversa. Questo è coerente con lo stato di Bell ∣Ψ−⟩\vert \Psi^- \rangle che abbiamo preparato.

Esperimento 2: usa Estimator per misurare l'energia​

Ora che abbiamo visto come campionare uno stato quantistico, usiamo Estimator per calcolare l'energia del nostro stato di Bell ∣Ψ−⟩=12(∣01⟩−∣10⟩)\vert \Psi^- \rangle = \frac{1}{\sqrt{2}}(\vert 01 \rangle - \vert 10 \rangle).

Mappa​

Come promemoria, l'energia del sistema è determinata dall'interazione tra gli spin (JJ) e il campo magnetico esterno (hxh_x), come catturato dall'Hamiltoniano:

H=JZ1Z0+hx(X1+X0)H = J Z_1 Z_0 + h_x (X_1 + X_0)

Ogni termine nell'Hamiltoniano ci dice come una certa combinazione di spin contribuisce all'energia. In Qiskit, possiamo rappresentare questi termini come operatori di Pauli, che sono semplicemente etichette per semplici azioni sui qubit:

  • Z1Z0Z_1 Z_0 agisce con un ZZ su entrambi i qubit.
  • X0X_0 agisce con XX sul qubit 0.
  • X1X_1 agisce con XX sul qubit 1.

Un SparsePauliOp in Qiskit è un modo per memorizzare un elenco di questi operatori di Pauli insieme ai loro coefficienti numerici. Questi operatori di Pauli sono gli osservabili che vogliamo che il computer quantistico misuri — le grandezze che ci dicono qualcosa sul sistema. Usando Estimator, possiamo calcolare il valore medio di ciascun osservabile sul nostro stato e combinarli in base ai coefficienti nell'Hamiltoniano per ottenere l'energia totale.

# Import Qiskit primitives
from qiskit.quantum_info import SparsePauliOp

# Parameters
J = 1.0 # antiferromagnetic coupling (J<0)
hx = -0.5 # transverse field strength

# 1. Define the Hamiltonian H = J Z1 Z2 + hx (X1 + X2)
obs = SparsePauliOp.from_list([("ZZ", J), ("XI", hx), ("IX", hx)])

# Make state
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.x(1)
qc.z(0)
<qiskit.circuit.instructionset.InstructionSet at 0x1387ed630>

Nota che abbiamo omesso la riga qc.measure_all() nel nostro codice. Questo perché con Estimator non è necessario specificare dove misurare nel Circuit. Gli diremo semplicemente quali osservabili vogliamo stimare e Qiskit si occupa delle misurazioni dietro le quinte.

Ottimizza​

Il passo di ottimizzazione procede come prima, con l'aggiunta di assicurarci che anche i nostri osservabili siano scritti in un modo comprensibile al computer quantistico.

# Transpile the circuit and optimize for running on the quantum computer selected
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)

qc_isa.draw("mpl")

Output of the previous code cell

Esegui​

Nel passo di esecuzione, caricheremo Estimator, poi invieremo il Circuit insieme all'elenco degli osservabili che vogliamo stimare al computer quantistico.

# Load the Runtime primitive and session
from qiskit_ibm_runtime import EstimatorV2 as Estimator

estimator = Estimator(mode=backend)
# Load the backend sampler

# noise_model = NoiseModel.from_backend(backend)

# Use Aer simulator in Estimator
# estimator_sim = BackendEstimatorV2(backend=backend_sim)

# Alternatively, load a fake backend with generic properties and define a simulator.
# backend_gen = GenericBackendV2(num_qubits=18)
# estimator_gen = BackendEstimatorV2(backend=backend_gen)
pubs = [(qc_isa, obs_isa)]
job = estimator.run([[qc_isa, obs_isa]])
res = job.result()

# Uncomment lines below to run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([[qc_isa,obs_isa]])
# res=job.result()

Post-elaborazione​

Infine, nel passo di post-elaborazione, stampiamo semplicemente l'energia calcolata dietro le quinte da Estimator.

print(res[0].data.evs)
-0.9934112021453058

Questa è l'energia del nostro stato!

Conclusione​

In questa lezione, abbiamo imparato come preparare un semplice stato quantistico a due qubit che rappresenta due spin in interazione. Abbiamo visto come usare Sampler per osservare la distribuzione degli esiti di misurazione e come usare Estimator per calcolare l'energia dello stato secondo l'Hamiltoniano. Lungo il percorso, abbiamo visto come l'Hamiltoniano codifica le interazioni tra gli spin e gli effetti di un campo esterno, e come diversi stati possono avere energie diverse.

Estensione a molti spin​

Finora abbiamo esaminato solo due spin, un numero sufficientemente piccolo da analizzare a mano. Nei sistemi fisici reali, come in un magnete o in un altro materiale complesso, ci sono spesso molti spin in interazione. Quando il numero di spin aumenta, l'Hamiltoniano diventa più complesso e trovare lo stato a minima energia diventa molto più difficile. È qui che i computer quantistici possono aiutare: preparando stati diversi e stimando le loro energie, possiamo esplorare configurazioni a bassa energia in modo più efficiente dei computer classici per sistemi di grandi dimensioni.

Un'estensione naturale di questo esperimento sarebbe aumentare il numero di qubit per rappresentare più spin e adeguare il modo in cui gli spin vengono preparati per cercare di trovare lo stato a minima energia. Questo approccio è l'essenza dei metodi variazionali, sui quali puoi imparare nel corso Variational Quantum Algorithms.

Esistono anche altri approcci quantistici allo studio delle energie dello stato fondamentale che vanno oltre le tecniche variazionali. Questi metodi non vengono trattati qui, ma vengono introdotti nel corso Quantum Diagonalization Algorithms se sei interessato ad approfondire.

Obiettivo di apprendimento​

Torna all'inizio dell'Esperimento 2 e riprova con uno stato di sovrapposizione diverso. Riesci a trovare uno stato con un'energia ancora più bassa di quella che abbiamo usato?

This translation based on the English version of 7 mag 2026