Vai al contenuto principale

Ansatz e forme variazionali

Al cuore di tutti gli algoritmi variazionali si trova l'idea chiave di analizzare le differenze tra stati, convenientemente collegati tramite una mappatura ben definita (ad esempio, continua e differenziabile) a partire da un insieme di parametri o variabili — da qui il nome.

Per prima cosa esploreremo come costruire circuiti parametrizzati a mano. Useremo questi circuito per definire una forma variazionale che rappresenta una raccolta di stati parametrizzati che il nostro algoritmo variazionale esplorerà. Poi costruiremo il nostro ansatz applicando questa forma variazionale al nostro stato di riferimento.

Esploreremo anche come bilanciare velocità e precisione nell'esplorazione di questo spazio di ricerca.

Un diagramma che mostra i componenti chiave della discussione sull'ansatz, inclusi ansatz euristici e ansatz specifici al problema.

Circuiti quantistici parametrizzati

Gli algoritmi variazionali operano esplorando e confrontando una gamma di stati quantistici ψ(θ)|\psi(\vec{\theta})\rangle, che dipendono da un insieme finito di kk parametri θ=(θ0,,θk1)\vec{\theta} = (\theta^0, \ldots, \theta^{k-1}). Questi stati possono essere preparati tramite un circuito quantistico parametrizzato, in cui i gate sono definiti con parametri regolabili. È possibile creare questo circuito parametrizzato senza assegnare angoli specifici:

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit rustworkx
from qiskit.circuit import QuantumCircuit, Parameter

theta = Parameter("θ")

qc = QuantumCircuit(3)
qc.rx(theta, 0)
qc.cx(0, 1)
qc.x(2)

qc.draw("mpl")

Output of the previous code cell

from math import pi

angle_list = [pi / 3, pi / 2]
circuits = [qc.assign_parameters({theta: angle}) for angle in angle_list]

for circuit in circuits:
display(circuit.draw("mpl"))

Output of the previous code cell

Output of the previous code cell

Forma Variazionale e Ansatz

Per ottimizzare iterativamente a partire da uno stato di riferimento ρ|\rho\rangle verso uno stato target ψ(θ)|\psi(\vec\theta)\rangle, dobbiamo definire una forma variazionale UV(θ)U_V(\vec{\theta}) che rappresenta una raccolta di stati parametrizzati che il nostro algoritmo variazionale esplorerà:

0URUR0=ρUV(θ)UA(θ)0=UV(θ)UR0=UV(θ)ρ=ψ(θ)\begin{aligned} |0\rangle \xrightarrow{U_R} U_R|0\rangle & = |\rho\rangle \xrightarrow{U_V(\vec{\theta})} U_A(\vec{\theta})|0\rangle \\[1mm] & = U_V(\vec{\theta})U_R|0\rangle \\[1mm] & = U_V(\vec{\theta})|\rho\rangle \\[1mm] & = |\psi(\vec{\theta})\rangle \\[1mm] \end{aligned}

Si noti che lo stato parametrizzato dipende sia dallo stato di riferimento ρ|\rho\rangle, che non dipende da alcun parametro, sia dalla forma variazionale UV(θ)U_V(\vec{\theta}), che dipende sempre dai parametri. Chiamiamo ansatz la combinazione di queste due metà: UA(θ):=UV(θ)URU_A(\vec\theta) := U_V(\vec\theta)U_R.

Mentre costruiamo il nostro ansatz per rappresentare una raccolta di stati parametrizzati che il nostro algoritmo variazionale esplorerà, ci imbattiamo in un problema importante: la dimensionalità. Un sistema a nn qubit (cioè lo spazio di Hilbert) ha un numero enorme di stati quantistici distinti nello spazio delle configurazioni. Avremmo bisogno di un numero ingestibile di parametri per esplorarlo completamente. Quantitativamente, la sua dimensionalità è D=22nD = 2^{2n}. Per peggiorare le cose, la complessità computazionale degli algoritmi di ricerca, e di altri simili, cresce esponenzialmente con questa dimensionalità, un fenomeno spesso indicato in letteratura come la maledizione della dimensionalità.

Per far fronte a questo ostacolo, è pratica comune imporre vincoli ragionevoli sulla forma variazionale in modo da esplorare solo gli stati più rilevanti. Trovare un ansatz troncato efficiente è un'area di ricerca attiva, ma tratteremo due design comuni.

Ansatz euristici e compromessi

Se non disponi di alcuna informazione sul tuo problema specifico che possa aiutare a ridurre la dimensionalità, puoi provare una famiglia arbitraria di circuiti parametrizzati con meno di 22n2^{2n} parametri. Tuttavia, ci sono alcuni compromessi da considerare:

  • Velocità: Riducendo lo spazio di ricerca, l'algoritmo può essere eseguito più velocemente.
  • Precisione: Ridurre lo spazio potrebbe rischiare di escludere la soluzione effettiva del problema, portando a soluzioni subottimali.
  • Rumore: I circuiti più profondi sono influenzati dal rumore, quindi dobbiamo sperimentare con la connettività, i gate e la fedeltà dei gate del nostro ansatz.

