メインコンテンツまでスキップ

ファンクションの作成(コードレベル)

本ガイドでは、実行環境の定義、実行ロジックの実装、ソースコードの管理、およびファンクションバージョンのデプロイを通じて、開発者がファンクションを作成する方法を説明します。

本ガイドは、UI 操作やジョブの実行ではなく、コードレベルにおけるファンクションの実装およびライフサイクルに焦点を当てています。

1. ファンクションの概念

ファンクションとは、量子計算またはハイブリッド計算のロジックをカプセル化した、再利用可能な実行ユニットを指します。

各ファンクションは、以下の要素で構成されます。

  • ランタイムテンプレート(フレームワークおよび実行環境)
  • ファンクションロジックを実装したソースコード
  • 1 つ以上のバージョン
  • ファンクションを呼び出し可能にするためのデプロイアーティファクト

2. ファンクションメタデータの定義

ファンクションを作成する際には、以下のメタデータの指定が必要です。

ファンクション名

ファンクション名は、プロジェクト内でファンクションを一意に識別するための名称です。

ルール:

  • 使用可能な文字:英小文字(a~z)、数字(0~9)、ハイフン(-)
  • スペースは使用不可
  • 最小文字数:2 文字
  • プロジェクト内で一意であること

これらのルールを満たしていない、または既に同名の関数が存在する場合、ファンクションの作成は失敗します。

説明(任意)

ファンクションの目的や想定される使用方法を説明するために、任意で説明文を設定できます。

3. ランタイムテンプレートの選択

ランタイムテンプレートは、以下の内容を定義します。

  • 実行環境
  • 量子または古典計算をサポートしているフレームワーク
  • ファンクションの初期構成

サポートされているテンプレートの例:

  • Qiskit
  • Braket
  • CUDA Quantum
  • PennyLane
  • その他のサポート対象環境

各テンプレートには、以下の内容が含まれます。

  • デフォルトの handler.py 実装
  • 互換性のあるランタイム実行環境
  • フレームワーク固有のヘルパーライブラリ

重要: ランタイムテンプレートはシステム固有のものです。

ファンクションを作成した後は、テンプレートを変更することはできません。

4. ファンクションバージョン

各ファンクションは、デフォルトのバージョン(例:v1)で初期化されます。

バージョンは、以下を表します。

  • ファンクションのソースコードの特定スナップショット
  • 個別に呼び出し可能なデプロイ単位

複数のバージョンを利用することで、開発者は以下を実現できます。

  • 実装を安全に反復・改善します。
  • 安定版に影響を与えずに新しいロジックをテストします。
  • 必要に応じて、以前の実装にロールバックします。

5. ファンクションロジックの実装

コアエントリーポイント:handler.py

handler.py ファイルには、ファンクションの主要な実行ロジックが含まれます。

ファンクションは、必ず processing(invocation_input) メソッドを公開する必要があります。

def processing(invocation_input):
# implement computation logic here
return result

invocation_input

  • 関数呼び出し時に渡される入力ペイロードを表します。
  • 通常、計算や回路構築に必要なパラメータが含まれます。

handler.py の主な役割:

  • 量子回路の構築
  • 実行ロジックの準備
  • 入力の検証
  • 実行結果の返却

依存関係の管理:requirements.txt

requirements.txtファイルには、ファンクションの実行に必要なすべての Python 依存ライブラリを指定します。

  • ここに記載されたライブラリは、デプロイ時に自動的にインストールされます。
  • 依存関係が不足している場合、デプロイまたは実行時にエラーが発生する可能性があります。

例:

qiskit
numpy

Processingファンクションの返却仕様

processing() ファンクションは、計算ロジックの初期化および構築のみを担当します。

本ファンクションは、量子回路の定義および量子ゲートの構成までを実施することを目的とします。

processing() ファンクションは、以下の処理を 実行してはなりません。

  • バックエンド上で回路を実行すること
  • ジョブを直接送信すること
  • 特定の実行デバイスへバインドすること
  • SDKレベルの実行APIを呼び出すこと

バックエンドの選択、デバイス設定、およびジョブ送信は、実行エンジン側で管理されます。

processing() ファンクションは、選択されたランタイムテンプレートに応じて、標準化されたオブジェクトを返却する必要があります。

SDK別の返却要件

1. Amazon Braket (braket)

  • 必須戻り値オブジェクト:braket.circuits.circuit.Circuit

  • 説明:ファンクションは、量子ゲートが適用された Amazon Braket の Circuit オブジェクトを返却する必要があります。測定命令(measurement)は明示的に定義可能です。

    もしくは、実行エンジンのデフォルト実行フローにより、すべての量子ビットが測定される動作に依存することも可能です。

  • 実装例:

from braket.circuits.circuit import Circuit

def processing(invocation_input):
# Initialize Braket circuit
circuit = Circuit()

