Sari la conținutul principal

Creează un plugin Transpiler

Versiuni de pachete

Codul de pe această pagină a fost dezvoltat folosind următoarele cerințe. Îți recomandăm să folosești aceste versiuni sau unele mai noi.

qiskit[all]~=2.3.0

Crearea unui plugin Transpiler este o modalitate excelentă de a-ți împărtăși codul de transpilare cu comunitatea Qiskit, permițând altor utilizatori să beneficieze de funcționalitatea pe care ai dezvoltat-o. Îți mulțumim pentru interesul de a contribui la comunitatea Qiskit!

Înainte de a crea un plugin Transpiler, trebuie să decizi ce tip de plugin este potrivit pentru situația ta. Există trei tipuri de plugin-uri Transpiler:

  • Plugin de etapă Transpiler. Alege-l dacă definești un manager de pași care poate înlocui una dintre cele 6 etape ale unui manager de pași stadializat prestabilit.
  • Plugin de sinteză unitară. Alege-l dacă codul tău de transpilare primește ca intrare o matrice unitară (reprezentată ca un array Numpy) și produce o descriere a unui Circuit cuantic care implementează acea unitară.
  • Plugin de sinteză de nivel înalt. Alege-l dacă codul tău de transpilare primește ca intrare un „obiect de nivel înalt", cum ar fi un operator Clifford sau o funcție liniară, și produce o descriere a unui Circuit cuantic care implementează acel obiect de nivel înalt. Obiectele de nivel înalt sunt reprezentate de subclase ale clasei Operation.

Odată ce ai stabilit ce tip de plugin să creezi, urmează acești pași pentru a crea plugin-ul:

  1. Creează o subclasă a clasei abstracte de plugin corespunzătoare:
  2. Expune clasa ca un entry point setuptools în metadatele pachetului, de obicei prin editarea fișierului pyproject.toml, setup.cfg sau setup.py al pachetului tău Python.

Nu există o limită pentru numărul de plugin-uri pe care un singur pachet le poate defini, dar fiecare plugin trebuie să aibă un nume unic. SDK-ul Qiskit în sine include un număr de plugin-uri, ale căror nume sunt rezervate. Numele rezervate sunt:

  • Plugin-uri de etapă Transpiler: Vezi acest tabel.
  • Plugin-uri de sinteză unitară: default, aqc, sk
  • Plugin-uri de sinteză de nivel înalt:
Clasa OperationNumele OperationNume rezervate
Cliffordclifforddefault, ag, bm, greedy, layers, lnn
LinearFunctionlinear_functiondefault, kms, pmh
PermutationGatepermutationdefault, kms, basic, acg, token_swapper

În secțiunile următoare, prezentăm exemple ale acestor pași pentru diferitele tipuri de plugin-uri. În aceste exemple, presupunem că creăm un pachet Python numit my_qiskit_plugin. Pentru informații despre crearea pachetelor Python, poți consulta acest tutorial de pe site-ul Python.

Exemplu: Creează un plugin de etapă Transpiler

În acest exemplu, creăm un plugin de etapă Transpiler pentru etapa layout (vezi Etapele Transpiler pentru o descriere a celor 6 etape ale pipeline-ului de transpilare integrat al Qiskit). Plugin-ul nostru rulează pur și simplu VF2Layout pentru un număr de încercări care depinde de nivelul de optimizare solicitat.

Mai întâi, creăm o subclasă a PassManagerStagePlugin. Există o metodă pe care trebuie să o implementăm, numită pass_manager. Această metodă primește ca intrare un PassManagerConfig și returnează managerul de pași pe care îl definim. Obiectul PassManagerConfig stochează informații despre Backend-ul țintă, cum ar fi harta de cuplare și Gate-urile de bază ale acestuia.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
# 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

Acum, expunem plugin-ul adăugând un entry point în metadatele pachetului nostru Python. Aici, presupunem că clasa pe care am definit-o este expusă într-un modul numit my_qiskit_plugin, de exemplu prin import în fișierul __init__.py al modulului my_qiskit_plugin. Edităm fișierul pyproject.toml, setup.cfg sau setup.py al pachetului nostru (în funcție de tipul de fișier pe care l-ai ales pentru a stoca metadatele proiectului tău Python):