Esiste un compromesso fondamentale tra qualità (o anche risolvibilità) e velocità: più parametri ci sono, più è probabile trovare un risultato preciso, ma più tempo ci vorrà per eseguire l'algoritmo.

Circuiti N-locali

Uno degli esempi più diffusi di ansatz euristici è quello dei circuit N-locali, per diverse ragioni:

  • Implementazione efficiente: L'ansatz N-locale è tipicamente composto da gate locali semplici che possono essere implementati in modo efficiente su un computer quantistico, utilizzando un numero ridotto di qubit fisici. Ciò rende più facile costruire e ottimizzare i circuiti quantistici.
  • Cattura correlazioni importanti: L'ansatz N-locale può catturare correlazioni importanti tra i qubit in un sistema quantistico, anche con un numero ridotto di gate. Questo perché i gate locali possono agire su qubit vicini e creare entanglement tra loro, il che può essere importante per simulare sistemi quantistici complessi.

Questi circuito sono composti da strati di rotazione e di entanglement che si alternano ripetendosi una o più volte nel modo seguente:

  • Ogni strato è formato da gate di dimensione al massimo NN, dove NN deve essere inferiore al numero di qubit.
  • Per uno strato di rotazione, i gate sono impilati uno sopra l'altro. Possiamo usare operazioni di rotazione standard, come RX o CRZ.
  • Per uno strato di entanglement, possiamo usare gate come i gate Toffoli o CX con una strategia di entanglement.
  • Entrambi i tipi di strati possono essere parametrizzati o meno, ma almeno uno di essi deve contenere parametri. Altrimenti, senza almeno un parametro, non ci sarebbero variazioni!
  • Facoltativamente, uno strato di rotazione aggiuntivo viene aggiunto alla fine del circuito.

Ad esempio, creiamo un circuito NLocal a cinque qubit con blocchi di rotazione formati da gate RX e CRZ, blocchi di entanglement formati da gate Toffoli che agiscono sui qubit [0,1,2][0,1,2], [0,2,3][0,2,3], [4,2,1][4,2,1] e [3,1,0][3,1,0] e 22 ripetizioni di ciascuno strato.

from qiskit.circuit.library import NLocal, CCXGate, CRZGate, RXGate
from qiskit.circuit import Parameter

theta = Parameter("θ")
ansatz = NLocal(
num_qubits=5,
rotation_blocks=[RXGate(theta), CRZGate(theta)],
entanglement_blocks=CCXGate(),
entanglement=[[0, 1, 2], [0, 2, 3], [4, 2, 1], [3, 1, 0]],
reps=2,
insert_barriers=True,
)
ansatz.decompose().draw("mpl")

Output of the previous code cell

Nell'esempio precedente, il gate più grande è il gate Toffoli, che agisce su tre qubit, rendendo il circuito 33-locale. Il tipo più comune di circuit NN-locali sono i circuit 22-locali con gate di rotazione a qubit singolo e gate di entanglement a 22 qubit.

Creiamo un circuit 22-locale usando la classe TwoLocal di Qiskit. La sintassi è la stessa di NLocal, ma ci sono alcune differenze. Ad esempio, la maggior parte dei gate, come RX, RZ e CNOT, può essere passata come stringa senza importare i gate o creare un'istanza di Parameter.

from qiskit.circuit.library import TwoLocal

ansatz = TwoLocal(
num_qubits=5,
rotation_blocks=["rx", "rz"],
entanglement_blocks="cx",
entanglement="linear",
reps=2,
insert_barriers=True,
)
ansatz.decompose().draw("mpl")

Output of the previous code cell

In questo caso abbiamo usato la distribuzione di entanglement lineare, in cui ogni qubit è collegato al successivo. Per conoscere le altre strategie, consulta la documentazione di TwoLocal.

Efficient SU2

efficient_su2 è un circuito hardware-efficiente composto da strati di operazioni a qubit singolo che coprono SU(2) e entanglement tramite CX. Si tratta di uno schema euristico che può essere usato per preparare funzioni d'onda di prova per algoritmi quantistici variazionali o come circuito di classificazione per il machine learning.

from qiskit.circuit.library import efficient_su2

ansatz = efficient_su2(4, su2_gates=["rx", "y"], entanglement="linear", reps=1)
ansatz.decompose().draw("mpl")

Output of the previous code cell

Ansatz specifici al problema

