Criar um plugin transpiler
O código desta página foi desenvolvido usando os seguintes requisitos. Recomendamos o uso dessas versões ou de versões mais recentes.
qiskit[all]~=2.5.0
Criar um plug-in de trans pilação é uma ótima maneira de compartilhar seu código de transpilação com a comunidade Qiskit mais ampla, permitindo que outros usuários se beneficiem da funcionalidade que você desenvolveu. Obrigado por seu interesse em contribuir com a comunidade Qiskit!
Antes de criar um plug-in de transpilador, você precisa decidir que tipo de plug-in é apropriado para a sua situação. Há três tipos de plug-ins de transpiladores:
- Plug-in de estágio Transpiler. Escolha essa opção se estiver definindo um gerenciador de passagens que possa ser substituído por um dos 6 estágios de um gerenciador de passagens predefinido.
- Plug-in de síntese unitária. Escolha essa opção se o seu código de transpilação receber como entrada uma matriz unitária (representada como uma matriz Numpy) e gerar uma descrição de um circuito quântico que implemente essa unitária.
- Plug-in de síntese de alto nível. Escolha essa opção se o seu código de transpilação receber como entrada um "objeto de alto nível", como um operador de Clifford ou uma função linear, e gerar uma descrição de um circuito quântico que implemente esse objeto de alto nível. Os objetos de alto nível são representados por subclasses da classe Operation.
Depois de determinar o tipo de plug-in a ser criado, siga estas etapas para criar o plug-in:
- Crie uma subclasse da classe de plug-in abstrata apropriada:
- PassManagerStagePlugin para um plug-in de estágio de transpilador,
- UnitarySynthesisPlugin para um plug-in de síntese unitária, e
- HighLevelSynthesisPlugin para um plug-in de síntese de alto nível.
- Exponha a classe como um ponto de entrada do setuptools nos metadados do pacote, geralmente editando o arquivo
pyproject.toml,setup.cfgousetup.pydo seu pacote Python.
Não há limite para o número de plug-ins que um único pacote pode definir, mas cada plug-in deve ter um nome exclusivo. O próprio Qiskit SDK inclui vários plug-ins, cujos nomes também são reservados. Os nomes reservados são:
- Plug-ins de estágio do Transpiler: Consulte esta tabela.
- Plug-ins de síntese unitária:
default,aqc,sk - Plug-ins de síntese de alto nível:
Classe de operação | Nome da operação | Nomes reservados |
|---|---|---|
| Clifford | clifford | default, ag, bm, greedy, layers, lnn |
| LinearFunction | linear_function | default, kms, pmh |
| PermutationGate | permutation | default, kms, basic, acg, token_swapper |
Nas próximas seções, mostraremos exemplos dessas etapas para os diferentes tipos de plug-ins. Nesses exemplos, presumimos que estamos criando um pacote Python chamado my_qiskit_plugin. Para obter informações sobre a criação de pacotes Python, consulte este tutorial no site Python.
Exemplo: Criar um plugin de etapa transpiler
Neste exemplo, criamos um plug-in de estágio do transpilador para o estágio layout (consulte Estágios do transpilador para obter uma descrição dos 6 estágios do pipeline de transpilação integrado do Qiskit).
Nosso plug-in simplesmente executa VF2Layout por um número de tentativas que depende do nível de otimização solicitado.
Primeiro, criamos uma subclasse de PassManagerStagePlugin. Há um método que precisamos implementar, chamado pass_manager. Esse método recebe como entrada um PassManagerConfig e retorna o gerenciador de passes que estamos definindo. O objeto PassManagerConfig armazena informações sobre o backend de destino, como o mapa de acoplamento e as portas de 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_pmAgora, expomos o plug-in adicionando um ponto de entrada em nossos metadados do pacote Python.
Aqui, supomos que a classe que definimos está exposta em um módulo chamado my_qiskit_plugin, por exemplo, ao ser importada no arquivo __init__.py do módulo my_qiskit_plugin .
Editamos o arquivo pyproject.toml, setup.cfg ou setup.py do nosso pacote (dependendo do tipo de arquivo que você escolheu para armazenar os metadados do projeto 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',
]
}
)Consulte a tabela de estágios do plug-in do transpiler para obter os pontos de entrada e as expectativas de cada estágio do transpiler.
Para verificar se o seu plug-in foi detectado com êxito pelo Qiskit, instale o pacote do plug-in e siga as instruções em Plug-ins do Transpiler para listar os plug-ins instalados e verifique se o seu plug-in aparece na lista:
from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins
list_stage_plugins("layout")Output:
['default', 'dense', 'sabre', 'trivial']
Se o nosso plug-in de exemplo estivesse instalado, o nome my_layout apareceria nessa lista.
Se quiser usar um estágio de transpilador integrado como ponto de partida para seu plug-in de estágio de transpilador, você poderá obter o gerenciador de passes para um estágio de transpilador integrado usando PassManagerStagePluginManager. A célula de código a seguir mostra como fazer isso para obter o estágio de otimização integrada para o nível de otimização 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
)Exemplo: Criar um plugin de síntese unitária
Neste exemplo, criaremos um plug-in de síntese unitária que simplesmente usa a passagem de transpilação UnitarySynthesis transpilação integrada para sintetizar uma porta. É claro que seu próprio plug-in fará algo mais interessante do que isso.
A classe UnitarySynthesisPlugin define a interface e o contrato para plug-ins de síntese unitária plug-ins. O método principal é run, que recebe como entrada uma matriz Numpy que armazena uma matriz unitária e retorna um DAGCircuit que representa o circuito sintetizado a partir dessa matriz unitária.
Além do método run , há vários métodos de propriedade que precisam ser definidos.
Consulte UnitarySynthesisPlugin para obter a documentação de todas as propriedades necessárias.
Vamos criar nossa subclasse 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 você achar que as entradas disponíveis para o run são insuficientes para seus objetivos, abra um problema explicando seus requisitos. As alterações na interface do plug-in, como a inclusão de entradas opcionais adicionais, serão feitas de forma compatível com as versões anteriores, de modo que não exijam alterações nos plug-ins existentes.
Todos os métodos prefixados com supports_ são reservados em uma classe derivada de UnitarySynthesisPlugin como parte da interface. Você não deve definir nenhum método supports_* personalizado em uma subclasse que não esteja definido na classe abstrata.
Agora, expomos o plug-in adicionando um ponto de entrada em nossos metadados do pacote Python.
Aqui, supomos que a classe que definimos está exposta em um módulo chamado my_qiskit_plugin, por exemplo, ao ser importada no arquivo __init__.py do módulo my_qiskit_plugin .
Editamos o arquivo pyproject.toml, setup.cfg ou setup.py do nosso pacote:
[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',
]
}
)Como antes, se o seu projeto usar setup.cfg ou setup.py em vez de pyproject.toml, consulte a documentação do setuptools para saber como adaptar essas linhas à sua situação.
Para verificar se o seu plug-in foi detectado com êxito pelo Qiskit, instale o pacote do plug-in e siga as instruções em Plug-ins do Transpiler para listar os plug-ins instalados e verifique se o seu plug-in aparece na lista:
from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names
unitary_synthesis_plugin_names()Output:
['aqc', 'clifford', 'default', 'gridsynth', 'sk']
Se o nosso plug-in de exemplo estivesse instalado, o nome my_unitary_synthesis apareceria nessa lista.
Para acomodar plug-ins de síntese unitária que expõem várias opções, a interface do plug-in tem uma opção para que os usuários forneçam um dicionário de configuração. Isso será passado para o método run por meio do argumento da palavra-chave options . Se o seu plug-in tiver essas opções de configuração, você deverá documentá-las claramente.
Exemplo: Criar um plug-in de síntese de alto nível
Neste exemplo, criaremos um plug-in de síntese de alto nível que simplesmente usa a função synth_clifford_bm integrada para sintetizar um operador Clifford.
A classe HighLevelSynthesisPlugin define a interface e o contrato para plug-ins de síntese de alto nível. O método principal é run.
O argumento posicional high_level_object é uma operação que representa o objeto de "alto nível" a ser sintetizado. Por exemplo, pode ser um LinearFunction ou um Clifford.
Os seguintes argumentos de palavra-chave estão presentes:
targetespecifica o backend de destino, permitindo que o plug-in acesse todas as informações específicas do destino, como o mapa de acoplamento, o conjunto de portas suportado e assim por diantecoupling_mapespecifica apenas o mapa de acoplamento e só é usado quandotargetnão é especificado.qubitsespecifica a lista de qubits sobre os quais o objeto de objeto de alto nível é definido, caso a síntese seja feita no circuito físico. Um valor deNoneindica que o layout ainda não foi escolhido e que os qubits físicos no alvo ou no mapa de acoplamento em que essa operação está operando ainda não foram determinados.optionsum dicionário de configuração de forma livre para opções específicas do plug-in. Se o seu plug-in tiver essas opções de configuração, você deve documentá-las claramente.
O método run retorna um QuantumCircuit que representa o circuito sintetizado a partir desse objeto de alto nível.
Também é permitido retornar None, indicando que o plug-in não consegue sintetizar o objeto de alto nível fornecido.
A síntese real de objetos de alto nível é realizada pelo HighLevelSynthesis passagem do transpilador.
Além do método run , há vários métodos de propriedade que precisam ser definidos.
Consulte HighLevelSynthesisPlugin para obter a documentação de todas as propriedades necessárias.
Vamos definir nossa subclasse 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 NoneEsse plug-in sintetiza objetos do tipo Clifford que têm no máximo 3 qubits, usando o método synth_clifford_bm .
Agora, expomos o plug-in adicionando um ponto de entrada em nossos metadados do pacote Python.
Aqui, supomos que a classe que definimos está exposta em um módulo chamado my_qiskit_plugin, por exemplo, ao ser importada no arquivo __init__.py do módulo my_qiskit_plugin .
Editamos o arquivo pyproject.toml, setup.cfg ou setup.py do nosso pacote:
[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',
]
}
)O name consiste em duas partes separadas por um ponto (.):
- O nome do tipo de operação que o plug-in sintetiza (neste caso,
clifford). Observe que essa cadeia de caracteres corresponde ao atributonameda classe Operation, e não ao nome da classe em si. - O nome do plug-in (nesse caso,
special).
Como antes, se o seu projeto usar setup.cfg ou setup.py em vez de pyproject.toml, consulte a documentação do setuptools para saber como adaptar essas linhas à sua situação.
Para verificar se o seu plug-in foi detectado com êxito pelo Qiskit, instale o pacote do plug-in e siga as instruções em Plug-ins do Transpiler para listar os plug-ins instalados e verifique se o seu plug-in aparece na lista:
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 o nosso plug-in de exemplo estivesse instalado, o nome my_clifford_synthesis apareceria nessa lista.
- Envie seu plug-in para o ecossistema Qiskit!
- Confira os tutoriais para ver exemplos de transpilação e execução de circuitos quânticos.