[project.entry-points."qiskit.transpiler.layout"]
"my_layout" = "my_qiskit_plugin:MyLayoutPlugin"

Vezi tabelul etapelor plugin-ului Transpiler pentru entry point-urile și așteptările pentru fiecare etapă Transpiler.

Pentru a verifica că plugin-ul tău este detectat cu succes de Qiskit, instalează pachetul plugin-ului tău și urmează instrucțiunile de la Plugin-uri Transpiler pentru listarea plugin-urilor instalate și asigură-te că plugin-ul tău apare în listă:

from qiskit.transpiler.preset_passmanagers.plugin import list_stage_plugins

list_stage_plugins("layout")
['default', 'dense', 'sabre', 'trivial']

Dacă plugin-ul nostru exemplu ar fi instalat, atunci numele my_layout ar apărea în această listă.

Dacă vrei să folosești o etapă Transpiler integrată ca punct de plecare pentru plugin-ul tău de etapă Transpiler, poți obține managerul de pași pentru o etapă Transpiler integrată folosind PassManagerStagePluginManager. Următoarea celulă de cod arată cum să faci asta pentru a obține etapa de optimizare integrată pentru nivelul de optimizare 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
)

Exemplu: Creează un plugin de sinteză unitară

În acest exemplu, vom crea un plugin de sinteză unitară care folosește pur și simplu pasul de transpilare built-in UnitarySynthesis pentru a sintetiza un Gate. Desigur, propriul tău plugin va face ceva mai interesant decât atât.

Clasa UnitarySynthesisPlugin definește interfața și contractul pentru plugin-urile de sinteză unitară. Metoda principală este run, care primește ca intrare un array Numpy ce stochează o matrice unitară și returnează un DAGCircuit reprezentând Circuit-ul sintetizat din acea matrice unitară. Pe lângă metoda run, există o serie de metode de tip proprietate care trebuie definite. Vezi UnitarySynthesisPlugin pentru documentația tuturor proprietăților obligatorii.

Să creăm subclasa noastră 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

Dacă descoperi că intrările disponibile pentru metoda run sunt insuficiente pentru scopurile tale, te rog să deschizi un issue explicând cerințele tale. Modificările aduse interfeței plugin-ului, cum ar fi adăugarea unor intrări opționale suplimentare, vor fi realizate într-un mod compatibil cu versiunile anterioare, astfel încât să nu necesite modificări din partea plugin-urilor existente.

Notă

Toate metodele cu prefixul supports_ sunt rezervate pe o clasă derivată din UnitarySynthesisPlugin ca parte a interfeței. Nu trebuie să definești metode personalizate supports_* pe o subclasă care nu sunt definite în clasa abstractă.

Acum, expunem plugin-ul adăugând un entry point în metadatele pachetului nostru Python. Presupunem că clasa pe care am definit-o este expusă într-un modul numit my_qiskit_plugin, de exemplu prin importare în fișierul __init__.py al modulului my_qiskit_plugin. Edităm fișierul pyproject.toml, setup.cfg sau setup.py al pachetului nostru:

[project.entry-points."qiskit.unitary_synthesis"]
"my_unitary_synthesis" = "my_qiskit_plugin:MyUnitarySynthesisPlugin"

Ca și înainte, dacă proiectul tău folosește setup.cfg sau setup.py în loc de pyproject.toml, consultă documentația setuptools pentru a adapta aceste linii situației tale.

Pentru a verifica că plugin-ul tău este detectat cu succes de Qiskit, instalează pachetul plugin-ului și urmează instrucțiunile din Transpiler plugins pentru a lista plugin-urile instalate și asigură-te că plugin-ul tău apare în listă:

from qiskit.transpiler.passes.synthesis import unitary_synthesis_plugin_names

unitary_synthesis_plugin_names()
['aqc', 'clifford', 'default', 'gridsynth', 'sk']

Dacă plugin-ul nostru exemplu ar fi instalat, atunci numele my_unitary_synthesis ar apărea în această listă.