# Apply quantum gates
circuit.h(0).cnot(0, 1)

# MANDATORY: Return the Circuit object
return circuit

2. IBM Qiskit (qiskit)

  • 必須戻り値オブジェクト:qiskit.QuantumCircuit

  • 説明:ファンクションは、Qiskit の QuantumCircuitオブジェクトを返却しなければなりません。

    返却される回路は、問題の要件に応じて以下を含む必要があります。

    ・古典レジスタ(classical registers)

    ・測定命令(measure instructions)

    特に、確率分布や状態カウント(state counts)を取得する必要がある場合には、古典レジスタおよび測定処理を明示的に回路へ含める必要があります。

  • 実装例:

from qiskit import QuantumCircuit

def processing(invocation_input):
# Initialize circuit with 2 qubits and 2 classical bits
qc = QuantumCircuit(2, 2)

# Add gates and measurements
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])

# MANDATORY: Return the QuantumCircuit object
return qc

3. PennyLane (pennylane)

  • 必須戻り値オブジェクト:Pythonファンクションを返却する必要があります。

    当該ファンクションは、PennyLaneのオペレーションを含み、PennyLaneの測定オブジェクト(例:qml.probs(), qml.expval(), qml.sample()など)を返す構造でなければなりません。

  • 説明:他のSDKでは、インスタンス化されたオブジェクトを返却する設計が一般的ですが、PennyLane における本アーキテクチャの標準設計では、未実行のファンクションそのものを返却する必要があります。

  • 重要事項:

    ・@qml.qnode デコレーターでラップしてはなりません。

    ・QNode の生成には Device 設定が必要です。

    ・デバイス設定は、下流の実行エンジン側で処理されます。

    そのため、processingファンクションでは QNode化されていない生のPythonファンクションを返却すること が要件となります。

  • 実装例:

import pennylane as qml

def processing(invocation_input):
# Define the circuit as a nested function
def simple_circuit():

# Apply gate
qml.Hadamard(wires=0)

# Return standard PennyLane measurements
return qml.probs(wires=0), qml.probs()

# MANDATORY: Return the FUNCTION itself (do not call it)
return simple_circuit

4. Rigetti PyQuil (pyquil)

  • 必須戻り値オブジェクト:pyquil.quil.Program

  • 説明:processing ファンクションでは、以下の処理を実装する必要があります。

    ①Program オブジェクトの初期化

    ②古典読み出し結果を保存するためのメモリ領域/レジスタ(例:ro)の宣言

    ③量子ゲートの適用

    ④MEASURE 命令の追加

    特に、測定結果を取得する場合は、古典メモリ領域の宣言および MEASURE 命令を明示的に追加することが必須要件となります。

  • 実装例:

from pyquil import Program
from pyquil.gates import X, MEASURE

def processing(invocation_input):
p = Program()

# Declare classical memory for readout
ro = p.declare('ro', 'BIT', 1)

# Apply gates
p += X(0)
p += X(1)
p += X(2)

# Add measurement instruction
p += MEASURE(0, ro[0])

# MANDATORY: Return the Program object
return p

5. D-Wave (pyqubo / dimod)

  • 必須戻り値オブジェクト:dimod.BinaryQuadraticModel (BQM)

  • 説明:量子アニーリング(Quantum Annealing)では、ゲート方式の量子回路は使用しません。

    代わりに、数理モデル(最適化問題)を構築します。

    processing ファンクションでは、以下の手順を実装する必要があります。

    ①目的関数(ハミルトニアン)を定義する。

    ②必要に応じて制約条件を定義する。

    ③PyQUBO(または同等ライブラリ)を使用してモデルを構築する。

    ④モデルをコンパイルし、BQM(Binary Quadratic Model)オブジェクトへ変換する。

    ⑤コンパイル済みの BQM オブジェクトを返却する。

    重要事項:返却値は 必ずコンパイル済みの BQM オブジェクトでなければなりません。

  • 実装例:

from pyqubo import Array, Constraint

def processing(invocation_input):
# Initialize binary variables
x = Array.create('x', shape=3, vartype='BINARY')

# Objective function and constraint
H = (x[0] + x[1] - 1)**2 + (x[1] + x[2] - 1)**2
constraint = Constraint(x[0] * x[1], label='constraint')
model = H + constraint

# Compile to BQM
bqm = model.compile().to_bqm()

# MANDATORY: Return the BQM object
return bqm

6. Microsoft Q# (qsharp)

  • 必須戻り値オブジェクト:Compiled Q# Operation(qsharp.compile()の出力オブジェクト)
  • 説明:ファンクションは、プロジェクトルートおよびターゲットプロファイルを指定して、Q# 環境を初期化する必要があります。続いて、提供された invocation_input を使用して対象の Q# オペレーションをコンパイルし、コンパイル済みオブジェクトを返却する必要があります。これにより、実行エンジンは指定されたバックエンド上で Q# コードをシームレスに実行できるようになります。
  • 実装例:
