Fasi del transpiler
Il codice di questa pagina è stato sviluppato in base ai seguenti requisiti. Si consiglia di utilizzare queste versioni o versioni più recenti.
qiskit[all]~=2.5.1
Questa pagina descrive le fasi della pipeline di transpilazione precostituita nell'SDK Qiskit. Le fasi sono sei:
initlayoutroutingtranslationoptimizationscheduling
La funzione generate_preset_pass_manager crea un gestore di passaggi preimpostato composto da questi stadi. I passaggi specifici che compongono ogni fase dipendono dagli argomenti passati a generate_preset_pass_manager. Il parametro optimization_level è un argomento posizionale che deve essere specificato; è un numero intero che può essere 0, 1, 2 o 3. Valori più alti indicano un'ottimizzazione più pesante ma più costosa (vedere Valori predefiniti e opzioni di configurazione della transpilazione ).
Il modo consigliato per transpilare un circuito è quello di creare un gestore di passaggi preimpostato e poi eseguirlo sul circuito, come descritto in Transpilare con i gestori di passaggi. Tuttavia, un'alternativa più semplice ma meno personalizzabile è quella di utilizzare la funzione transpile funzione. Questa funzione accetta il circuito direttamente come argomento. Come per generate_preset_pass_manager, gli specifici passaggi di transpiler utilizzati dipendono dagli argomenti, come optimization_level, passati a transpile. Infatti, internamente la funzione transpile chiama generate_preset_pass_manager per creare un gestore di passaggi preimpostato ed eseguirlo sul circuito.
Fase iniziale
Questo primo stadio fa ben poco per impostazione predefinita ed è utile soprattutto se si vogliono includere le proprie ottimizzazioni iniziali. Poiché la maggior parte degli algoritmi di layout e routing sono progettati per funzionare solo con porte a uno o due qubit, questa fase viene utilizzata anche per tradurre le porte che operano su più di due qubit in porte che operano solo su uno o due qubit.
Per ulteriori informazioni sull'implementazione delle ottimizzazioni iniziali per questa fase, vedere la sezione sui plugin e sulla personalizzazione dei gestori di passaggi.
Fase di layout
La fase successiva riguarda la configurazione o la connettività del backend a cui verrà inviato il circuito. In generale, i circuiti quantistici sono entità astratte i cui qubit sono rappresentazioni "virtuali" o "logiche" dei qubit reali utilizzati nei calcoli. Per eseguire una sequenza di porte, è necessaria una corrispondenza biunivoca tra i qubit "virtuali" e quelli "fisici" in un dispositivo quantistico reale. Questa mappatura è memorizzata come oggetto Layout e fa parte dei vincoli definiti nell'architettura del set di istruzioni (ISA) di un backend.
La scelta della mappatura è estremamente importante per ridurre al minimo il numero di operazioni SWAP necessarie per mappare il circuito di ingresso sulla topologia del dispositivo e garantire l'utilizzo dei qubit più ben calibrati. Data l'importanza di questa fase, i gestori dei passaggi preimpostati provano alcuni metodi diversi per trovare il layout migliore. In genere si tratta di due fasi: prima si cerca di trovare un layout "perfetto" (un layout che non richieda alcuna operazione di SWAP) e poi si effettua un passaggio euristico che cerca di trovare il layout migliore da utilizzare se non è possibile trovare un layout perfetto. Per questa prima fase si utilizzano in genere due Passes :
TrivialLayout: Mappa ingenuamente ogni qubit virtuale sullo stesso qubit fisico numerato sul dispositivo (ad esempio, [0,1,1,3] -> [0,1,1,3] ). Questo è un comportamento storico utilizzato solo inoptimzation_level=1per cercare di trovare un layout perfetto. Se non riesce, il tentativo successivo èVF2Layout.VF2Layout: Si tratta di unAnalysisPassche seleziona un layout ideale trattando questa fase come un problema di isomorfismo dei sottografi, risolto dall'algoritmo VF2++. Se viene trovato più di un layout, viene eseguita un'euristica di punteggio per selezionare la mappatura con l'errore medio più basso.
Poi, per la fase euristica, vengono utilizzati due passaggi per impostazione predefinita:
DenseLayout: Trova il sottografo del dispositivo con la maggiore connettività e che ha lo stesso numero di qubit del circuito (usato per il livello di ottimizzazione 1 se nel circuito sono presenti operazioni di flusso di controllo (come IfElseOp )).SabreLayout: Questo passaggio seleziona un layout partendo da un layout casuale iniziale ed eseguendo ripetutamenteSabreSwapl'algoritmo. Questo passaggio viene utilizzato solo nei livelli di ottimizzazione 1, 2 e 3 se non viene trovato un layout perfetto tramite ilVF2Layoutpassaggio. Per ulteriori dettagli su questo algoritmo, consultare il documento " arXiv:1809.02573 " (Analisi delle prestazioni di un algoritmo di ottimizzazione basato su una rete neurale artificiale).
Fase di instradamento
Per implementare un gate a due qubit tra qubit non direttamente connessi su un dispositivo quantistico, è necessario inserire nel circuito una o più porte SWAP per spostare gli stati dei qubit fino a renderli adiacenti sulla mappa dei gate del dispositivo. Ogni porta SWAP rappresenta un'operazione costosa e rumorosa da eseguire. Pertanto, trovare il numero minimo di porte SWAP necessarie per mappare un circuito su un determinato dispositivo è un passo importante nel processo di transpilazione. Per motivi di efficienza, questa fase viene in genere calcolata insieme alla fase di layout per impostazione predefinita, ma sono logicamente distinte l'una dall'altra. Lo stadio Layout seleziona i qubit hardware da utilizzare, mentre lo stadio Routing inserisce la quantità appropriata di porte SWAP per eseguire i circuiti utilizzando il layout selezionato.
Tuttavia, trovare la mappatura SWAP ottimale è difficile. In effetti, si tratta di un problema NP-hard, quindi proibitivamente costoso da calcolare per tutti i dispositivi quantistici e i circuiti di input, tranne quelli più piccoli. Per ovviare a questo problema, Qiskit utilizza un algoritmo euristico stocastico chiamato SabreSwap per calcolare una mappatura SWAP buona, ma non necessariamente ottimale. L'uso di un metodo stocastico significa che i circuiti generati non sono garantiti come uguali in caso di esecuzioni ripetute. Infatti, eseguendo ripetutamente lo stesso circuito, si ottiene una distribuzione delle profondità del circuito e dei numeri di porte in uscita. Per questo motivo molti utenti scelgono di eseguire più volte la funzione di instradamento (o l'intero StagedPassManager) e di selezionare i circuiti a più bassa profondità dalla distribuzione delle uscite.
Ad esempio, prendiamo un circuito GHZ a 15 qubit eseguito 100 volte, utilizzando un "cattivo" (disconnesso) initial_layout.
import matplotlib.pyplot as plt
from qiskit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.providers.fake_provider import GenericBackendV2
backend = GenericBackendV2(15)
ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))
depths = []
for seed in range(100):
pass_manager = generate_preset_pass_manager(
optimization_level=1,
backend=backend,
layout_method="trivial", # Fixed layout mapped in circuit order
seed_transpiler=seed, # For reproducible results
)
depths.append(pass_manager.run(ghz).depth())
plt.figure(figsize=(8, 6))
plt.hist(depths, align="left", color="#AC557C")
plt.xlabel("Depth", fontsize=14)
plt.ylabel("Counts", fontsize=14)Output:
Text(0, 0.5, 'Counts')
Questa ampia distribuzione dimostra quanto sia difficile per il mappatore SWAP calcolare la mappatura migliore. Per capire meglio, analizziamo sia il circuito in esecuzione sia i qubit scelti nell'hardware.
ghz.draw("mpl", idle_wires=False)Output:
from qiskit.visualization import plot_circuit_layout
# Plot the hardware graph and indicate which hardware qubits were chosen to run the circuit
transpiled_circ = pass_manager.run(ghz)
plot_circuit_layout(transpiled_circ, backend)Output:
Come si può vedere, questo circuito deve eseguire un gate a due qubit tra i qubit 0 e 14, che sono molto distanti tra loro nel grafo di connettività. L'esecuzione di questo circuito richiede quindi l'inserimento di porte SWAP per eseguire tutte le porte a due qubit utilizzando il passaggio SabreSwap .
Si noti inoltre che l'algoritmo SabreSwap è diverso dal metodo SabreLayout più ampio della fase precedente. Per impostazione predefinita, SabreLayout esegue sia il layout che il routing e restituisce il circuito trasformato. Questo viene fatto per alcune particolari ragioni tecniche specificate nella pagina di riferimento dell'API del pass.
Fase di traduzione
Quando si scrive un circuito quantistico, si è liberi di utilizzare qualsiasi porta quantistica (operazione unitaria) si desideri, insieme a una serie di operazioni non legate alle porte, come la misurazione dei qubit o le istruzioni di reset. Tuttavia, la maggior parte dei dispositivi quantistici supporta nativamente solo una manciata di operazioni quantistiche, sia che si tratti di porte quantistiche che di operazioni diverse dalle porte. Questi gate nativi fanno parte della definizione dell'ISA di un target e questa fase del preset PassManagers traduce (o srotola ) i gate specificati in un circuito nei gate nativi di un backend specificato. Si tratta di un passo importante, poiché consente l'esecuzione del circuito da parte del backend, ma comporta solitamente un aumento della profondità e del numero di porte logiche.
Due casi speciali sono particolarmente importanti da sottolineare e aiutano a illustrare le funzioni di questa fase.
- Se un gate SWAP non è un gate nativo del backend di destinazione, sono necessari tre gate CNOT:
print("native gates:" + str(sorted(backend.operation_names)))
qc = QuantumCircuit(2)
qc.swap(0, 1)
qc.decompose().draw("mpl")Output:
native gates:['cx', 'delay', 'id', 'measure', 'reset', 'rz', 'sx', 'x']
Essendo il prodotto di tre porte CNOT, uno SWAP è un'operazione costosa da eseguire su dispositivi quantistici rumorosi. Tuttavia, tali operazioni sono solitamente necessarie per incorporare un circuito nelle limitate connettività dei gate di molti dispositivi. Pertanto, la minimizzazione del numero di porte SWAP in un circuito è un obiettivo primario del processo di transpilazione.
- Una porta Toffoli, o porta controllata-non controllata (
ccx), è una porta a tre qubit. Dato che il nostro set di porte di base comprende solo porte a uno o due qubit, questa operazione deve essere scomposta. Tuttavia, è piuttosto costoso:
qc = QuantumCircuit(3)
qc.ccx(0, 1, 2)
qc.decompose().draw("mpl")Output:
Per ogni porta Toffoli in un circuito quantistico, l'hardware può eseguire fino a sei porte CNOT e una manciata di porte a singolo qubit. Questo esempio dimostra che qualsiasi algoritmo che faccia uso di più porte di Toffoli finirà per essere un circuito con una grande profondità e sarà quindi sensibilmente influenzato dal rumore.
Fase di ottimizzazione
Questa fase è incentrata sulla scomposizione dei circuiti quantistici nel set di porte di base del dispositivo di destinazione e deve contrastare l'aumento della profondità dovuto alle fasi di layout e routing. Fortunatamente, esistono molte routine per ottimizzare i circuiti combinando o eliminando le porte. In alcuni casi, questi metodi sono così efficaci che i circuiti di uscita hanno una profondità inferiore a quella degli ingressi, anche dopo il layout e l'instradamento nella topologia hardware. In altri casi, non si può fare molto e il calcolo può essere difficile da eseguire su dispositivi rumorosi. In questa fase i vari livelli di ottimizzazione iniziano a differenziarsi.
- Per
optimization_level=1, questa fase preparaOptimize1qGatesDecompositioneCXCancellationche combinano catene di porte a singolo qubit e annullano qualsiasi porta CNOT retrostante. - Per
optimization_level=2, questo stadio utilizza ilCommutativeCancellationinvece diCXCancellation, che elimina le porte ridondanti sfruttando le relazioni di commutazione. - Per
optimization_level=3, questa fase prepara i seguenti passaggi:
Inoltre, questa fase esegue anche alcuni controlli finali per assicurarsi che tutte le istruzioni del circuito siano composte dalle porte di base disponibili sul backend di destinazione.
L'esempio seguente, che utilizza uno stato GHZ, mostra gli effetti di diverse impostazioni del livello di ottimizzazione sulla profondità del circuito e sul numero di gate.
L'output della transpilazione varia a causa del mappatore stocastico SWAP. Pertanto, è probabile che i numeri riportati di seguito cambino ogni volta che si esegue il codice.

