Crea un plugin 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
La creazione di un plugin per il transpiler è un ottimo modo per condividere il codice di transpilazione con la comunità di Qiskit, consentendo ad altri utenti di beneficiare delle funzionalità sviluppate. Grazie per il vostro interesse a contribuire alla comunità di Qiskit!
Prima di creare un plugin per il transpiler, è necessario decidere quale tipo di plugin è adatto alla propria situazione. Esistono tre tipi di plugin di transpiler:
- Plugin per palcoscenico Transpiler. Scegliere questa opzione se si sta definendo un gestore di passaggi che può essere sostituito da uno dei 6 stadi di un gestore di passaggi preimpostato.
- Plugin di sintesi unitaria. Scegliere questa opzione se il codice di transpilazione prende in input una matrice unitaria (rappresentata come array Numpy) e produce una descrizione di un circuito quantistico che implementa tale matrice unitaria.
- Plugin di sintesi ad alto livello. Scegliere questa opzione se il codice di transpilazione prende in input un "oggetto di alto livello", come un operatore di Clifford o una funzione lineare, e restituisce una descrizione di un circuito quantistico che implementa quell'oggetto di alto livello. Gli oggetti di alto livello sono rappresentati da sottoclassi della classe Operation.
Una volta stabilito quale tipo di plugin creare, seguire i seguenti passaggi per creare il plugin:
- Creare una sottoclasse della classe astratta del plugin appropriata:
- PassManagerStagePlugin per un plugin dello stadio transpiler,
- UnitarySynthesisPlugin per un plugin di sintesi unitario e
- HighLevelSynthesisPlugin per un plugin di sintesi di alto livello.
- Esporre la classe come punto di ingresso di setuptools nei metadati del pacchetto, in genere modificando i file
pyproject.toml,setup.cfg, osetup.pyper il pacchetto Python.
Non c'è limite al numero di plugin che un singolo pacchetto può definire, ma ogni plugin deve avere un nome unico. Lo stesso SDK di Qiskit include una serie di plugin, i cui nomi sono anch'essi riservati. I nomi riservati sono:
- Plugin dello stage Transpiler: Vedere questa tabella.
- Plugin di sintesi unitaria:
default,aqc,sk - Plugin di sintesi ad alto livello:
Classe di funzionamento | Nome operazione | Nomi riservati |
|---|---|---|
| Clifford | clifford | default, ag, bm, greedy, layers, lnn |
| LinearFunction | linear_function | default, kms, pmh |
| PermutationGate | permutation | default, kms, basic, acg, token_swapper |
Nelle prossime sezioni verranno mostrati esempi di questi passaggi per i diversi tipi di plugin. In questi esempi, si ipotizza di creare un pacchetto Python chiamato my_qiskit_plugin. Per informazioni sulla creazione di pacchetti Python, potete consultare questo tutorial dal sito web Python.
Esempio: Creare un plugin per la fase di transpilazione
In questo esempio, creiamo un plugin per lo stage del transpiler per lo stage layout (vedere Stadi del transpiler per una descrizione dei 6 stadi della pipeline di transpilazione integrata di Qiskit).
Il nostro plugin esegue semplicemente VF2Layout per un numero di prove che dipende dal livello di ottimizzazione richiesto.
Per prima cosa, creiamo una sottoclasse di PassManagerStagePlugin. C'è un metodo che dobbiamo implementare, chiamato pass_manager. Questo metodo prende in input un oggetto PassManagerConfig e restituisce il gestore di passaggi che stiamo definendo. L'oggetto PassManagerConfig memorizza informazioni sul backend di destinazione, come la sua mappa di accoppiamento e le porte di base.
# This import is needed for python versions prior to 3.10
from __future__ import annotations
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import VF2Layout
from qiskit.transpiler.passmanager_config import PassManagerConfig
from qiskit.transpiler.preset_passmanagers import common
from qiskit.transpiler.preset_passmanagers.plugin import (
PassManagerStagePlugin,
)
class MyLayoutPlugin(PassManagerStagePlugin):
def pass_manager(
self,
pass_manager_config: PassManagerConfig,
optimization_level: int | None = None,
) -> PassManager:
layout_pm = PassManager(
[
VF2Layout(
coupling_map=pass_manager_config.coupling_map,
properties=pass_manager_config.backend_properties,
max_trials=optimization_level * 10 + 1,
target=pass_manager_config.target,
)
]
)
layout_pm += common.generate_embed_passmanager(
pass_manager_config.coupling_map
)
return layout_pmOra, esponiamo il plugin aggiungendo un punto di ingresso nei metadati del nostro pacchetto Python.
In questo caso, si assume che la classe definita sia esposta in un modulo chiamato my_qiskit_plugin, ad esempio importata nel file __init__.py del modulo my_qiskit_plugin .
Modifichiamo il file pyproject.toml, setup.cfg o setup.py del nostro pacchetto (a seconda del tipo di file scelto per memorizzare i metadati del progetto Python ):
[project.entry-points."qiskit.transpiler.layout"]
"my_layout" = "my_qiskit_plugin:MyLayoutPlugin"[options.entry_points]
qiskit.transpiler.layout =
my_layout = my_qiskit_plugin:MyLayoutPluginfrom setuptools import setup
setup(
# ...,
entry_points={
'qiskit.transpiler.layout': [
'my_layout = my_qiskit_plugin:MyLayoutPlugin',
]
}
)Consultare la tabella delle fasi del plugin transpiler per i punti di ingresso e le aspettative per ciascuna fase del transpiler.
Per verificare che il vostro plugin sia stato rilevato con successo da Qiskit, installate il vostro pacchetto di plugin e seguite le istruzioni riportate in Transpiler plugins per l'elenco dei plugin installati e assicuratevi che il vostro plugin appaia nell'elenco:
from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins
list_stage_plugins("layout")Output:
['default', 'dense', 'sabre', 'trivial']
Se il nostro plugin di esempio fosse installato, in questo elenco comparirebbe il nome my_layout .
Se si vuole usare uno stadio transpiler incorporato come punto di partenza per il proprio plugin di stadio transpiler, si può ottenere il gestore di pass per uno stadio transpiler incorporato usando PassManagerStagePluginManager. La seguente cella di codice mostra come eseguire questa operazione per ottenere lo stadio di ottimizzazione incorporato per il livello di ottimizzazione 3.
from qiskit.transpiler.preset_passmanagers.plugin import (
PassManagerStagePluginManager,
)
# Initialize the plugin manager
plugin_manager = PassManagerStagePluginManager()
# Here we create a pass manager config to use as an example.
# Instead, you should use the pass manager config that you already received as input
# to the pass_manager method of your PassManagerStagePlugin.
pass_manager_config = PassManagerConfig()
# Obtain the desired built-in transpiler stage
optimization = plugin_manager.get_passmanager_stage(
"optimization", "default", pass_manager_config, optimization_level=3
)Esempio: Creare un plugin di sintesi unitario
In questo esempio, creeremo un plugin di sintesi unitaria che utilizza semplicemente il passaggio di transpilazione integrato per sintetizzare un gate UnitarySynthesis per sintetizzare un gate. Naturalmente, il vostro plugin farà qualcosa di più interessante di questo.
La classe UnitarySynthesisPlugin definisce l'interfaccia e il contratto per i plugin di sintesi unitaria plugin. Il metodo principale è run, che prende in input un array Numpy che memorizza una matrice unitaria e restituisce un DAGCircuit che rappresenta il circuito sintetizzato da quella matrice unitaria.
Oltre al metodo run , è necessario definire una serie di metodi di proprietà.
Vedere UnitarySynthesisPlugin per la documentazione di tutte le proprietà richieste.
Creiamo la nostra sottoclasse UnitarySynthesisPlugin :
import numpy as np
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.converters import circuit_to_dag
from qiskit.dagcircuit.dagcircuit import DAGCircuit
from qiskit.quantum_info import Operator
from qiskit.transpiler.passes import UnitarySynthesis
from qiskit.transpiler.passes.synthesis.plugin import UnitarySynthesisPlugin
class MyUnitarySynthesisPlugin(UnitarySynthesisPlugin):
@property
def supports_basis_gates(self):
# Returns True if the plugin can target a list of basis gates
return True
@property
def supports_coupling_map(self):
# Returns True if the plugin can synthesize for a given coupling map
return False
@property
def supports_natural_direction(self):
# Returns True if the plugin supports a toggle for considering
# directionality of 2-qubit gates
return False
@property
def supports_pulse_optimize(self):
# Returns True if the plugin can optimize pulses during synthesis
return False
@property
def supports_gate_lengths(self):
# Returns True if the plugin can accept information about gate lengths
return False
@property
def supports_gate_errors(self):
# Returns True if the plugin can accept information about gate errors
return False
@property
def supports_gate_lengths_by_qubit(self):
# Returns True if the plugin can accept information about gate lengths
# (The format of the input differs from supports_gate_lengths)
return False
@property
def supports_gate_errors_by_qubit(self):
# Returns True if the plugin can accept information about gate errors
# (The format of the input differs from supports_gate_errors)
return False
@property
def min_qubits(self):
# Returns the minimum number of qubits the plugin supports
return None
@property
def max_qubits(self):
# Returns the maximum number of qubits the plugin supports
return None
@property
def supported_bases(self):
# Returns a dictionary of supported bases for synthesis
return None
def run(self, unitary: np.ndarray, **options) -> DAGCircuit:
basis_gates = options["basis_gates"]
synth_pass = UnitarySynthesis(basis_gates, min_qubits=3)
qubits = QuantumRegister(3)
circuit = QuantumCircuit(qubits)
circuit.append(Operator(unitary).to_instruction(), qubits)
dag_circuit = synth_pass.run(circuit_to_dag(circuit))
return dag_circuitSe si scopre che gli ingressi disponibili per il sistema run sono insufficienti per i vostri scopi, aprite un problema spiegando le vostre esigenze. Le modifiche all'interfaccia del plugin, come l'aggiunta di ulteriori ingressi opzionali, saranno effettuate in modo retrocompatibile, in modo da non richiedere modifiche ai plugin esistenti.
Tutti i metodi con prefisso supports_ sono riservati a una classe derivata da UnitarySynthesisPlugin come parte dell'interfaccia. Non si devono definire metodi supports_* personalizzati su una sottoclasse che non siano definiti nella classe astratta.
Ora, esponiamo il plugin aggiungendo un punto di ingresso nei metadati del nostro pacchetto Python.
In questo caso, si assume che la classe definita sia esposta in un modulo chiamato my_qiskit_plugin, ad esempio importata nel file __init__.py del modulo my_qiskit_plugin .
Modifichiamo il file pyproject.toml, setup.cfg o setup.py del nostro pacchetto:
[project.entry-points."qiskit.unitary_synthesis"]
"my_unitary_synthesis" = "my_qiskit_plugin:MyUnitarySynthesisPlugin"[options.entry_points]
qiskit.unitary_synthesis =
my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPluginfrom setuptools import setup
setup(
# ...,
entry_points={
'qiskit.unitary_synthesis': [
'my_unitary_synthesis = my_qiskit_plugin:MyUnitarySynthesisPlugin',
]
}
)Come prima, se il tuo progetto utilizza setup.cfg O setup.py invece di pyproject.toml, consulta la documentazione di setuptools per sapere come adattare queste righe alla tua situazione.
Per verificare che il vostro plugin sia stato rilevato con successo da Qiskit, installate il vostro pacchetto di plugin e seguite le istruzioni riportate in Transpiler plugins per l'elenco dei plugin installati e assicuratevi che il vostro plugin appaia nell'elenco:
from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names
unitary_synthesis_plugin_names()Output:
['aqc', 'clifford', 'default', 'gridsynth', 'sk']
Se il nostro plugin di esempio fosse installato, in questo elenco comparirebbe il nome my_unitary_synthesis .
Per accogliere i plugin di sintesi unitaria che espongono più opzioni, l'interfaccia del plugin ha un'opzione che consente agli utenti di fornire un dizionario di configurazione dizionario di configurazione. Questo verrà passato al metodo run tramite l'argomento della parola chiave options . Se il vostro plugin ha queste opzioni di configurazione, dovreste documentarle chiaramente.
Esempio: Creare un plugin di sintesi di alto livello
In questo esempio, creeremo un plugin di sintesi di alto livello che utilizza semplicemente la funzione integrata synth_clifford_bm per sintetizzare un operatore Clifford.
La classe HighLevelSynthesisPlugin definisce l'interfaccia e il contratto per i plugin di sintesi di alto livello. Il metodo primario è run.
L'argomento posizionale high_level_object è un' operazione che rappresenta l'oggetto di "alto livello" da sintetizzare. Ad esempio, potrebbe essere un LinearFunction o un Clifford.
Sono presenti i seguenti argomenti di parole chiave:
targetspecifica il backend di destinazione, consentendo al plugin di accedere a tutte le informazioni specifiche del target, come la mappa di accoppiamento, l'insieme dei gate supportati e così viacoupling_mapspecifica solo la mappa di accoppiamento e viene utilizzato solo quandotargetnon è specificato.qubitsspecifica l'elenco dei qubit su cui viene definito l'oggetto di alto livello, nel caso in cui la sintesi venga effettuata sul circuito fisico di alto livello, nel caso in cui la sintesi venga effettuata sul circuito fisico. Un valore diNoneindica che il layout non è ancora stato scelto e che i qubit fisici nella mappa di destinazione o di accoppiamento su cui opera questa operazione non sono ancora stati determinati.options, un dizionario di configurazione a forma libera per le opzioni specifiche del plugin. Se il plugin ha queste opzioni di configurazione dovrebbe documentarle chiaramente.
Il metodo run restituisce un oggetto QuantumCircuit che rappresenta il circuito sintetizzato da quell'oggetto di alto livello.
Può anche restituire None, indicando che il plugin non è in grado di sintetizzare l'oggetto di alto livello indicato.
La sintesi vera e propria degli oggetti di alto livello viene eseguita dal programma HighLevelSynthesis transpiler.
Oltre al metodo run , è necessario definire una serie di metodi di proprietà.
Vedere HighLevelSynthesisPlugin per la documentazione di tutte le proprietà richieste.
Definiamo la nostra sottoclasse HighLevelSynthesisPlugin :
from qiskit.synthesis import synth_clifford_bm
from qiskit.transpiler.passes.synthesis.plugin import HighLevelSynthesisPlugin
class MyCliffordSynthesisPlugin(HighLevelSynthesisPlugin):
def run(
self,
high_level_object,
coupling_map=None,
target=None,
qubits=None,
**options,
) -> QuantumCircuit:
if high_level_object.num_qubits <= 3:
return synth_clifford_bm(high_level_object)
else:
return NoneQuesto plugin sintetizza oggetti di tipo Clifford che hanno al massimo 3 qubit, utilizzando il metodo synth_clifford_bm .
Ora, esponiamo il plugin aggiungendo un punto di ingresso nei metadati del nostro pacchetto Python.
In questo caso, si assume che la classe definita sia esposta in un modulo chiamato my_qiskit_plugin, ad esempio importata nel file __init__.py del modulo my_qiskit_plugin .
Modifichiamo il file pyproject.toml, setup.cfg o setup.py del nostro pacchetto:
[project.entry-points."qiskit.synthesis"]
"clifford.my_clifford_synthesis" = "my_qiskit_plugin:MyCliffordSynthesisPlugin"[options.entry_points]
qiskit.synthesis =
clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPluginfrom setuptools import setup
setup(
# ...,
entry_points={
'qiskit.synthesis': [
'clifford.my_clifford_synthesis = my_qiskit_plugin:MyCliffordSynthesisPlugin',
]
}
)name è composto da due parti separate da un punto (.):
- Il nome del tipo di operazione che il plugin sintetizza (in questo caso,
clifford). Si noti che questa stringa corrisponde all'attributonamedella classe Operation e non al nome della classe stessa. - Il nome del plugin (in questo caso,
special).
Come prima, se il tuo progetto utilizza setup.cfg O setup.py invece di pyproject.toml, consulta la documentazione di setuptools per sapere come adattare queste righe alla tua situazione.
Per verificare che il vostro plugin sia stato rilevato con successo da Qiskit, installate il vostro pacchetto di plugin e seguite le istruzioni riportate in Transpiler plugins per l'elenco dei plugin installati e assicuratevi che il vostro plugin appaia nell'elenco:
from qiskit.transpiler.passes.synthesis import (
high_level_synthesis_plugin_names,
)
high_level_synthesis_plugin_names("clifford")Output:
['ag', 'bm', 'default', 'greedy', 'layers', 'lnn', 'rb_default']
Se il nostro plugin di esempio fosse installato, in questo elenco comparirebbe il nome my_clifford_synthesis .
- Invia il tuo plugin all'ecosistema Qiskit!
- Consultate le esercitazioni per trovare esempi di transpiling e di esecuzione di circuiti quantistici.