Algoritmi quantistici: Stima della fase
Kento Ueda (15 maggio 2024)
Questo notebook fornisce i concetti fondamentali e l'implementazione della Trasformata di Fourier Quantistica (QFT) e della Stima della Fase Quantistica (QPE).
Scarica il pdf della lezione originale. Nota che alcuni frammenti di codice potrebbero risultare deprecati poiché si tratta di immagini statiche.
Il tempo QPU approssimativo per eseguire questo esperimento è di 7 secondi.
1. Introduzione
Trasformata di Fourier Quantistica (QFT)
La Trasformata di Fourier Quantistica è l'equivalente quantistico della trasformata di Fourier discreta classica. È una trasformazione lineare applicata agli stati quantistici, che mappa le basi computazionali nelle loro rappresentazioni nella base di Fourier. La QFT svolge un ruolo chiave in molti algoritmi quantistici, offrendo un metodo efficiente per estrarre informazioni di periodicità dagli stati quantistici. La QFT può essere implementata con operazioni tramite gate quantistici come i gate di Hadamard e i gate di fase controllata per qubit, consentendo un'accelerazione esponenziale rispetto alla trasformata di Fourier classica.
- Applicazioni: È una componente fondamentale in algoritmi quantistici come l'algoritmo di Shor per la fattorizzazione di interi grandi e il logaritmo discreto.
Stima della Fase Quantistica (QPE)
La Stima della Fase Quantistica è un algoritmo quantistico utilizzato per stimare la fase associata a un autovettore di un operatore unitario. Questo algoritmo fornisce un ponte tra le proprietà matematiche astratte degli stati quantistici e le loro applicazioni computazionali.
- Applicazioni: Può risolvere problemi come il calcolo degli autovalori di matrici unitarie e la simulazione di sistemi quantistici.
Insieme, QFT e QPE costituiscono la spina dorsale essenziale di molti algoritmi quantistici che risolvono problemi impossibili per i computer classici. Al termine di questo notebook, avrai acquisito una comprensione di come queste tecniche vengono implementate.
2. Basi della Trasformata di Fourier Quantistica (QFT)
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator
from qiskit.visualization import plot_histogram, plot_bloch_multivector
from qiskit.quantum_info import Statevector
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from numpy import pi
Per analogia con la trasformata di Fourier discreta, la QFT agisce su uno stato quantistico per qubit e lo mappa nello stato quantistico .
dove .
Oppure nella rappresentazione matriciale unitaria:
2.1. Intuizione
La trasformata di Fourier quantistica (QFT) opera una trasformazione tra due basi: la base computazionale (Z) e la base di Fourier. Ma cosa significa la base di Fourier in questo contesto? Probabilmente ricordi che la trasformata di Fourier di una funzione descrive la convoluzione di su una funzione sinusoidale con frequenza . In termini semplici: la trasformata di Fourier è una funzione che descrive quanta parte di ciascuna frequenza sarebbe necessaria per costruire una funzione a partire da funzioni seno (o coseno). Per avere un'idea più chiara di cosa significhi la QFT in questo contesto, considera le immagini in sequenza qui sotto, che mostrano un numero codificato in binario usando quattro qubit:
Nella base computazionale, i numeri vengono memorizzati in binario usando gli stati e .
Nota la frequenza con cui i diversi qubit cambiano: il qubit più a sinistra si capovolge a ogni incremento del numero, il successivo ogni 2 incrementi, il terzo ogni 4 incrementi, e così via.
Se applichiamo una trasformata di Fourier quantistica a questi stati, otteniamo la mappa:
(Spesso denotiamo gli stati nella base di Fourier con la tilde (~)).
Nella base di Fourier, i numeri vengono memorizzati utilizzando diverse rotazioni attorno all'asse Z:
Il numero che vogliamo memorizzare determina l'angolo di rotazione di ciascun qubit attorno all'asse Z. Nello stato , tutti i qubit sono nello stato . Come mostrato nell'esempio precedente, per codificare lo stato su 4 qubit, abbiamo ruotato il qubit più a sinistra di giri completi ( radianti). Il qubit successivo viene ruotato del doppio ( radianti, ovvero giri completi), questo angolo viene poi raddoppiato per il qubit successivo, e così via.
Ancora una volta, nota la frequenza con cui cambia ciascun qubit. Il qubit più a sinistra (qubit 0) in questo caso ha la frequenza più bassa, e quello più a destra la più alta.
2.2 Esempio: QFT a 1 qubit
Consideriamo il caso .
La matrice unitaria può essere scritta come:
Questa operazione è il risultato dell'applicazione del gate di Hadamard ().
2.3 Rappresentazione a prodotto della QFT
Generalizziamo la trasformazione per , applicata allo stato .
2.4 Esempio: Costruzione del circuito QFT a 3 qubit
Dalla descrizione precedente, potrebbe non essere immediatamente chiaro come costruire un circuito QFT. Per ora, nota semplicemente che ci aspettiamo che i tre qubit abbiano fasi che evolvono a "velocità" diverse. Capire esattamente come questo si traduca in un circuito è lasciato come esercizio al lettore. Nella dispensa in pdf sono presenti diversi diagrammi ed esempi. Ulteriori risorse includono questa lezione del corso sui Fondamenti degli algoritmi quantistici.
Dimostreremo la QFT usando solo simulatori, quindi non utilizzeremo il framework Qiskit patterns fino a quando non passeremo alla stima della fase quantistica.
# Prepare for 3 qubits circuit
qr = QuantumRegister(3)
cr = ClassicalRegister(3)
qc = QuantumCircuit(qr, cr)
qc.h(2)
qc.cp(pi / 2, 1, 2) # Controlled-phase gate from qubit 1 to qubit 2
qc.cp(pi / 4, 0, 2) # Controlled-phase gate from qubit 0 to qubit 2
qc.draw(output="mpl")
qc.h(1)
qc.cp(pi / 2, 0, 1) # Controlled-phase gate from qubit 0 to qubit 1
qc.draw(output="mpl")
qc.h(0)
qc.draw(output="mpl")
qc.swap(0, 2)
qc.draw(output="mpl")
Proviamo ad applicare la QFT a come esempio.
Per prima cosa, confermiamo la notazione binaria dell'intero 5 e creiamo il circuito che codifica lo stato 5:
bin(5)
'0b101'
qc = QuantumCircuit(3)
qc.x(0)
qc.x(2)
qc.draw(output="mpl")
Verifichiamo gli stati quantistici usando il simulatore Aer:
statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Infine, aggiungiamo la QFT e visualizziamo lo stato finale dei nostri qubit:
qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)
qc.h(1)
qc.cp(pi / 2, 0, 1)
qc.h(0)
qc.swap(0, 2)
qc.draw(output="mpl")
statevector = Statevector(qc)
plot_bloch_multivector(statevector)