Il codice seguente costruisce uno stato GHZ a 15-qubit e confronta il optimization_levels della transpilazione in termini di profondità del circuito risultante, numero di porte e numero di porte multi-qubit.
ghz = QuantumCircuit(15)
ghz.h(0)
ghz.cx(0, range(1, 15))
depths = []
gate_counts = []
multiqubit_gate_counts = []
levels = [str(x) for x in range(4)]
for level in range(4):
pass_manager = generate_preset_pass_manager(
optimization_level=level,
backend=backend,
seed_transpiler=1234,
)
circ = pass_manager.run(ghz)
depths.append(circ.depth())
gate_counts.append(sum(circ.count_ops().values()))
multiqubit_gate_counts.append(circ.count_ops()["cx"])
fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.bar(levels, depths, label="Depth")
ax1.set_xlabel("Optimization Level")
ax1.set_ylabel("Depth")
ax1.set_title("Output Circuit Depth")
ax2.bar(levels, gate_counts, label="Number of Circuit Operations")
ax2.bar(levels, multiqubit_gate_counts, label="Number of CX gates")
ax2.set_xlabel("Optimization Level")
ax2.set_ylabel("Number of gates")
ax2.legend()
ax2.set_title("Number of output circuit gates")
fig.tight_layout()
plt.show()Output:
Pianificazione
Quest'ultimo stadio viene eseguito solo se viene richiesto esplicitamente (come lo stadio Init) e non viene eseguito per impostazione predefinita (anche se è possibile specificare un metodo impostando l'argomento scheduling_method quando si chiama generate_preset_pass_manager). La fase di schedulazione viene utilizzata in genere una volta che il circuito è stato tradotto nella base di destinazione, mappato sul dispositivo e ottimizzato. Questi passaggi si concentrano sulla considerazione di tutto il tempo di inattività di un circuito. Ad alto livello, il passaggio di schedulazione può essere pensato come l'inserimento esplicito di istruzioni di ritardo per tenere conto del tempo di inattività tra le esecuzioni dei gate e per controllare quanto tempo il circuito rimarrà in esecuzione sul backend.
Di seguito è riportato un esempio:
ghz = QuantumCircuit(5)
ghz.h(0)
ghz.cx(0, range(1, 5))
# Use fake backend
backend = GenericBackendV2(5)
# Run with optimization level 3 and 'asap' scheduling pass
pass_manager = generate_preset_pass_manager(
optimization_level=3,
backend=backend,
scheduling_method="asap",
seed_transpiler=1234,
)
circ = pass_manager.run(ghz)
circ.draw(output="mpl", idle_wires=False)Output:
Il transpiler ha inserito le istruzioni Delay per tenere conto del tempo di inattività di ciascun qubit. Per avere un'idea più precisa della tempistica del circuito, possiamo anche osservarlo con la funzione timeline.draw() :
La programmazione di un circuito prevede due parti: l'analisi e la mappatura dei vincoli, seguite da un passaggio di imbottitura. La prima parte richiede l'esecuzione di un passaggio di analisi dello scheduling (per impostazione predefinita questo è ALAPSchedulingAnalysis), che analizza il circuito e registra l'ora di inizio di ogni istruzione del circuito in una schedulazione. Una volta che il circuito ha una programmazione iniziale, è possibile eseguire ulteriori passaggi per tenere conto di eventuali vincoli temporali sul backend di destinazione. Infine, un passaggio di imbottitura come PadDelay o PadDynamicalDecoupling può essere eseguito.
Passi successivi
- Per imparare a usare la funzione
generate_preset_passmanager, iniziare con l'argomento Impostazioni predefinite e opzioni di configurazione della transpilazione. - Continua ad apprendere sulla transpilazione con l'argomento Transpiler con pass manager.
- Prova la guida Confronta le impostazioni del transpiler.
- Consulta la documentazione dell'API Transpile.