import os
import qsharp

def processing(invocation_input):
# Optional: Debugging the execution path
print("Current working directory:", os.getcwd())

# Initialize the Q# workspace and target profile
qsharp.init(project_root='./function', target_profile=qsharp.TargetProfile.Base)

# MANDATORY: Return the compiled Q# operation
return qsharp.compile(f"Main.RandomNBits({invocation_input})")

戻り値オブジェクト一覧表

SDK 名標準インポート必須の戻り値型
Braketfrom braket.circuits.circuit import CircuitCircuit
Qiskitfrom qiskit import QuantumCircuitQuantumCircuit
PennyLaneimport pennylane as qmlfunction (qml のオペレーションを含む callable オブジェクト)
PyQuilfrom pyquil import ProgramProgram
D-Wavefrom pyqubo import Arraydimod.BinaryQuadraticModel (BQM)
Q#import qsharpCompiled Q# Operationqsharp.compile() により生成されるオブジェクト)

結果変換:post_processing(job_result)

実行エンジンがジョブを完了すると、プラットフォームは生の実行出力を含んだ job_result オブジェクトを生成します。 各関数は handler.py の中でpost_processing(job_result) メソッドを定義しなければなりません。

このメソッドは、生の実行結果を最終的に呼び出し元に返すレスポンスに変換する役割を担います。

ファンクションシグネチャ

def post_processing(job_result):
return transformed_output

入力(Input)

  • job_result: 実行エンジンから返される完了した実行結果。

出力(Output)

  • 任意のシリアライズ可能な Python オブジェクト(例:dict、list、string、number)。

post_processingが定義されていない場合、関数のデプロイは失敗します。

例1 — 測定確率の計算

ユースケース:生の測定カウントを正規化された確率分布に変換します。

def post_processing(job_result):
counts = job_result.get_counts()
total_shots = sum(counts.values())

probabilities = {
state: count / total_shots
for state, count in counts.items()
}

return probabilities

例2 — 特定の量子状態のフィルタリング

ユースケース:ターゲット量子状態の成功率を抽出します。

def post_processing(job_result):
counts = job_result.get_counts()
target_state = "11"

success_rate = counts.get(target_state, 0) / sum(counts.values())

return f"{target_state}: {success_rate * 100:.2f}%"

例3 — 実行結果全文の返却

ユースケース:デバッグや高度な解析のために、生の実行出力を完全に保持します。

def post_processing(job_result):
return job_result

6. ソースコードの変更および拡張

開発者は、以下の方法でファンクションの実装をカスタマイズできます。

  • デフォルトのテンプレートコードを編集します。
  • 追加の Python モジュール(*.py)を追加します。
  • 既存のロジックを置き換える、または拡張するためにカスタムソースファイルをアップロードします。

本プラットフォームは、以下の両方をサポートしています。

  • 組み込みエディタによる直接的なコード編集
  • ローカルの Python ファイルのアップロード

すべてのソースコードの変更は、ファンクションのバージョン管理によって追跡されます。

7. 変更の保存およびバージョン作成

ソースコードが変更されるたびに、以下の処理が行われます。

  • 変更を保存すると、新しいファンクションバージョンが作成されます。
  • 各バージョンには、ソースコードの完全なスナップショットが保持されます。
  • バージョンは、デプロイ用に個別に選択できます。

この仕組みにより、制御された反復開発および試行錯誤が可能になります。

8. ファンクションバージョンのデプロイ

デプロイでは、特定のファンクションバージョンが実行可能なアーティファクトとしてパッケージ化されます。 デプロイ時には、以下の処理が行われます。

  • requirements.txt に記載された依存関係がインストールされます。
  • ソースコードの検証が行われます。
  • ファンクションバージョンが呼び出し可能な状態になります。

デプロイが正常に完了すると、以下が可能になります。

  • SDK または API を介してファンクションを呼び出します。
  • 実行ジョブの作成にファンクションバージョンを利用します。

9. 主要な制約およびベストプラクティス

  • ファンクション作成後は、ランタイムテンプレートを変更することはできません。
  • 各デプロイは、特定のファンクションバージョンに対応します。
  • デプロイ前に、必要なすべての依存関係を宣言してください。
  • バージョン管理を活用し、実験的な変更を安定版リリースから分離してください。

10. 次のステップ

ファンクションをデプロイした後、開発者は通常、以下の作業を行います。

  • SDK または API を使用して、プログラムからファンクションを呼び出します
  • デプロイ済みのファンクションを基に、実行ジョブを作成・管理します
  • 実行結果やログを監視します

詳細については、以下のガイドを参照してください。