Vai al contenuto principale

qubit, gate e circuiti quantistici

nota

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.

"circuito logico classico e circuito quantistico"

3. gate quantistico a qubit singolo

3.1 Stato quantistico e sfera di Bloch

Lo stato di un qubit è rappresentato come una sovrapposizione di 0|0\rangle e 1|1\rangle. Uno stato quantistico arbitrario è rappresentato come

ψ=α0+β1|\psi\rangle =\alpha|0\rangle+ \beta|1\rangle

dove α\alpha e β\beta sono numeri complessi tali che α2+β2=1|\alpha|^2+|\beta|^2=1.

0|0\rangle e 1|1\rangle sono vettori nello spazio vettoriale complesso bidimensionale:

0=(10),1=(01)|0\rangle = \begin{pmatrix} 1 \\0 \end{pmatrix}, |1\rangle = \begin{pmatrix} 0\\1 \end{pmatrix}

Pertanto, uno stato quantistico arbitrario è rappresentato anche come

ψ=α(10)+β(01)=(αβ)|\psi\rangle = \alpha\begin{pmatrix} 1 \\ 0 \end{pmatrix} + \beta\begin{pmatrix}0\\ 1 \end{pmatrix} = \begin{pmatrix} \alpha \\ \beta \end{pmatrix}

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 0|0\rangle e 1|1\rangle. È normalizzato a 1.

ψψ=(αβ)(αβ)=1\langle\psi|\psi\rangle = \begin{pmatrix} \alpha^* & \beta^* \end{pmatrix} \begin{pmatrix} \alpha \\ \beta \end{pmatrix} = 1

|\psi\rangle =\begin\{pmatrix\} \alpha \\ \beta \end\{pmatrix\} è anche chiamato statevector.

Uno stato quantistico a qubit singolo è rappresentato anche come

ψ=cosθ20+eiφsinθ21=((cosθ2eiφsinθ2))|\psi\rangle =\cos\frac{\theta}{2}|0\rangle+e^{i\varphi}\sin\frac{\theta}{2}|1\rangle =\left( \begin{pmatrix} \cos\frac{\theta}{2}\\ e^{i\varphi}\sin\frac{\theta}{2} \end{pmatrix}\right)

dove θ\theta e φ\varphi sono gli angoli della sfera di Bloch nella figura seguente.

Sfera di Bloch 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")

Output della cella di codice precedente

gate X

Il gate X è una rotazione di π\pi attorno all'asse xx della sfera di Bloch. Applicare il gate X a 0|0\rangle dà come risultato 1|1\rangle, e applicare il gate X a 1|1\rangle dà come risultato 0|0\rangle, quindi è un'operazione simile al gate NOT classico, ed è anche noto come bit flip. La rappresentazione matriciale del gate X è la seguente.

X=(0110)X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix}
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")

Output della cella di codice precedente

In IBM Quantum®, lo stato iniziale è impostato a 0|0\rangle, quindi il circuito quantistico sopra in rappresentazione matriciale è

X0=(0110)(10)=(01)=1X|0\rangle= \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} \begin{pmatrix} 1 \\ 0 \end{pmatrix} =\begin{pmatrix} 0 \\ 1 \end{pmatrix} = |1\rangle

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,))

Output della cella di codice precedente

Il vettore verticale è visualizzato come vettore riga, con numeri complessi (la parte immaginaria è indicizzata da jj).

gate H

Il gate di Hadamard è una rotazione di π\pi attorno a un asse a metà strada tra gli assi xx e zz della sfera di Bloch. Applicare il gate H a 0|0\rangle crea uno stato di sovrapposizione come 0+12\frac{|0\rangle + |1\rangle}{\sqrt{2}}. La rappresentazione matriciale del gate H è la seguente.

H=12(1111)H = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \\ \end{pmatrix}
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")

Output della cella di codice precedente

# 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,))

Output della cella di codice precedente

Questo è

H0=12(1111)(10)=12(11)=(0.7070.707)=12(0+1)H|0\rangle= \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} \begin{pmatrix} 1 \\0 \end{pmatrix} =\frac{1}{\sqrt{2}}\begin{pmatrix} 1 \\ 1 \end{pmatrix} =\begin{pmatrix} 0.707 \\ 0.707 \end{pmatrix} =\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle)

