qubit, gate e circuiti quantistici
Kifumi Numata (19 Apr 2024)
Clicca qui per scaricare il PDF della lezione originale. Nota che alcuni frammenti di codice potrebbero essere deprecati poiché si tratta di immagini statiche.
Il tempo QPU approssimativo per eseguire questo esperimento è di 5 secondi.
1. Introduzione
Bit, gate e circuiti sono i mattoni fondamentali del calcolo quantistico. Imparerai la computazione quantistica con il modello a circuiti usando bit quantistici e gate, e ripasserai anche la sovrapposizione, la misura e l'entanglement.
In questa lezione imparerai:
- gate a qubit singolo
- Sfera di Bloch
- Sovrapposizione
- Misura
- gate a due qubit e stato di entanglement
Alla fine di questa lezione imparerai la profondità del circuito, fondamentale per il calcolo quantistico su scala utility.
2. La computazione come diagramma
Quando si usano qubit o bit, è necessario manipolarli per trasformare gli input che abbiamo negli output di cui abbiamo bisogno. Per i programmi più semplici con pochissimi bit, è utile rappresentare questo processo in un diagramma noto come diagramma di circuito.
La figura in basso a sinistra è un esempio di circuito classico, quella in basso a destra è un esempio di circuito quantistico. In entrambi i casi, gli input si trovano a sinistra e gli output a destra, mentre le operazioni sono rappresentate da simboli. I simboli usati per le operazioni sono chiamati "gate", principalmente per ragioni storiche.
3. gate quantistico a qubit singolo
3.1 Stato quantistico e sfera di Bloch
Lo stato di un qubit è rappresentato come una sovrapposizione di e . Uno stato quantistico arbitrario è rappresentato come
dove e sono numeri complessi tali che .
e sono vettori nello spazio vettoriale complesso bidimensionale:
Pertanto, uno stato quantistico arbitrario è rappresentato anche come
Da questo si può vedere che lo stato di un bit quantistico è un vettore unitario in uno spazio con prodotto interno complesso bidimensionale con base ortonormale e . È normalizzato a 1.
|\psi\rangle =\begin\{pmatrix\} \alpha \\ \beta \end\{pmatrix\} è anche chiamato statevector.
Uno stato quantistico a qubit singolo è rappresentato anche come
dove e sono gli angoli della sfera di Bloch nella figura seguente.
Nelle prossime celle di codice costruiremo i calcoli di base a partire dai componenti fondamentali di Qiskit. Creeremo un circuito vuoto e aggiungeremo operazioni quantistiche, discutendo i gate e visualizzando i loro effetti man mano che procediamo.
Puoi eseguire la cella con "Shift" + "Invio". Importa prima le librerie.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-aer qiskit-ibm-runtime
# Import the qiskit library
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector
from qiskit_ibm_runtime import Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.visualization import plot_histogram
Preparare il circuito quantistico
Creeremo e disegneremo un circuito a qubit singolo.
# Create the single-qubit quantum circuit
qc = QuantumCircuit(1)
# Draw the circuit
qc.draw("mpl")
gate X
Il gate X è una rotazione di attorno all'asse della sfera di Bloch. Applicare il gate X a dà come risultato , e applicare il gate X a dà come risultato , quindi è un'operazione simile al gate NOT classico, ed è anche noto come bit flip. La rappresentazione matriciale del gate X è la seguente.
qc = QuantumCircuit(1) # Prepare the single-qubit quantum circuit
# Apply a X gate to qubit 0
qc.x(0)
# Draw the circuit
qc.draw("mpl")
In IBM Quantum®, lo stato iniziale è impostato a , quindi il circuito quantistico sopra in rappresentazione matriciale è
Ora eseguiamo questo circuito usando un simulatore di statevector.
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))
Il vettore verticale è visualizzato come vettore riga, con numeri complessi (la parte immaginaria è indicizzata da ).
gate H
Il gate di Hadamard è una rotazione di attorno a un asse a metà strada tra gli assi e della sfera di Bloch. Applicare il gate H a crea uno stato di sovrapposizione come . La rappresentazione matriciale del gate H è la seguente.
qc = QuantumCircuit(1) # Create the single-qubit quantum circuit
# Apply an Hadamard gate to qubit 0
qc.h(0)
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.70710678+0.j, 0.70710678+0.j],
dims=(2,))
Questo è
Questo stato di sovrapposizione è così comune e importante da avere un suo simbolo:
Applicando il gate a , abbiamo creato una sovrapposizione di e in cui una misura nella base computazionale (lungo z nella rappresentazione della sfera di Bloch) darebbe ciascuno stato con uguale probabilità.
Stato
Potresti aver intuito che esiste uno stato corrispondente:
Per creare questo stato, applica prima un gate X per ottenere , poi applica un gate H.
qc = QuantumCircuit(1) # Create the single-qubit quantum circuit
# Apply a X gate to qubit 0
qc.x(0)
# Apply an Hadamard gate to qubit 0
qc.h(0)
# draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([ 0.70710678+0.j, -0.70710678+0.j],
dims=(2,))
Questo è
Applicando il gate a si ottiene una sovrapposizione uguale di e , ma il segno di è negativo.
3.2 Stato quantistico a qubit singolo ed evoluzione unitaria
Le azioni di tutti i gate che abbiamo visto finora sono state unitarie, il che significa che possono essere rappresentate da un operatore unitario. In altre parole, lo stato di output si ottiene applicando allo stato iniziale una matrice unitaria:
Una matrice unitaria è una matrice che soddisfa
In termini di operazione del computer quantistico, diremmo che applicare un gate quantistico al qubit fa evolvere lo stato quantistico. I gate a qubit singolo più comuni includono i seguenti.
gate di Pauli:
dove il prodotto esterno è stato calcolato come segue:
Altri gate a qubit singolo tipici:
Il significato e l'uso di questi gate sono descritti in modo più dettagliato nel corso Fondamenti di Informazione Quantistica.
Esercizio 1
Usa Qiskit per creare circuit quantistici che preparino gli stati descritti di seguito. Poi esegui ciascun circuito usando il simulatore statevector e visualizza lo stato risultante sulla sfera di Bloch. Come bonus, prova ad anticipare quale dovrebbe essere lo stato finale basandoti sull'intuizione sui gate e le rotazioni nella sfera di Bloch.
(1)
(2)
(3)
Suggerimento: il gate Z può essere utilizzato con
qc.z(0)
Soluzione:
### (1) XX|0> ###
# Create the single-qubit quantum circuit
qc = QuantumCircuit(1) ##your code goes here##
# Add a X gate to qubit 0
qc.x(0) ##your code goes here##
# Add a X gate to qubit 0
qc.x(0) ##your code goes here##
# Draw a circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))
### (2) HH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.h(0)
qc.draw("mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([1.+0.j, 0.+0.j],
dims=(2,))
### (3) HZH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
qc.draw("mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)
Statevector([0.+0.j, 1.+0.j],
dims=(2,))
3.3 Misurazione
La misurazione è teoricamente un argomento molto complesso. Ma in termini pratici, effettuare una misurazione lungo (come fanno tutti i computer quantistici IBM®) semplicemente forza lo stato del qubit a diventare oppure , e noi osserviamo il risultato.
- è la probabilità di ottenere quando misuriamo.
- è la probabilità di ottenere quando misuriamo.
Quindi, e sono chiamate ampiezze di probabilità. (vedi "regola di Born")
Per esempio, ha la stessa probabilità di diventare o alla misurazione. ha il 75% di probabilità di diventare .
Simulatore Qiskit Aer
Ora misuriamo un circuito che prepara la sovrapposizione equiprobabile descritta sopra. Dobbiamo aggiungere i gate di misurazione, poiché il simulatore Qiskit Aer simula per default un hardware quantistico ideale (senza rumore). Nota: il simulatore Aer può anche applicare un modello di rumore basato su un vero computer quantistico. Torneremo sui modelli di rumore più avanti.
# Create a new circuit with one qubits (first argument) and one classical bits (second argument)
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0, 0) # Add the measurement gate
qc.draw(output="mpl")
Siamo ora pronti per eseguire il nostro circuito sul simulatore Aer. In questo esempio, useremo il valore predefinito shots=1024, il che significa che effettueremo 1024 misurazioni. Poi visualizzeremo quei conteggi in un istogramma.
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'0': 521, '1': 503}
Vediamo che gli 0 e gli 1 sono stati misurati con una probabilità di quasi il 50% ciascuno. Sebbene il rumore non sia stato simulato qui, gli stati rimangono comunque probabilistici. Quindi, pur aspettandoci una distribuzione circa 50-50, raramente la troveremo esattamente uguale. Proprio come 100 lanci di una moneta raramente daranno esattamente 50 volte per lato.
4. gate quantistici multi-qubit e entanglement
4.1 Circuito quantistico multi-qubit
Possiamo creare un circuito quantistico a due qubit con il seguente codice. Applicheremo un gate H a ciascun qubit.
# Create the two qubits quantum circuit
qc = QuantumCircuit(2)
# Apply an H gate to qubit 0
qc.h(0)
# Apply an H gate to qubit 1
qc.h(1)
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.5+0.j, 0.5+0.j, 0.5+0.j, 0.5+0.j],
dims=(2, 2))
Nota: ordinamento dei bit in Qiskit
Qiskit usa la notazione Little Endian per l'ordinamento di qubit e bit, il che significa che il qubit 0 è il bit più a destra nelle stringhe di bit. Esempio: significa che q0 è e q1 è . Fai attenzione perché alcune pubblicazioni nel campo del calcolo quantistico usano la notazione Big Endian (il qubit 0 è il bit più a sinistra), e altrettanto fa buona parte della letteratura di meccanica quantistica.
Un'altra cosa da notare è che nella rappresentazione di un circuito quantistico, è sempre posizionato in cima al circuito. Tenendo questo a mente, lo stato quantistico del circuito precedente può essere scritto come prodotto tensoriale di stati quantistici a singolo qubit.
( )
Lo stato iniziale di Qiskit è , quindi applicando a ciascun qubit si passa a uno stato di sovrapposizione uniforme.
La regola di misura è la stessa del caso a singolo qubit: la probabilità di misurare è .
# Draw a Bloch sphere
plot_bloch_multivector(out_vector)

