Codifica dei dati
Introduzione e notazione
Per utilizzare un algoritmo quantistico, i dati classici devono essere in qualche modo introdotti in un circuito quantistico. Questo processo è solitamente denominato codifica dei dati, ma viene anche chiamato caricamento dei dati. Ricorda dalle lezioni precedenti il concetto di feature mapping, ovvero una mappatura delle caratteristiche dei dati da uno spazio a un altro. Il semplice trasferimento di dati classici su un computer quantistico è una sorta di mappatura, e potrebbe essere definita un feature mapping. In pratica, i feature mapping integrati in Qiskit (come z_Feature Map e ZZ Feature Map) includono tipicamente layer di rotazione e layer di entanglement che estendono lo stato a molte dimensioni nello spazio di Hilbert. Questo processo di codifica è una parte critica degli algoritmi di machine learning quantistico e influisce direttamente sulle loro capacità computazionali.
Alcune delle tecniche di codifica descritte di seguito possono essere simulate in modo efficiente in modo classico; questo è particolarmente evidente nei metodi di codifica che producono stati prodotto (cioè, che non creano entanglement tra i qubit). Ricorda inoltre che l'utilità quantistica è più probabile laddove la complessità di tipo quantistico del dataset corrisponda bene al metodo di codifica. Quindi è molto probabile che tu finisca per scrivere i tuoi circuiti di codifica personalizzati. Qui mostriamo un'ampia varietà di possibili strategie di codifica semplicemente per permetterti di confrontarle e metterle in contrasto, e per vedere cosa è possibile fare. Alcune affermazioni molto generali possono essere fatte sull'utilità delle tecniche di codifica. Ad esempio, efficient_su2 (vedi di seguito) con uno schema di entanglement completo ha molte più probabilità di catturare caratteristiche quantistiche dei dati rispetto ai metodi che producono stati prodotto (come z_feature_map). Ma questo non significa che efficient_su2 sia sufficiente, o sufficientemente adatto al tuo dataset, per produrre un'accelerazione quantistica. Ciò richiede un'attenta considerazione della struttura dei dati da modellare o classificare. Esiste anche un compromesso con la profondità del circuito, poiché molti feature map che effettuano un entanglement completo dei qubit in un circuito producono circuiti molto profondi, troppo profondi per ottenere risultati utilizzabili sui computer quantistici odierni.
Notazione
Un dataset è un insieme di vettori di dati: , dove ogni vettore è di dimensione , ovvero . Questo potrebbe essere esteso a caratteristiche dei dati complesse. In questa lezione, utilizzeremo occasionalmente queste notazioni per l'insieme completo e i suoi elementi specifici come . Tuttavia, ci riferiremo principalmente al caricamento di un singolo vettore dal nostro dataset alla volta, e spesso faremo riferimento semplicemente a un singolo vettore di caratteristiche come .
Inoltre, è comune usare il simbolo per riferirsi al feature mapping del vettore di dati . Nell'informatica quantistica in particolare, è comune riferirsi alle mappature usando una notazione che enfatizza la natura unitaria di queste operazioni. Si potrebbe correttamente usare lo stesso simbolo per entrambe; entrambe sono feature mapping. Nel corso di questo corso, tendenzialmente utilizzeremo:
- quando discutiamo di feature mapping nel machine learning in generale, e
- quando discutiamo di implementazioni circuitali dei feature mapping.
Normalizzazione e perdita di informazione
Nel machine learning classico, le caratteristiche dei dati di addestramento vengono spesso "normalizzate" o ridimensionate, il che migliora spesso le prestazioni del modello. Un metodo comune per farlo è la normalizzazione min-max o la standardizzazione. Nella normalizzazione min-max, le colonne delle caratteristiche della matrice di dati (ad esempio, la caratteristica ) vengono normalizzate:
dove min e max si riferiscono al minimo e al massimo della caratteristica sui vettori di dati nel dataset . Tutti i valori delle caratteristiche ricadono quindi nell'intervallo unitario: per tutti , .
La normalizzazione è anche un concetto fondamentale nella meccanica quantistica e nell'informatica quantistica, ma è leggermente diversa dalla normalizzazione min-max. La normalizzazione nella meccanica quantistica richiede che la lunghezza (nel contesto dell'informatica quantistica, la 2-norma) di un vettore di stato sia uguale all'unità: , garantendo che le probabilità di misura sommino a 1. Lo stato viene normalizzato dividendo per la 2-norma; cioè, ridimensionando
Nell'informatica quantistica e nella meccanica quantistica, questa non è una normalizzazione imposta dalle persone sui dati, ma una proprietà fondamentale degli stati quantistici. A seconda dello schema di codifica scelto, questo vincolo può influire sul modo in cui i dati vengono ridimensionati. Ad esempio, nella codifica in ampiezza (vedi di seguito), il vettore di dati viene normalizzato come richiesto dalla meccanica quantistica, e questo influisce sulla scala dei dati codificati. Nella codifica di fase, si raccomanda di ridimensionare i valori delle caratteristiche come in modo che non vi sia perdita di informazione dovuta all'effetto modulo- della codifica nell'angolo di fase di un qubit[1,2].
Metodi di codifica
Nelle prossime sezioni, faremo riferimento a un piccolo dataset classico di esempio composto da vettori di dati, ciascuno con caratteristiche:
Nella notazione introdotta sopra, potremmo dire che la caratteristica del vettore di dati nel nostro insieme è ad esempio.
Codifica in base
La codifica in base codifica una stringa di bit classici in uno stato della base computazionale di un sistema a qubit. Prendiamo ad esempio Questo può essere rappresentato come una stringa di bit come , e da un sistema a qubit come lo stato quantistico . Più in generale, per una stringa di bit: , il corrispondente stato a qubit è con per . Nota che questo riguarda una singola caratteristica.
La codifica in base nell'informatica quantistica rappresenta ogni bit classico come un qubit separato, mappando la rappresentazione binaria dei dati direttamente sugli stati quantistici nella base computazionale. Quando è necessario codificare più caratteristiche, ciascuna viene prima convertita nella sua forma binaria e poi assegnata a un gruppo distinto di qubit — un gruppo per caratteristica — dove ogni qubit riflette un bit nella rappresentazione binaria di quella caratteristica.
Come esempio, codifichiamo il vettore (5, 7, 0).
Supponiamo che tutte le caratteristiche siano memorizzate in quattro bit (più del necessario, ma sufficienti per rappresentare qualsiasi intero a una cifra in base 10):
5 → binario 0101
7 → binario 0111
0 → binario 0000
Queste stringhe di bit sono assegnate a tre gruppi di quattro qubit, quindi lo stato complessivo a 12 qubit nella base è:
Qui, i primi quattro qubit rappresentano la prima caratteristica, i successivi quattro la seconda caratteristica, e gli ultimi quattro la terza caratteristica. Il codice seguente converte il vettore di dati (5,7,0) in uno stato quantistico, ed è generalizzato per fare lo stesso con altre caratteristiche a singola cifra.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit
from qiskit import QuantumCircuit
# Data point to encode
x = 5 # binary: 0101
y = 7 # binary: 0111
z = 0 # binary: 0000
# Convert each to 4-bit binary list
x_bits = [int(b) for b in format(x, "04b")] # [0,1,0,1]
y_bits = [int(b) for b in format(y, "04b")] # [0,1,1,1]
z_bits = [int(b) for b in format(z, "04b")] # [0,0,0,0]
# Combine all bits
all_bits = x_bits + y_bits + z_bits # [0,1,0,1,0,1,1,1,0,0,0,0]
# Initialize a 12-qubit quantum circuit
qc = QuantumCircuit(12)
# Apply x-gates where the bit is 1
for idx, bit in enumerate(all_bits):
if bit == 1:
qc.x(idx)
qc.draw("mpl")

