Compilatore quantistico approssimativo
qiskit.synthesis.unitary.aqc
Implementazione del compilatore quantistico approssimativo descritto nell'articolo [1].
Interfaccia
L'interfaccia pubblica principale di questo modulo si raggiunge passando unitary_synthesis_method='aqc' a transpile(). In questo modo si cambia il metodo di sintesi per utilizzare AQCSynthesisPlugin. Le singole classi sono:
AQC( [ottimizzatore, seme] ) | Un'implementazione generica del compilatore quantistico approssimativo. |
ApproximateCircuit(num_qubit[, nome] ) | Una classe base che rappresenta un circuito approssimativo. |
ApproximatingObjective() | Una classe di base per la definizione di un problema di ottimizzazione. |
CNOTUnitCircuit(num_qubit, cnot[, tol, nome] ) | Una classe che rappresenta un circuito approssimato basato su blocchi di unità CNOT. |
CNOTUnitObjective(num_qubit, cnodi) | Una classe base per la definizione di un problema basato sull'unità CNOT. |
DefaultCNOTUnitObjective(num_qubit, cnodi) | Un'implementazione ingenua della funzione obiettivo basata sulle unità CNOT. |
FastCNOTUnitObjective(num_qubit, cnodi) | Implementazione della funzione obiettivo e del calcolatore del gradiente, simile a DefaultCNOTUnitObjective ma più veloce. |
Dettagli matematici
Siamo interessati alla compilazione di un circuito quantistico, che formalizziamo come la ricerca della migliore rappresentazione del circuito in termini di una sequenza ordinata di gate di una matrice unitaria target , con alcuni vincoli hardware aggiuntivi. In particolare, esaminiamo le rappresentazioni che potrebbero essere vincolate in termini di connettività hardware e di profondità del gate e scegliamo una base di gate in termini di CNOT e di gate di rotazione. Ricordiamo che la combinazione di CNOT e porte di rotazione è universale in e quindi non limita la compilazione.
Per definire correttamente cosa intendiamo per "migliore rappresentazione del circuito", definiamo la metrica come la norma di Frobenius tra la matrice unitaria del circuito compilato e la matrice unitaria di riferimento , ovvero . Questa scelta è motivata da considerazioni di programmazione matematica ed è correlata ad altre formulazioni presenti in letteratura. Analizziamo il problema più nel dettaglio.
Sia il numero di qubit e . Data una struttura CNOT e un vettore di angoli di rotazione , il circuito parametrico forma una matrice . Se ci viene dato un circuito di destinazione che forma una matrice , allora vorremmo calcolare
dove il prodotto interno è il prodotto interno di Frobenius. Si noti che per tutti gli unitari e , quindi l'obiettivo ha campo in .
La nostra strategia consiste nel massimizzare
utilizzando il suo gradiente. Ne discuteremo ora le caratteristiche specifiche con un esempio.
Mentre l'intervallo di è un sottoinsieme di per costruzione, il circuito di destinazione può formare una matrice unitaria generale. Tuttavia, per qualsiasi ,
Pertanto, dovremmo normalizzare il circuito di destinazione in base alla sua fase globale e poi compilare approssimativamente il circuito normalizzato. Possiamo aggiungere la fase globale in seguito.
Nell'algoritmo, indica la matrice target non normalizzata e la matrice target normalizzata. Ora che abbiamo , diamo la funzione gradiente all'ottimizzatore del metodo di Nesterov e calcoliamo .
Per aggiungere nuovamente la fase globale, possiamo formare il circuito di controllo come segue
Si noti che nel documento abbiamo ottimizzato utilizzando il metodo di Nesterov, ma per le sue garanzie di convergenza e non per la sua velocità nella pratica. È molto più veloce utilizzare L-BFGS, che viene utilizzato come ottimizzatore predefinito in questa implementazione.
Un utilizzo di base dell'algoritmo AQC dovrebbe consistere nei seguenti passaggi:
# Define a target circuit as a unitary matrix
unitary = ...
# Define a number of qubits for the algorithm, at least 3 qubits
num_qubits = round(math.log2(unitary.shape[0]))
# Choose a layout of the CNOT structure for the approximate circuit, e.g. ``spin`` for
# a linear layout.
layout = options.get("layout") or "spin"
# Choose a connectivity type, e.g. ``full`` for full connectivity between qubits.
connectivity = options.get("connectivity") or "full"
# Define a targeted depth of the approximate circuit in the number of CNOT units.
depth = int(options.get("depth") or 0)
# Generate a network made of CNOT units
cnots = make_cnot_network(
num_qubits=num_qubits,
network_layout=layout,
connectivity_type=connectivity,
depth=depth
)
# Create an optimizer to be used by AQC
optimizer = partial(scipy.optimize.minimize, method="L-BFGS-B")
# Create an instance
aqc = AQC(optimizer)
# Create a template circuit that will approximate our target circuit
approximate_circuit = CNOTUnitCircuit(num_qubits=num_qubits, cnots=cnots)
# Create an objective that defines our optimization problem
approximating_objective = DefaultCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots)
# Run optimization process to compile the unitary
aqc.compile_unitary(
target_matrix=unitary,
approximate_circuit=approximate_circuit,
approximating_objective=approximating_objective
)Ora approximate_circuit è un circuito che approssima l'unitario target a un certo grado e può essere utilizzato al posto della matrice originale.
Utilizza una funzione ausiliaria, make_cnot_network.
make_cnot_network
qiskit.synthesis.unitary.aqc.make_cnot_network(num_qubits, network_layout='spin', connectivity_type='full', depth=0)
Genera una rete composta da blocchi di edifici contenenti ciascuno una porta CNOT ed eventualmente alcune porte a singolo qubit. Questa rete modella un operatore quantistico in questione. Si noti che ogni blocco costruttivo ha 2 ingressi e uscite corrispondenti a una coppia di qubit. Ciò che in realtà restituiamo è una catena di indici di coppie di qubit condivisi da ogni blocco di edifici in una fila.
Parametri
- num_qubits (int) – numero di qubit.
- network_layout (str) – tipo di geometria della rete,
{"sequ", "spin", "cart", "cyclic_spin", "cyclic_line"}. - connectivity_type (str) – tipo di connettività inter-qubit,
{"full", "line", "star"}. - depth (int) – profondità della rete CNOT, cioè il numero di strati, dove ogni strato è costituito da un singolo blocco CNOT; il valore predefinito viene selezionato se
L <= 0.
Risultati
Una matrice di dimensioni (2, N) che definisce gli strati in cnot-network, dove N
è uguale a L, oppure è definito da un tipo concreto di rete.
Aumenti
ValueError - se viene passato un tipo di layout di rete CNOT non supportato o un numero di qubit o una combinazione di parametri non supportati.
Tipo di restituzione
Si può sfruttare una versione accelerata della funzione obiettivo. Implementa lo stesso algoritmo matematico di quello predefinito DefaultCNOTUnitObjective , ma viene eseguito più velocemente. L'istanziazione della classe di funzioni obiettivo accelerate è simile al caso predefinito:
# Creare un obiettivo che definisca il nostro problema di ottimizzazione approximating_objective = FastCNOTUnitObjective(num_qubits=num_qubits, cnots=cnots)
Il resto del codice dell'esempio precedente non cambia.
Riferimenti
[1] : Liam Madden, Andrea Simonetto, Problemi di compilazione quantistica di miglior approssimazione.