Skip to main content
IBM Quantum Platform

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:

  1. Creare una sottoclasse della classe astratta del plugin appropriata:
  2. Esporre la classe come punto di ingresso di setuptools nei metadati del pacchetto, in genere modificando i file pyproject.toml, setup.cfg, o setup.py per 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
Cliffordclifforddefault, ag, bm, greedy, layers, lnn
LinearFunctionlinear_functiondefault, kms, pmh
PermutationGatepermutationdefault, 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_pm

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 (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"

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_circuit

Se 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.

Nota

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"

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:

  • target specifica 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ì via
  • coupling_map specifica solo la mappa di accoppiamento e viene utilizzato solo quando target non è specificato.
  • qubits specifica 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 di None indica 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 None

Questo 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"

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'attributo name della 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 .

Suggerimento
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.