Possiamo vedere che la nostra funzione QFT ha funzionato correttamente. Il qubit 0 è stato ruotato di di giro completo, il qubit 1 di giri completi (equivalente a di giro completo), e il qubit 2 di giri completi (equivalente a di giro completo).
2.5 Esercizio: QFT
(1) Implementa la QFT a 4 qubit.
##your code goes here##
(2) Applica la QFT a , simula e traccia il vettore di stato usando la sfera di Bloch.
##your code goes here##
Soluzione dell'esercizio: QFT
(1)
qr = QuantumRegister(4)
cr = ClassicalRegister(4)
qc = QuantumCircuit(qr, cr)
qc.h(3)
qc.cp(pi / 2, 2, 3)
qc.cp(pi / 4, 1, 3)
qc.cp(pi / 8, 0, 3)
qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)
qc.h(1)
qc.cp(pi / 2, 0, 1)
qc.h(0)
qc.swap(0, 3)
qc.swap(1, 2)
qc.draw(output="mpl")
(2)
bin(14)
'0b1110'
qc = QuantumCircuit(4)
qc.x(1)
qc.x(2)
qc.x(3)
qc.draw("mpl")
qc.h(3)
qc.cp(pi / 2, 2, 3)
qc.cp(pi / 4, 1, 3)
qc.cp(pi / 8, 0, 3)
qc.h(2)
qc.cp(pi / 2, 1, 2)
qc.cp(pi / 4, 0, 2)
qc.h(1)
qc.cp(pi / 2, 0, 1)
qc.h(0)
qc.swap(0, 3)
qc.swap(1, 2)
qc.draw(output="mpl")
statevector = Statevector(qc)
plot_bloch_multivector(statevector)