Mentre gli ansatz euristici e hardware-efficienti ci aiutano a risolvere un problema in modo generico, possiamo sfruttare la conoscenza specifica del problema per restringere lo spazio di ricerca del circuito a un tipo specifico. Questo ci permetterà di guadagnare velocità senza perdere precisione nel processo di ricerca.

Ottimizzazione

In un problema di max-cut, vogliamo partizionare i nodi di un grafo in modo da massimizzare il numero di archi tra nodi in gruppi diversi. La partizione max-cut desiderata per il grafo seguente è evidente: il nodo 0 a sinistra deve essere separato dal resto dei nodi a destra da un taglio.

import rustworkx as rx
from rustworkx.visualization import mpl_draw

n = 4
G = rx.PyGraph()
G.add_nodes_from(range(n))
# The edge syntax is (start, end, weight)
edges = [(0, 1, 1.0), (0, 2, 1.0), (0, 3, 1.0), (1, 2, 1.0), (2, 3, 1.0)]
G.add_edges_from(edges)

mpl_draw(
G, pos=rx.shell_layout(G), with_labels=True, edge_labels=str, node_color="#1192E8"
)

Output of the previous code cell

Per utilizzare l'algoritmo QAOA per un problema di max-cut, abbiamo bisogno di un Hamiltoniano di Pauli che codifichi il costo in modo tale che il valore di aspettazione minimo dell'operatore corrisponda al numero massimo di archi tra nodi in due gruppi diversi.

Per questo semplice esempio, l'operatore è una combinazione lineare di termini con operatori Z sui nodi collegati da un arco (ricorda che il 0° qubit è il più a destra): ZZII+IZZI+ZIIZ+IZIZ+IIZZZZII + IZZI + ZIIZ + IZIZ + IIZZ. Una volta costruito l'operatore, l'ansatz per l'algoritmo QAOA può essere facilmente creato usando il circuito QAOAAnsatz della libreria di circuito Qiskit.

# Pre-defined ansatz circuit, operator class and visualization tools
from qiskit.circuit.library import QAOAAnsatz
from qiskit.quantum_info import SparsePauliOp

# Problem to Hamiltonian operator
hamiltonian = SparsePauliOp.from_list(
[("ZZII", 1), ("IZZI", 1), ("ZIIZ", 1), ("IZIZ", 1), ("IIZZ", 1)]
)
# QAOA ansatz circuit
ansatz = QAOAAnsatz(hamiltonian, reps=2)
# Draw
ansatz.decompose(reps=3).draw("mpl")

Output of the previous code cell

L'immagine precedente illustra l'ansatz nei gate di base per chiarezza. Tuttavia, può essere espresso a più livelli di decomposizione modificando l'argomento reps oppure disegnando il circuito senza il metodo decompose. Ad esempio, la seguente rappresentazione mostra direttamente la struttura QAOA con il valore predefinito di reps, che è reps=1.

ansatz.decompose(reps=2).draw("mpl")

Output of the previous code cell

Quantum Machine Learning

Nel machine learning, un'applicazione comune è la classificazione dei dati in due o più categorie. Questo comporta la codifica di un punto dati in una feature map che mappa i vettori di caratteristiche classici nello spazio di Hilbert quantistico. Costruire feature map quantistiche basate su circuiti quantistici parametrizzati che sono difficili da simulare classicamente è un passo importante verso l'ottenimento di un potenziale vantaggio rispetto agli approcci classici di machine learning ed è un'area di ricerca attiva.

La zz_feature_map può essere usata per creare un circuito parametrizzato. Possiamo passare i nostri punti dati alla feature map (xx) e una forma variazionale separata per passare i pesi come parametri (θ\theta).

from qiskit.circuit.library import zz_feature_map, TwoLocal

data = [0.1, 0.2]

zz_feature_map_reference = zz_feature_map(feature_dimension=2, reps=2)
zz_feature_map_reference = zz_feature_map_reference.assign_parameters(data)

variation_form = TwoLocal(2, ["ry", "rz"], "cz", reps=2)
vqc_ansatz = zz_feature_map_reference.compose(variation_form)
vqc_ansatz.decompose().draw("mpl")

Output of the previous code cell

Riepilogo

Con questa lezione hai imparato come definire il tuo spazio di ricerca con una forma variazionale:

  • Preparare stati con un circuito quantistico parametrizzato, in cui i gate sono definiti con parametri regolabili
  • Come costruire ansatz che bilancino velocità e precisione
  • Ansatz euristici
  • Ansatz specifici al problema

Il nostro flusso di lavoro variazionale ad alto livello è il seguente:

Un diagramma circuitale che mostra due unitarie: una che prepara lo stato di riferimento e un'altra che prepara l'ansatz.

Per ogni parametro variazionale θ\vec\theta, verrà prodotto uno stato quantistico diverso. Per trovare i parametri ottimali, dobbiamo definire una funzione di costo specifica al problema per aggiornare iterativamente i parametri del nostro ansatz.