Source code for qudas.gate.executor

from __future__ import annotations

from concurrent.futures import ThreadPoolExecutor, as_completed
from typing import Dict, Any, Optional

from qudas.core.base import QdExecutorBase

from .input import QdGateInput
from .output import QdGateOutput


[docs]class QdGateExecutor(QdExecutorBase): """量子ゲート方式の ``Executor``。 * デフォルトでは ``qiskit_simulator`` を用いて実行します。 * :py:meth:`run_split` によりブロック毎に backend を切り替えた並列実行も可能です。 """ # -------------------------------------------------------------- # コンストラクタ / 共通パラメータ # -------------------------------------------------------------- def __init__( self, provider: str = "default", provider_config: Optional[Dict[str, Any]] = None, provider_map: Optional[Dict[str, str]] = None, provider_config_map: Optional[Dict[str, Dict[str, Any]]] = None, ) -> None: """Parameters ---------- provider : str, optional The provider to use for the executor. (e.g. "qiskit", "braket") provider_config : dict[str, Any], optional The configuration for the provider. provider_map : dict[str, str], optional The mapping of block labels to providers. (e.g. {"block0": "qiskit", "block1": "braket"}) provider_config_map : dict[str, dict[str, Any]], optional The mapping of block labels to provider configurations. (e.g. {"block0": {"backend": "qiskit_simulator"}, "block1": {"backend": "braket_ionq"}}) """ super().__init__(provider, provider_config, provider_map, provider_config_map) # -------------------------------------------------------------- # パブリック API # --------------------------------------------------------------
[docs] def run( self, input_data: QdGateInput ) -> QdGateOutput: # noqa: D401 – simple method name """単一の :class:`QdGateInput` を実行し、 ``QdGateOutput`` を返却。""" block = input_data.block provider = self.resolve_provider(block.label) config = self.resolve_provider_config(block.label) _, result = self._run_single_block(block, provider, config) return QdGateOutput({block.label: result})
[docs] def run_split( self, input_data: QdGateInput ) -> QdGateOutput: # noqa: D401 – simple method name """入力をブロックごとに分割して並列実行します。 Parameters ---------- input_data : QdGateInput 実行対象の量子回路ブロックを含む入力。 Returns ------- QdGateOutput ブロック名をキー、各 backend の実行結果を値とする辞書を ``results`` として保持します。 """ if not hasattr(input_data, "blocks"): raise AttributeError("input_data は 'blocks' 属性を持つ必要があります。") results: Dict[str, Dict[str, Any]] = {} # 並列実行 (CPU バウンドではないため ThreadPoolExecutor で十分) with ThreadPoolExecutor() as pool: future_map = { pool.submit( self._run_single_block, block, self.resolve_provider(block.label), self.resolve_provider_config(block.label), ): block.label for block in input_data.blocks } for future in as_completed(future_map): label, result = future.result() results[label] = result return QdGateOutput(results)
# -------------------------------------------------------------- # 内部ユーティリティ # -------------------------------------------------------------- def _run_single_block(self, block, provider: str, kwargs: Dict[str, Any]): """1 ブロック分の量子回路を指定バックエンドで実行。""" # --- 現在は QuantumCircuitBlock (SDK 非依存) をサポート ------------------ if provider == "qiskit" or provider == "default": # ``block`` の型に応じて回路を用意 if hasattr(block, "gates"): # 新しい QuantumCircuitBlock 形式 circuit = self._block_to_qiskit(block) else: # 旧形式: ``circuit`` 属性に直接 qiskit.QuantumCircuit が入っている想定 circuit = self._ensure_qiskit_circuit(getattr(block, "circuit", None)) result = self._run_qiskit(circuit, **kwargs) else: raise NotImplementedError(f"Provider '{provider}' は未サポートです。") return block.label, result # ------------------------------------------------------------------ # 量子回路ブロック → Qiskit 変換 # ------------------------------------------------------------------ @staticmethod def _block_to_qiskit(block): """QuantumCircuitBlock を Qiskit ``QuantumCircuit`` へ変換する簡易実装。""" try: from qiskit import QuantumCircuit # type: ignore qc = QuantumCircuit(block.num_qubits, block.num_qubits) for gate_ir in block.gates: # ゲート名に応じてダイナミックにメソッド呼び出し gate_name = gate_ir.gate.lower() # 制御ゲート (cx, cz など) は controls + targets を結合して渡す qargs = gate_ir.controls + gate_ir.targets # パラメータ付きゲート (rx, ry, rz ...) は params を先頭に try: method = getattr(qc, gate_name) except AttributeError: # 未対応ゲートはスキップ (必要に応じて追加実装) continue # 呼び出し引数を組み立て if gate_ir.params: method(*gate_ir.params, *qargs) else: method(*qargs) # 省略した classical register への測定を追加 (デフォルト: 全量子ビット) qc.measure_all() return qc except Exception: # qiskit import error or conversion error → fallback return QdGateExecutor._ir_to_qiskit(None) # ------------------------------------------------------------------ # backend 実装 # ------------------------------------------------------------------ @staticmethod def _run_qiskit(circuit, **kwargs): """Qiskit Aer/Basics を用いて回路をシミュレーション。""" try: # lazy import – qiskit が入っていない環境でも動作させるため from qiskit import Aer, execute # type: ignore backend = Aer.get_backend(kwargs.get("backend", "qasm_simulator")) job = execute(circuit, backend=backend, **kwargs) counts = job.result().get_counts() return {"counts": dict(counts), "device": "qiskit_simulator"} except Exception: # noqa: BLE001 – ImportError or runtime errors # qiskit 非インストール or その他エラー → naive fallback return QdGateExecutor._run_naive(device="qiskit_simulator(fallback)") # ------------------------------------------------------------------ # フォールバック実装 # ------------------------------------------------------------------ @staticmethod def _run_naive(device: str = "naive"): """依存ライブラリが無い環境向けの簡易実装。""" # とりあえず半々のビット列が得られたと仮定 return {"counts": {"00": 512, "11": 512}, "device": device} # ------------------------------------------------------------------ # 変換ユーティリティ # ------------------------------------------------------------------ @staticmethod def _ir_to_qiskit(ir): """`QuAlgorithmIR` → Qiskit ``QuantumCircuit`` 変換。 現状は最小実装として、ゲート情報を無視し 1qubit の Hadamard + 測定を生成。 IR がよりリッチになった際はここで map してください。 """ try: from qiskit import QuantumCircuit # type: ignore qc = QuantumCircuit(1, 1) qc.h(0) qc.measure(0, 0) return qc except Exception: # qiskit 無い場合はダミーを返す (呼び出し側でフォールバック) return None @staticmethod def _ensure_qiskit_circuit(obj): """入力が QuantumCircuit でない場合はダミー回路へ置き換える。""" try: from qiskit import QuantumCircuit # type: ignore if isinstance(obj, QuantumCircuit): return obj except Exception: pass # qiskit import error → fallthrough # fallback dummy circuit return QdGateExecutor._ir_to_qiskit(None)
# 下位互換性維持のためのエイリアス ----------------------------------------- QdGateExec = QdGateExecutor