Ora misuriamo questo circuito.
# Create a new circuit with two qubits (first argument) and two classical bits (second argument)
qc = QuantumCircuit(2, 2)
# Apply the gates
qc.h(0)
qc.h(1)
# Add the measurement gates
qc.measure(0, 0) # Measure qubit 0 and save the result in bit 0
qc.measure(1, 1) # Measure qubit 1 and save the result in bit 1
# Draw the circuit
qc.draw(output="mpl")
Ora useremo di nuovo un simulatore Aer per verificare sperimentalmente che le probabilità relative di tutti i possibili stati di output siano approssimativamente uguali.
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'10': 262, '01': 246, '00': 265, '11': 251}
Come previsto, gli stati , , , sono stati misurati ciascuno circa nel 25% dei casi.
4.2 gate quantistici multi-qubit
gate CNOT
Un gate CNOT ("NOT controllato" o CX) è un gate a due qubit, il cui funzionamento coinvolge due qubit contemporaneamente: il qubit di controllo e il qubit target. Un CNOT inverte il qubit target solo quando il qubit di controllo è .
| Input (target, controllo) | Output (target, controllo) |
|---|---|
| 00 | 00 |
| 01 | 11 |
| 10 | 10 |
| 11 | 01 |
Simuliamo prima l'azione di questo gate a due qubit quando q0 e q1 sono entrambi , e otteniamo il vettore di stato di output. La sintassi Qiskit usata è qc.cx(qubit di controllo, qubit target).
# Create a circuit with two quantum registers and two classical registers
qc = QuantumCircuit(2, 2)
# Apply the CNOT (cx) gate to a |00> state.
qc.cx(0, 1) # Here the control is set to q0 and the target is set to q1.
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
dims=(2, 2))
Come previsto, applicare un gate CNOT su non ha modificato lo stato, poiché il qubit di controllo si trovava nello stato . Torniamo all'operazione CNOT. Questa volta applicheremo un gate CNOT a e vedremo cosa succede.
qc = QuantumCircuit(2, 2)
# q0=1, q1=0
qc.x(0) # Apply a X gate to initialize q0 to 1
qc.cx(0, 1) # Set the control bit to q0 and the target bit to q1.
# Draw the circuit
qc.draw(output="mpl")
# See the statevector
out_vector = Statevector(qc)
print(out_vector)
Statevector([0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
dims=(2, 2))
Applicando un gate CNOT, lo stato è diventato .
Verifichiamo questi risultati eseguendo il circuito su un simulatore.
# Add measurements
qc.measure(0, 0)
qc.measure(1, 1)
# Draw the circuit
qc.draw(output="mpl")
# Run the circuit on a simulator to get the results
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(backend)
job = sampler.run([isa_qc])
result = job.result()
# Print the results
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
{'11': 1024}
I risultati mostrano che è stato misurato con probabilità del 100%.
4.3 Entanglement quantistico ed esecuzione su un dispositivo quantistico reale
Iniziamo introducendo uno specifico stato entangled particolarmente importante nel calcolo quantistico, per poi definire il termine "entangled":
e questo stato si chiama stato di Bell.
Uno stato entangled è uno stato composto da stati quantistici e che non può essere rappresentato come prodotto tensoriale di stati quantistici individuali.
Se ha due stati e :
il prodotto tensoriale di questi due stati è il seguente
ma non esistono coefficienti e in grado di soddisfare entrambe le equazioni. Pertanto, non è rappresentabile come prodotto tensoriale di stati quantistici individuali, e , e ciò significa che è uno stato entangled.
Creiamo lo stato di Bell ed eseguiamolo su un vero computer quantistico. Seguiremo i quattro passi per scrivere un programma quantistico, chiamati Qiskit patterns:
- Map problem to quantum circuits and operators
- Optimize for target hardware
- Execute on target hardware
- Post-process the results
Passo 1. Mappare il problema in circuiti e operatori quantistici
In un programma quantistico, i circuiti quantistici sono il formato nativo per rappresentare le istruzioni quantistiche. Quando si crea un circuito, di solito si crea un nuovo oggetto QuantumCircuit e poi si aggiungono le istruzioni in sequenza.
Il seguente blocco di codice crea un circuito che produce uno stato di Bell, lo specifico stato entangled a due qubit descritto sopra.
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure(0, 0)
qc.measure(1, 1)
qc.draw("mpl")
Passo 2. Ottimizzare per l'hardware target
Qiskit converte i circuiti astratti in circuiti QISA (Quantum Instruction Set Architecture) che rispettano i vincoli dell'hardware target e ottimizza le prestazioni del circuito. Prima dell'ottimizzazione occorre quindi specificare l'hardware target.
Se non hai qiskit-ibm-runtime, dovrai installarlo prima. Per maggiori informazioni su Qiskit Runtime, consulta la documentazione API.
# Install
# !pip install qiskit-ibm-runtime
Specificheremo l'hardware target.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
service.backends()
# You can specify the device
# backend = service.backend('ibm_kingston')
# You can also identify the least busy device
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
La traspirazione del circuito è un processo molto articolato. In breve, riscrive il circuito in uno logicamente equivalente usando i "gate nativi" (gate che un particolare computer quantistico è in grado di eseguire) e mappa i qubit del circuito ai qubit reali ottimali sul computer quantistico target. Per approfondire la traspirazione, consulta questa documentazione.
# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
target_circuit = pm.run(qc)
target_circuit.draw("mpl", idle_wires=False)
Puoi vedere che nella traspirazione il circuito è stato riscritto usando nuovi gate. Per maggiori informazioni, consulta la documentazione su ECRGate.
Passo 3. Eseguire il circuito target
Ora eseguiremo il circuito target sul dispositivo reale.
sampler = Sampler(backend)
job_real = sampler.run([target_circuit])
job_id = job_real.job_id()
print("job id:", job_id)
L'esecuzione sul dispositivo reale potrebbe richiedere un'attesa in coda, poiché i computer quantistici sono risorse preziose e molto richieste. Il job_id viene usato per verificare lo stato di esecuzione e i risultati del job in seguito.
# Check the job status (replace the job id below with your own)
job_real.status(job_id)
Puoi anche controllare lo stato del job dalla tua dashboard IBM Quantum:https://quantum.cloud.ibm.com/workloads
# If the Notebook session got disconnected you can also check your job status by running the following code
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
job_real = service.job(job_id) # Input your job-id between the quotations
job_real.status()
# Execute after job has successfully run
result_real = job_real.result()
print(result_real[0].data.c.get_counts())
Passo 4. Post-elaborare i risultati
Infine, dobbiamo post-elaborare i risultati per produrre output nel formato atteso, come valori o grafici.
plot_histogram(result_real[0].data.c.get_counts())
Come puoi vedere, e sono i risultati osservati più frequentemente. Ci sono alcuni risultati diversi da quelli attesi, dovuti al rumore e alla decoerenza dei qubit. Impareremo di più sugli errori e sul rumore nei computer quantistici nelle lezioni successive di questo corso.
4.4 Stato GHZ
Il concetto di entanglement può essere esteso a sistemi con più di due qubit. Lo stato GHZ (stato di Greenberger-Horne-Zeilinger) è uno stato massimamente entangled di tre o più qubit. Lo stato GHZ per tre qubit è definito come
Può essere creato con il seguente circuito quantistico.
qc = QuantumCircuit(3, 3)
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)
qc.measure(0, 0)
qc.measure(1, 1)
qc.measure(2, 2)
qc.draw("mpl")
La "profondità" di un circuito quantistico è una metrica utile e comune per descrivere i circuiti quantistici. Traccia un percorso attraverso il circuito quantistico, muovendosi da sinistra a destra, cambiando qubit solo quando sono collegati da un gate multi-qubit. Conta il numero di gate lungo quel percorso. Il numero massimo di gate per qualsiasi percorso all'interno del circuito è la profondità. Nei moderni computer quantistici rumorosi, i circuiti poco profondi hanno meno errori ed è più probabile che restituiscano buoni risultati. I circuiti molto profondi, invece, no.
Usando QuantumCircuit.depth(), possiamo verificare la profondità del nostro circuito quantistico. La profondità del circuito precedente è 4. Il qubit in cima ha solo tre gate inclusa la misura. Ma esiste un percorso dal qubit in cima verso il qubit 1 o il qubit 2 che include un ulteriore gate CNOT.
qc.depth()
4
Esercizio 2
Lo stato GHZ di un sistema a 8 qubit è
Scrivi il codice per preparare questo stato con il circuito più superficiale possibile. La profondità del circuito quantistico più superficiale è 5, inclusi i gate di misura.
Soluzione:
# Step 1
qc = QuantumCircuit(8, 8)
##your code goes here##
qc.h(0)
qc.cx(0, 4)
qc.cx(4, 6)
qc.cx(6, 7)
qc.cx(4, 5)
qc.cx(0, 2)
qc.cx(2, 3)
qc.cx(0, 1)
qc.barrier() # for visual separation
# measure
for i in range(8):
qc.measure(i, i)
qc.draw("mpl")
# print(qc.depth())
print(qc.depth())
5
from qiskit.visualization import plot_histogram
# Step 2
# For this exercise, the circuit and operators are simple, so no optimizations are needed.
# Step 3
# Run the circuit on a simulator to get the results
backend = AerSimulator()
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=1024)
result = job.result()
counts = result[0].data.c.get_counts()
print(counts)
# Step 4
# Plot the counts in a histogram
plot_histogram(counts)
{'11111111': 535, '00000000': 489}
5. Riepilogo
Hai imparato il calcolo quantistico con il modello a circuiti usando qubit e gate quantistici, e hai ripassato la sovrapposizione, la misura e l'entanglement. Hai anche imparato il metodo per eseguire il circuito quantistico su un dispositivo quantistico reale.
Nell'esercizio finale per creare un circuito GHZ, hai cercato di ridurre la profondità del circuito, che è un fattore importante per ottenere una soluzione a scala di utilità su un computer quantistico rumoroso. Nelle lezioni successive di questo corso imparerai il rumore e i metodi di mitigazione degli errori in dettaglio. In questa lezione, come introduzione, abbiamo considerato la riduzione della profondità del circuito su un dispositivo ideale, ma nella realtà dobbiamo tenere conto dei vincoli del dispositivo reale, come la connettività dei qubit. Nelle lezioni successive di questo corso imparerai di più su questo aspetto.
# See the version of Qiskit
import qiskit
qiskit.__version__
'2.0.2'