Questo stato di sovrapposizione è così comune e importante da avere un suo simbolo:

+12(0+1).|+\rangle \equiv \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle).

Applicando il gate HH a 0|0\rangle, abbiamo creato una sovrapposizione di 0|0\rangle e 1|1\rangle in cui una misura nella base computazionale (lungo z nella rappresentazione della sfera di Bloch) darebbe ciascuno stato con uguale probabilità.

Stato |-\rangle

Potresti aver intuito che esiste uno stato |-\rangle corrispondente:

012.|-\rangle \equiv \frac{|0\rangle -|1\rangle}{\sqrt{2}}.

Per creare questo stato, applica prima un gate X per ottenere 1|1\rangle, 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")

Output della cella di codice precedente

# 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,))

Output della cella di codice precedente

Questo è

H1=12(11 11)(0 1)=12(1 1)=(0.707 0.707)=12(01)=H|1\rangle= \frac{1}{\sqrt{2}} \begin{pmatrix} 1 & 1 \\\ 1 & -1 \end{pmatrix} \begin{pmatrix} 0 \\\ 1 \end{pmatrix} =\frac{1}{\sqrt{2}}\begin{pmatrix} 1 \\\ -1 \end{pmatrix} =\begin{pmatrix} 0.707 \\\ -0.707 \end{pmatrix} =\frac{1}{\sqrt{2}}(|0\rangle-|1\rangle) = |-\rangle

Applicando il gate HH a 1|1\rangle si ottiene una sovrapposizione uguale di 0|0\rangle e 1|1\rangle, ma il segno di 1|1\rangle è 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:

ψ=Uψ|\psi^{'}\rangle = U|\psi\rangle

Una matrice unitaria è una matrice che soddisfa

UU=UU=I.U^{\dagger}U =U U^{\dagger} = I.

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:

X=(0110)=01+10X = \begin{pmatrix} 0 & 1 \\ 1 & 0 \\ \end{pmatrix} = |0\rangle \langle 1|+|1\rangle \langle 0| Y=(0ii0)=i01+i10Y = \begin{pmatrix} 0 & -i \\ i & 0 \\ \end{pmatrix} = -i|0\rangle \langle 1|+i|1\rangle \langle 0| Z=(1001)=0011Z = \begin{pmatrix} 1 & 0 \\ 0 & -1 \\ \end{pmatrix} = |0\rangle \langle 0|-|1\rangle \langle 1|

dove il prodotto esterno è stato calcolato come segue:

00=[10][10]=[1000],10=[01][10]=[0010],|0\rangle \langle 0|= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} =\begin{bmatrix} 1 & 0 \\ 0 & 0 \\ \end{bmatrix}, \quad |1\rangle \langle 0|= \begin{bmatrix} 0 \\ 1 \end{bmatrix} \begin{bmatrix} 1 & 0 \end{bmatrix} =\begin{bmatrix} 0 & 0 \\ 1 & 0 \\ \end{bmatrix}, \quad 01=[10][01]=[0100],11=[01][01]=[0001],|0\rangle \langle 1|= \begin{bmatrix} 1 \\ 0 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} =\begin{bmatrix} 0 & 1 \\ 0 & 0 \\ \end{bmatrix}, \quad |1\rangle \langle 1|= \begin{bmatrix} 0 \\ 1 \end{bmatrix} \begin{bmatrix} 0 & 1 \end{bmatrix} =\begin{bmatrix} 0 & 0 \\ 0 & 1 \\ \end{bmatrix}, \quad

Altri gate a qubit singolo tipici:

H=12[1111],S=[100i],T=[100exp(iπ/4)]H= \frac{1}{\sqrt{2}}\begin{bmatrix} 1 & 1 \\ 1 & -1 \\ \end{bmatrix},\quad S = \begin{bmatrix} 1 & 0 \\ 0 & i \\ \end{bmatrix}, \quad T = \begin{bmatrix} 1 & 0 \\ 0 & exp(i\pi/4) \\ \end{bmatrix} Rx(θ)=eiθX/2=cosθ2Iisinθ2X=[cosθ2isinθ2isinθ2cosθ2]R_x(\theta) = e^{-i\theta X/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}X = \begin{bmatrix} cos\frac{\theta}{2} & -i sin \frac{\theta}{2} \\ -i sin \frac{\theta}{2} & cos\frac{\theta}{2} \\ \end{bmatrix} Ry(θ)=eiθY/2=cosθ2Iisinθ2Y=[cosθ2sinθ2sinθ2cosθ2]R_y(\theta) = e^{-i\theta Y/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}Y = \begin{bmatrix} cos\frac{\theta}{2} & - sin \frac{\theta}{2} \\ sin \frac{\theta}{2} & cos\frac{\theta}{2} \\ \end{bmatrix} Rz(θ)=eiθZ/2=cosθ2Iisinθ2Z=[eiθ/200eiθ/2]R_z(\theta) = e^{-i\theta Z/2} = cos\frac{\theta}{2}I - i sin \frac{\theta}{2}Z = \begin{bmatrix} e^{-i\theta /2} & 0 \\ 0 & e^{i\theta /2} \\ \end{bmatrix}

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) XX0XX|0\rangle

(2) HH0HH|0\rangle

(3) HZH0HZH|0\rangle

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")

Output of the previous code cell

# 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,))

Output of the previous code cell

### (2) HH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.h(0)
qc.draw("mpl")

Output of the previous code cell

# 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,))

Output of the previous code cell

### (3) HZH|0> ###
##your code goes here##
qc = QuantumCircuit(1)
qc.h(0)
qc.z(0)
qc.h(0)
qc.draw("mpl")

Output of the previous code cell

# 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,))

Output of the previous code cell

3.3 Misurazione

La misurazione è teoricamente un argomento molto complesso. Ma in termini pratici, effettuare una misurazione lungo zz (come fanno tutti i computer quantistici IBM®) semplicemente forza lo stato del qubit α0+β1(s.t.α2+β2=1)\alpha|0\rangle+\beta|1\rangle \quad (s.t.|\alpha|^2+|\beta|^2=1) a diventare 0|0\rangle oppure 1|1\rangle, e noi osserviamo il risultato.

  • α2|\alpha|^2 è la probabilità di ottenere 0|0\rangle quando misuriamo.
  • β2|\beta|^2 è la probabilità di ottenere 1|1\rangle quando misuriamo.

Quindi, α\alpha e β\beta sono chiamate ampiezze di probabilità. (vedi "regola di Born")

Per esempio, 220+221\frac{\sqrt{2}}{2}|0\rangle+\frac{\sqrt{2}}{2}|1\rangle ha la stessa probabilità di diventare 0|0\rangle o 1|1\rangle alla misurazione. 32012i1\frac{\sqrt{3}}{2}|0\rangle-\frac{1}{2}i|1\rangle ha il 75% di probabilità di diventare 0|0\rangle.

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")

Output of the previous code cell

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}

Output of the previous code cell

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")

Output of the previous code cell

# 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: 01|01\rangle significa che q0 è 1|1\rangle e q1 è 0|0\rangle. 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, q0|q_0\rangle è 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.

q1q0=(a0+b1)(c0+d1)|q1\rangle \otimes|q0\rangle = (a|0\rangle+b|1\rangle) \otimes (c|0\rangle+d|1\rangle)

=ac00+ad01+bc10+bd11= ac|0\rangle|0\rangle+ad|0\rangle|1\rangle+bc|1\rangle|0\rangle+bd|1\rangle|1\rangle

=ac00+ad01+bc10+bd11= ac|00\rangle+ad|01\rangle+bc|10\rangle+bd|11\rangle

( ac2+ad2+bc2+bd2=1|ac|^2+ |ad|^2+ |bc|^2+ |bd|^2=1 )

Lo stato iniziale di Qiskit è 00=00|0\rangle|0\rangle=|00\rangle, quindi applicando HH a ciascun qubit si passa a uno stato di sovrapposizione uniforme.

H0H0=12(0+1)12(0+1)=12(00+01+10+11)H|0\rangle \otimes H|0\rangle=\frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) \otimes \frac{1}{\sqrt{2}}(|0\rangle+|1\rangle) = \frac{1}{2}(|00\rangle+|01\rangle+|10\rangle+|11\rangle)