Pentru a acomoda plugin-urile de sinteză unitară care expun mai multe opțiuni, interfața plugin-ului are o opțiune pentru ca utilizatorii să furnizeze un dicționar de configurare liber. Acesta va fi transmis metodei run prin argumentul keyword options. Dacă plugin-ul tău are aceste opțiuni de configurare, ar trebui să le documentezi clar.

Exemplu: Creează un plugin de sinteză de nivel înalt

În acest exemplu, vom crea un plugin de sinteză de nivel înalt care folosește pur și simplu funcția built-in synth_clifford_bm pentru a sintetiza un operator Clifford.

Clasa HighLevelSynthesisPlugin definește interfața și contractul pentru plugin-urile de sinteză de nivel înalt. Metoda principală este run. Argumentul pozițional high_level_object este o Operation care reprezintă obiectul „de nivel înalt" ce urmează să fie sintetizat. De exemplu, poate fi o LinearFunction sau un Clifford. Sunt prezenți următorii argumenți cu cuvânt cheie:

  • target specifică Backend-ul țintă, permițând plugin-ului să acceseze toate informațiile specifice țintei, precum harta de cuplare, setul de Gate-uri suportate și altele
  • coupling_map specifică doar harta de cuplare și este folosit numai când target nu este specificat.
  • qubits specifică lista de Qubiți peste care este definit obiectul de nivel înalt, în cazul în care sinteza se face pe Circuit-ul fizic. O valoare de None indică faptul că layout-ul nu a fost încă ales și că Qubiții fizici din țintă sau harta de cuplare pe care operează această operație nu au fost încă determinați.
  • options, un dicționar de configurare liber pentru opțiunile specifice plugin-ului. Dacă plugin-ul tău are aceste opțiuni de configurare, ar trebui să le documentezi clar.

Metoda run returnează un QuantumCircuit care reprezintă Circuit-ul sintetizat din acel obiect de nivel înalt. Este, de asemenea, permis să returneze None, indicând că plugin-ul nu poate sintetiza obiectul de nivel înalt dat. Sinteza efectivă a obiectelor de nivel înalt este realizată de pasul Transpiler HighLevelSynthesis.

Pe lângă metoda run, există o serie de metode de proprietate care trebuie definite. Vezi HighLevelSynthesisPlugin pentru documentația tuturor proprietăților obligatorii.

Hai să definim subclasa noastră 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

Acest plugin sintetizează obiecte de tip Clifford care au cel mult 3 Qubiți, folosind metoda synth_clifford_bm.

Acum, expunem plugin-ul adăugând un entry point în metadatele pachetului nostru Python. Presupunem că clasa definită este expusă într-un modul numit my_qiskit_plugin, de exemplu prin importul în fișierul __init__.py al modulului my_qiskit_plugin. Edităm fișierul pyproject.toml, setup.cfg sau setup.py al pachetului nostru:

[project.entry-points."qiskit.synthesis"]
"clifford.my_clifford_synthesis" = "my_qiskit_plugin:MyCliffordSynthesisPlugin"

name constă din două părți separate printr-un punct (.):

  • Numele tipului de Operation pe care îl sintetizează plugin-ul (în acest caz, clifford). Reține că acest șir corespunde atributului name al clasei Operation, nu numelui clasei în sine.
  • Numele plugin-ului (în acest caz, special).

Ca și înainte, dacă proiectul tău folosește setup.cfg sau setup.py în loc de pyproject.toml, consultă documentația setuptools pentru a adapta aceste linii situației tale.

Pentru a verifica că plugin-ul tău este detectat cu succes de Qiskit, instalează pachetul plugin-ului și urmează instrucțiunile de la Transpiler plugins pentru listarea plugin-urilor instalate, și asigură-te că plugin-ul tău apare în listă:

from qiskit.transpiler.passes.synthesis import (
high_level_synthesis_plugin_names,
)

high_level_synthesis_plugin_names("clifford")
['ag', 'bm', 'default', 'greedy', 'layers', 'lnn', 'rb_default']

Dacă plugin-ul nostru exemplu ar fi instalat, atunci numele my_clifford_synthesis ar apărea în această listă.

Recomandare