最初のマネージド・ワークフローをデプロイする
パッケージのバージョン
このページのコードは、次の要件を使用して開発されました。 これらのバージョン、またはそれ以降のバージョンの使用を推奨します。
qiskit[all]~=2.6.1
qiskit-serverless~=0.38.0
このページでは、ワークフローをアップロードして実行し、その結果を取得します。サンプルのワークフローは、Qiskit Circuitを最適化してシミュレーションします。古典ハードウェア上でCircuitをシミュレーションするため、QPU時間は使用しません。
始める前に、クライアントパッケージをインストールして認証してください。
AIアシスタントの支援を受けて独自のコードをデプロイするには、Qiskit Function templatesリポジトリにあるAIスキルを使用してください。
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"qiskit": "qiskit", "qiskit_ibm_catalog": "qiskit-ibm-catalog", "qiskit_serverless": "qiskit-serverless"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
ワークフローを書く
フォルダー構造を作成する
マネージド・ワークフローは、1つのエントリーポイント・ファイルを持つPythonファイルのフォルダーです。マネージド・ワークフローは、フォルダー内のすべて(最大50 MB)をアップロードし、エントリーポイントをスクリプトとして実行します。ワークフローはフォルダー全体をアップロードするため、deploy.ipynbなどのローカルファイルはフォルダーの外に置いてください。次のレイアウトを作成します。
hello_world_project/
├── deploy.ipynb # Your local notebook: upload and run the workflow
└── source_files/ # The working directory that you upload
└── hello_world.py # The entrypoint
hello worldのエントリーポイントを書く
エントリーポイントはCircuitを読み取り、それを最適化してシミュレーションし、カウントと2 Qubit深さを保存します。次のコードをsource_files/hello_world.pyとして保存します。
このページをノートブックとして読んでいる場合、hello_world.pyのコードセルは%%writefileマジックコマンドで始まります。このセルを実行すると、コードを実行する代わりに、コードがファイルに保存されます。
# This cell is hidden from users. It creates the working directory.
from pathlib import Path
Path("./source_files").mkdir(exist_ok=True)
%%writefile ./source_files/hello_world.py
from qiskit.primitives import StatevectorSampler
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_serverless import Job, get_arguments, save_result, update_status
# Read the arguments that you pass to run()
arguments = get_arguments()
circuit = arguments["circuit"]
shots = arguments.get("shots", 1024)
# Optimize the circuit. Printed text goes to the job's logs.
update_status(Job.OPTIMIZING_HARDWARE)
pass_manager = generate_preset_pass_manager(
optimization_level=1, basis_gates=["cz", "rz", "sx", "x"]
)
isa_circuit = pass_manager.run(circuit)
two_qubit_depth = isa_circuit.depth(lambda instruction: instruction.operation.num_qubits == 2)
print(f"Two-qubit depth after optimization: {two_qubit_depth}", flush=True)
# Simulate the circuit and return the counts
sampler = StatevectorSampler(seed=1234)
pub_result = sampler.run([isa_circuit], shots=shots).result()[0]
save_result({"counts": pub_result.data.meas.get_counts(), "two_qubit_depth": two_qubit_depth})
Writing ./source_files/hello_world.py
エントリーポイントは、qiskit_serverlessの3つの関数を使用します。
-
get_arguments()は、run()に渡したキーワード引数を辞書として返します。各値は、JSONシリアライズ可能であるか、QuantumCircuitのようにクライアントがエンコードできるオブジェクトである必要があります。エンコードされたオブジェクトは、渡したものと同じオブジェクトとして届きます。 -
update_status(Job.OPTIMIZING_HARDWARE)は、詳細ステータスをRUNNING: OPTIMIZING_FOR_HARDWAREに設定します。これはジョブの実行中に確認できます。update_status()で進捗を報告するを参照してください。 -
save_result()は、後でjob.result()で読み取れるJSONシリアライズ可能な値を保存します。このワークフローは、カウントを辞書として、最適化されたCircuitの2 Qubit深さを整数として返します。
2 Qubit深さなど、ワークフローが標準出力に書き込んだものはすべて、ジョブのログに送られます。タイムスタンプとログレベルを追加するには、loggingモジュールを使用します。ログの書き込みとエラー処理を参照してください。
ワークフローをアップロードする
ローカルのノートブックで、hello_world_projectフォルダーから次のコードを実行します。このコードは、保存済みのアカウントで接続し、ワークフローを定義してアップロードします。entrypointはワークフローが実行するファイル、working_dirはアップロードするフォルダーです。
from qiskit_ibm_catalog import ManagedWorkflow, ManagedWorkflowClient
client = ManagedWorkflowClient()
hello_world = ManagedWorkflow(
title="hello-world",
entrypoint="hello_world.py",
working_dir="./source_files/",
)
hello_world = client.upload(hello_world)
入力を指定してワークフローを実行する
hello_worldへの入力として渡す、3 QubitのGHZ Circuitを構築します。
from qiskit import QuantumCircuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure_all()
Circuitとショット数をキーワード引数としてhello_worldを実行します。引数名は、エントリーポイントがget_arguments()で読み取るキーと一致します。
job = hello_world.run(circuit=circuit, shots=1024)
print(job.job_id)
54705348-5117-44b7-935b-e6986ef182fa
run()は、ワークフローがジョブをキューに入れた後にJobを返します。ジョブが実行されるのを待ちません。Pythonセッションを閉じた後も、ジョブはクラウド上で継続します。後でもう一度見つけるには、client.jobs()で最近のジョブを一覧表示します。
ステータス、結果、ログを取得する
run()を呼び出した直後は、通常、ステータスはQUEUEDです。
job.status()
'QUEUED'
ステータスは、QUEUEDからINITIALIZING、RUNNING、そしてDONEへと変化します。
job.result()は、ジョブが終了するまで待機し、ワークフローがsave_result()に渡した値を返します。
job.result()
{'counts': {'111': 514, '000': 510}, 'two_qubit_depth': 2}
# This cell is hidden from users. It checks the result.
assert set(_["counts"]) <= {"000", "111"} # noqa: F821
assert sum(_["counts"].values()) == 1024 # noqa: F821
from qiskit.visualization import plot_histogram
plot_histogram(job.result()["counts"])
ジョブの実行中にログを読み取ることができます。ワークフローは、約15秒ごとに新しい行をアップロードします。ログには、ワークフローが出力した行が含まれます。
print(job.logs())
Two-qubit depth after optimization: 2
# This cell is hidden from users. It checks the logs.
assert "Two-qubit depth after optimization" in job.logs()
ワークフローを更新する
ワークフローを変更するには、そのファイルを編集し、同じtitleで再度upload()を呼び出します。新しいアップロードは以前のものを置き換え、すでに開始されたジョブには影響しません。
arguments_schemaやsizes_mapなどのフィールドは、新しいアップロードで省略した場合、保存されている値が保持されます。
次のステップ
- 入力と出力を定義するで、ワークフローがサポートする引数と結果のタイプを確認します。
- 依存関係を宣言するで、ワークフローでより多くのPythonパッケージを使用します。
- ワークフローから量子ジョブを実行するで、QPU上でCircuitを実行し、各ステージを報告し、QPUジョブを追跡します。
- マネージド・ワークフローを実行するで、すべてのジョブステータスを確認し、失敗したジョブをデバッグし、後でジョブを見つける方法を学びます。
- 動作するコードから始めるには、Qiskit Function templatesのいずれかをデプロイするか、テンプレートを自分のワークフロー向けに調整します。