=12((11)(11))=12(1111)=12((1000)+(0100)+(0010)+(0001))=\frac{1}{2}\left( \begin{pmatrix} 1 \\ 1 \end{pmatrix} \otimes \begin{pmatrix} 1 \\ 1 \end{pmatrix}\right) = \frac{1}{2}\begin{pmatrix} 1 \\ 1 \\ 1 \\ 1 \end{pmatrix}=\frac{1}{2}\left(\begin{pmatrix} 1 \\ 0 \\ 0 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 1 \\ 0 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 0 \\ 1 \\ 0 \end{pmatrix}+\begin{pmatrix} 0 \\ 0 \\ 0 \\ 1 \end{pmatrix}\right)

La regola di misura è la stessa del caso a singolo qubit: la probabilità di misurare 00|00\rangle è ac2|ac|^2.

# Draw a Bloch sphere
plot_bloch_multivector(out_vector)

Output of the previous code cell

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")

Output of the previous code cell

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}

Output of the previous code cell

Come previsto, gli stati 00|00\rangle, 01|01\rangle, 10|10\rangle, 11|11\rangle 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 è 1|1\rangle.

Input (target, controllo)Output (target, controllo)
0000
0111
1010
1101

Simuliamo prima l'azione di questo gate a due qubit quando q0 e q1 sono entrambi 0|0\rangle, 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")

Output of the previous code cell

# 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 00|00\rangle non ha modificato lo stato, poiché il qubit di controllo si trovava nello stato 0|0\rangle. Torniamo all'operazione CNOT. Questa volta applicheremo un gate CNOT a 01|01\rangle 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")

Output of the previous code cell

# 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 01|01\rangle è diventato 11|11\rangle.

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")

Output of the previous code cell

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

Output of the previous code cell

I risultati mostrano che 11|11\rangle è 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":

1200+1211\frac{1}{\sqrt{2}}|00\rangle + \frac{1}{\sqrt{2}}|11\rangle

e questo stato si chiama stato di Bell.

Uno stato entangled è uno stato ψAB|\psi_{AB}\rangle composto da stati quantistici ψA|\psi_A\rangle e ψB|\psi_B\rangle che non può essere rappresentato come prodotto tensoriale di stati quantistici individuali.

Se ψAB|\psi_{AB}\rangle ha due stati ψA|\psi\rangle_A e ψB|\psi\rangle_B:

ψAB=12(00+11)=12(0A0B+1A1B)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) = \frac{1}{\sqrt{2}}(|0\rangle_A|0\rangle_B +|1\rangle_A|1\rangle_B) ψA=a00+a11|\psi\rangle_A = a_0|0\rangle+a_1|1\rangle ψB=b00+b11|\psi\rangle_B = b_0|0\rangle+b_1|1\rangle

il prodotto tensoriale di questi due stati è il seguente

ψAψB=a0b000+a0b101+a1b010+a1b111|\psi\rangle _A\otimes |\psi\rangle _B = a_0 b_0|00\rangle+a_0 b_1|01\rangle+a_1 b_0|10\rangle+a_1 b_1|11\rangle

ma non esistono coefficienti a0,a1,b0a_0, a_1, b_0 e b1b_1 in grado di soddisfare entrambe le equazioni. Pertanto, ψAB|\psi_{AB}\rangle non è rappresentabile come prodotto tensoriale di stati quantistici individuali, ψA|\psi\rangle_A e ψB|\psi\rangle_B, e ciò significa che ψAB=12(00+11)|\psi_{AB}\rangle = \frac{1}{\sqrt{2}}(|00\rangle +|11\rangle) è 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:

  1. Map problem to quantum circuits and operators
  2. Optimize for target hardware
  3. Execute on target hardware
  4. 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")

Output of the previous code cell

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, 00|00\rangle e 11|11\rangle 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

12(000+111)\frac{1}{\sqrt 2}(|000\rangle + |111\rangle)

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")

Output of the previous code cell

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 è

12(00000000+11111111)\frac{1}{\sqrt 2}(|00000000\rangle + |11111111\rangle)

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())

Output of the previous code cell

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}

Output of the previous code cell

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'