Verifica la tua comprensione
Leggi la domanda seguente, rifletti sulla tua risposta, poi clicca il triangolo per rivelare la soluzione.
Scrivi del codice per codificare il primo vettore del nostro dataset di esempio :
utilizzando la codifica in base.
Risposta:
import math
from qiskit import QuantumCircuit
# Data point to encode
x = 4 # binary: 0100
y = 8 # binary: 1000
z = 5 # binary: 0101
# Convert each to 4-bit binary list
x_bits = [int(b) for b in format(x, '04b')] # [0,1,0,0]
y_bits = [int(b) for b in format(y, '04b')] # [1,0,0,0]
z_bits = [int(b) for b in format(z, '04b')] # [0,1,0,1]
# Combine all bits
all_bits = x_bits + y_bits + z_bits # [0,1,0,0,1,0,0,0,0,1,0,1]
# Initialize a 12-qubit quantum circuit
qc = QuantumCircuit(12)
# Apply x-gates where the bit is 1
for idx, bit in enumerate(all_bits):
if bit == 1:
qc.x(idx)
qc.draw('mpl')
Codifica in ampiezza
La codifica in ampiezza codifica i dati nelle ampiezze di uno stato quantistico. Rappresenta un vettore di dati classico -dimensionale normalizzato, , come le ampiezze di uno stato quantistico a qubit, :
dove è la stessa dimensione dei vettori di dati di prima, è l'-esimo elemento di e è l'-esimo stato della base computazionale. Qui, è una costante di normalizzazione da determinare a partire dai dati da codificare. Questa è la condizione di normalizzazione imposta dalla meccanica quantistica:
In generale, questa è una condizione diversa dalla normalizzazione min/max utilizzata per ciascuna caratteristica su tutti i vettori di dati. Il modo in cui questo viene gestito dipenderà dal tuo problema specifico. Ma la condizione di normalizzazione della meccanica quantistica qui sopra non può essere aggirata.
Nella codifica in ampiezza, ogni caratteristica in un vettore di dati è memorizzata come l'ampiezza di un diverso stato quantistico. Poiché un sistema di qubit fornisce ampiezze, la codifica in ampiezza di caratteristiche richiede qubit.
Come esempio, codifichiamo il primo vettore del nostro dataset di esempio , utilizzando la codifica in ampiezza. Normalizzando il vettore risultante, otteniamo:
e il risultante stato quantistico a 2 qubit sarebbe:
Nell'esempio precedente, il numero di caratteristiche nel vettore non è una potenza di 2. Quando non è una potenza di 2, scegliamo semplicemente un valore per il numero di qubit tale che e aggiungiamo costanti non informative come padding al vettore delle ampiezze (in questo caso, uno zero).
Come nella codifica in base, una volta calcolato lo stato che codificherà il nostro dataset, in Qiskit possiamo usare la funzione initialize per prepararlo:
import math
desired_state = [
1 / math.sqrt(105) * 4,
1 / math.sqrt(105) * 8,
1 / math.sqrt(105) * 5,
1 / math.sqrt(105) * 0,
]
qc = QuantumCircuit(2)
qc.initialize(desired_state, [0, 1])
qc.decompose(reps=5).draw(output="mpl")
Un vantaggio della codifica in ampiezza è il requisito di soli qubit per la codifica. Tuttavia, gli algoritmi successivi devono operare sulle ampiezze di uno stato quantistico, e i metodi per preparare e misurare gli stati quantistici tendono a non essere efficienti.
Verifica la tua comprensione
Leggi le domande seguenti, rifletti sulle tue risposte, poi clicca i triangoli per rivelare le soluzioni.
Scrivi lo stato normalizzato per la codifica del seguente vettore (composto da due vettori del nostro dataset di esempio):
utilizzando la codifica in ampiezza.
Risposta:
Per codificare 6 numeri, avremo bisogno di almeno 6 stati disponibili sulle cui ampiezze possiamo codificare. Questo richiederà 3 qubit. Usando un fattore di normalizzazione sconosciuto , possiamo scrivere:
Nota che
Quindi infine,
Per lo stesso vettore di dati scrivi del codice per creare un circuito che carichi queste caratteristiche dei dati usando la codifica in ampiezza.
Risposta:
desired_state = [
9 / math.sqrt(270),
8 / math.sqrt(270),
6 / math.sqrt(270),
2 / math.sqrt(270),
9 / math.sqrt(270),
2 / math.sqrt(270),
0,
0,
]
print(desired_state)
qc = QuantumCircuit(3)
qc.initialize(desired_state, [0, 1, 2])
qc.decompose(reps=8).draw(output="mpl")
[0.5477225575051662, 0.48686449556014766, 0.36514837167011077, 0.12171612389003691, 0.5477225575051662, 0.12171612389003691, 0, 0]
Potresti dover gestire vettori di dati molto grandi. Considera il vettore
Scrivi del codice per automatizzare la normalizzazione e generare un circuito quantistico per la codifica in ampiezza.
Risposta:
Ci sono molte possibili risposte. Ecco del codice che stampa alcuni passaggi intermedi:
import numpy as np
from math import sqrt
init_list = [4, 8, 5, 9, 8, 6, 2, 9, 2, 5, 7, 0, 3, 7, 5]
qubits = round(np.log(len(init_list)) / np.log(2) + 0.4999999999)
need_length = 2**qubits
pad = need_length - len(init_list)
for i in range(0, pad):
init_list.append(0)
init_array = np.array(init_list) # Unnormalized data vector
length = sqrt(
sum(init_array[i] ** 2 for i in range(0, len(init_array)))
) # Vector length
norm_array = init_array / length # Normalized array
print("Normalized array:")
print(norm_array)
print()
qubit_numbers = []
for i in range(0, qubits):
qubit_numbers.append(i)
print(qubit_numbers)
qc = QuantumCircuit(qubits)
qc.initialize(norm_array, qubit_numbers)
qc.decompose(reps=7).draw(output="mpl")
Normalized array: [0.17342199 0.34684399 0.21677749 0.39019949 0.34684399 0.26013299 0.086711 0.39019949 0.086711 0.21677749 0.30348849 0. 0.1300665 0.30348849 0.21677749 0. ]
[0, 1, 2, 3]

Vedi vantaggi nella codifica in ampiezza rispetto alla codifica in base? Se sì, spiegali.
Risposta:
Potrebbero esserci diverse risposte. Una risposta è che, dato l'ordine fisso degli stati della base, questa codifica in ampiezza preserva l'ordine dei numeri codificati. Spesso verrà anche codificata in modo più denso.
Un vantaggio della codifica in ampiezza è che sono necessari solo qubit per un vettore di dati -dimensionale (a caratteristiche) . Tuttavia, la codifica in ampiezza è generalmente una procedura inefficiente che richiede una preparazione di stato arbitraria, che è esponenziale nel numero di gate CNOT. Detto diversamente, la preparazione dello stato ha una complessità di runtime polinomiale di nel numero di dimensioni, dove , e è il numero di qubit. La codifica in ampiezza "fornisce un risparmio esponenziale in spazio al costo di un aumento esponenziale in tempo"[3]; tuttavia, aumenti del runtime a sono realizzabili in certi casi[4]. Per un'accelerazione quantistica end-to-end, è necessario considerare la complessità del runtime di caricamento dei dati.