3. Basi della Stima della Fase Quantistica (QPE)
Data un'operazione unitaria , la QPE stima in ; poiché è unitario, tutti i suoi autovalori hanno norma 1.
3.1 Procedura
1. Configurazione
si trova in un insieme di registri qubit. Un ulteriore insieme di qubit costituisce il registro di conteggio in cui memorizzeremo il valore :
2. Sovrapposizione
Applica un'operazione di gate di Hadamard a bit sul registro di conteggio:
3. Operazioni unitarie controllate
Dobbiamo introdurre l'unitario controllato che applica l'operatore unitario al registro target solo se il corrispondente bit di controllo è . Poiché è un operatore unitario con autovettore tale che , questo implica:
3.2 Esempio: QPE con il gate T
Usiamo il gate come esempio di QPE e stimiamo la sua fase .
Ci aspettiamo di trovare:
dove
Inizializziamo come autovettore del gate applicando un gate :
qpe = QuantumCircuit(4, 3)
qpe.x(3)
qpe.draw(output="mpl")
Successivamente, applichiamo i gate di Hadamard ai qubit di conteggio:
for qubit in range(3):
qpe.h(qubit)
qpe.draw(output="mpl")
Eseguiamo le operazioni unitarie controllate:
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(pi / 4, counting_qubit, 3) # This is C-U
repetitions *= 2
qpe.draw(output="mpl")
Applichiamo la trasformata di Fourier quantistica inversa per convertire lo stato del registro di conteggio, poi misuriamo il registro di conteggio:
from qiskit.circuit.library import QFT
# Apply inverse QFT
qpe.append(QFT(3, inverse=True), [0, 1, 2])
qpe.draw(output="mpl")
for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")
Possiamo simulare usando il simulatore Aer:
aer_sim = AerSimulator()
shots = 2048
pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)
t_qpe = pm.run(qpe)
sampler = Sampler(mode=aer_sim)
job = sampler.run([t_qpe], shots=shots)
result = job.result()
answer = result[0].data.c.get_counts()
plot_histogram(answer)
Vediamo che otteniamo un solo risultato (001) con certezza, che si traduce in decimale: 1. Ora dobbiamo dividere il nostro risultato (1) per per ottenere :
L'algoritmo di Shor ci permette di fattorizzare un numero costruendo un circuito con incognito e ottenendo .
3.3 Esercizio
Stima usando 3 qubit per il conteggio e un qubit per un autovettore.
. Poiché vogliamo implementare , dobbiamo impostare .
##your code goes here##
Soluzione dell'esercizio:
# Create and set up circuit
qpe = QuantumCircuit(4, 3)
# Apply H-Gates to counting qubits:
for qubit in range(3):
qpe.h(qubit)
# Prepare our eigenstate |psi>:
qpe.x(3)
# Do the controlled-U operations:
angle = 2 * pi / 3
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(angle, counting_qubit, 3)
repetitions *= 2
# Do the inverse QFT:
qpe.append(QFT(3, inverse=True), [0, 1, 2])
for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")
aer_sim = AerSimulator()
shots = 4096
pm = generate_preset_pass_manager(backend=aer_sim, optimization_level=1)
t_qpe = pm.run(qpe)
sampler = Sampler(mode=aer_sim)
job = sampler.run([t_qpe], shots=shots)
result = job.result()
answer = result[0].data.c.get_counts()
plot_histogram(answer)
4. Esecuzione con la primitiva Sampler di Qiskit Runtime
Eseguiremo la QPE usando un dispositivo quantistico reale e seguiremo i 4 passi dei pattern Qiskit.
- Mappa il problema in un formato nativo quantistico
- Ottimizza i circuiti
- Esegui il circuito target
- Post-elabora i risultati
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Sampler
# Loading your IBM Quantum® account(s)
service = QiskitRuntimeService()
4.1 Passo 1: Mappa il problema in circuiti e operatori quantistici
qpe = QuantumCircuit(4, 3)
qpe.x(3)
for qubit in range(3):
qpe.h(qubit)
repetitions = 1
for counting_qubit in range(3):
for i in range(repetitions):
qpe.cp(pi / 4, counting_qubit, 3) # This is C-U
repetitions *= 2
qpe.append(QFT(3, inverse=True), [0, 1, 2])
for n in range(3):
qpe.measure(n, n)
qpe.draw(output="mpl")
backend = service.least_busy(simulator=False, operational=True, min_num_qubits=4)
print(backend)
<IBMBackend('ibm_strasbourg')>
4.2 Passo 2: Ottimizza per l'hardware target
# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qpe)
qc_compiled.draw("mpl", idle_wires=False)

4.3 Passo 3: Esegui sull'hardware target
real_sampler = Sampler(mode=backend)
job = real_sampler.run([qc_compiled], shots=1024)
job_id = job.job_id()
print("job id:", job_id)
job id: d13p4zb5z6q00087ag00
job = service.job(job_id) # Input your job-id between the quotations
job.status()
'DONE'
result_real = job.result()
print(result_real)
PrimitiveResult([SamplerPubResult(data=DataBin(c=BitArray(<shape=(), num_shots=1024, num_bits=3>)), metadata={'circuit_metadata': {}})], metadata={'execution': {'execution_spans': ExecutionSpans([DoubleSliceSpan(<start='2025-06-09 22:39:00', stop='2025-06-09 22:39:00', size=1024>)])}, 'version': 2})
4.4 Passo 4: Post-elabora i risultati
from qiskit.visualization import plot_histogram
plot_histogram(result_real[0].data.c.get_counts())
# See the version of Qiskit
import qiskit
qiskit.__version__
'2.0.2'