論理プログラムの実行とデコード

Microsoft Quantum Development Kit (QDK) を使用して、論理Q#またはOpenQASMプログラムをQuantum Intermediate Representation(QIR)にコンパイルし、その後qdk.simulation.run_qirを使用してqodecを介してプログラムを実行します。 ノイズ モデルを設定し、組み込みの症候群 デコーダーまたはオプションの deq 回路レベル デコーダーを選択します。

[前提条件]

開始する前に、次のタスクを完了してください。

Q#の例では、qodecコード用の命令を含むprotocolという名前のC4を使用します。 これを作成するには、「QEC プロトコルの開発と検証」を参照してください。

import qodec
from qdk import ec

c4 = qodec.Code(
    "C4",
    stabilizers=["X_0 X_1 X_2 X_3", "Z_0 Z_1 Z_2 Z_3"],
    x=["X_0 X_1", "X_0 X_2"],
    z=["Z_0 Z_2", "Z_0 Z_1"],
)

protocol = ec.build_qodec(
    c4,
    strategy="bare-css/v1",
    strict=False,
)

report = ec.audit(protocol)
if report.errors or report.warnings:
    raise ValueError(str(report))

これをを実行する前に、qodec を監査します。 ランタイムは、qodec のチェック、読み出し値、フラグ、フレームの数式を信頼するため、無効な宣言を行うと誤った論理結果やデコーダー障害が発生する可能性があります。

このワークフローでは、qdk.simulation.run_qirとともにqodecを使用します。 qodec ツールは、個別のStimPython パッケージを使用して、Stim形式のガジェット回路を解析します。 qdk.stim.runは呼び出されません。 Linux Arm64 または WindowsArm64 で、ソースから Stim をインストールし、Python または Jupyter カーネルを再起動してから、Stim 形式の回線に対応する qodec を実行します。

qodec命令に一致するQIR呼び出し

QIR プログラムの各量子呼び出しを、qodecの最上位命令セット内の命令と照合します。 命令ニーモニックは、 QIR 呼び出し先名と完全に一致する必要があります。

次の表に、マッピングの例を示します。

ソース呼び出し 必要な qodec ニーモニック
Q# X(q) または OpenQASMx q; __quantum__qis__x__body
Q# H(q) または OpenQASMh q; __quantum__qis__h__body
Q# CNOT(a, b) または OpenQASMcx a, b; __quantum__qis__cx__body
Q# Adjoint S(q) または OpenQASMsdg q; __quantum__qis__s__adj
Q# M(q) または OpenQASMc = measure q; __quantum__qis__m__body
Q# の intrinsic 操作 Foo Foo
foo という名前の OpenQASM の intrinsic ゲートまたは def foo

QIR 引数は、次の順序で qodec 命令要素にバインドされます。

  1. 量子ビット ポインタ引数は、ブロック オペランドにバインドされます。
  2. スカラー引数は命令パラメーターにバインドされます。
  3. 結果ポインター引数は、命令の結果と返されたフラグを受け取ります。

量子呼び出しが上位命令セットの命令と一致しない場合、コンパイラはエラーを発生させます。

Q# で論理命令を宣言する

qodec コードから構築された C4 は、prepare_z_all、measure_z_all、syndrome、cx_all などのカスタム命令を公開します。 同じ名前の Q# の intrinsic 操作を宣言します。

カスタム測定操作で必要になるため、アダプティブ ターゲット プロファイルを使用して Q# を初期化します。

from qdk import TargetProfile, qsharp

qsharp.init(target_profile=TargetProfile.Adaptive_RI)

C4命令をQ#操作として宣言します。

qsharp.eval("""
@Measurement()
operation prepare_z_all(block : Qubit) : Result { body intrinsic; }

@Measurement()
operation prepare_x_all(block : Qubit) : Result { body intrinsic; }

@Measurement()
operation measure_z_all(block : Qubit) : (Result, Result) { body intrinsic; }

@Measurement()
operation measure_x_all(block : Qubit) : (Result, Result) { body intrinsic; }

operation cx_all(control : Qubit, target : Qubit) : Unit { body intrinsic; }
operation syndrome(block : Qubit) : Unit { body intrinsic; }
""")

このプログラムでは、各 Q#Qubit は 1 つのエンコードされたブロックを表します。 C4 ブロックには 2 つの論理量子ビットがあるため、各測定操作は 2 つの論理結果を返します。 各準備操作では、1 つの拒否フラグが返されます。

論理 Q# 実験をコンパイルする

状態の準備と測定 (SPAM) 実験を定義します。

qsharp.eval("""
operation TestSpamZ() : (Result[], Result[]) {
    use block = Qubit();
    let reject = prepare_z_all(block);
    let (left, right) = measure_z_all(block);
    ([reject], [left, right])
}
""")

TestSpamZ 操作を QIR にコンパイルします。

qir = str(qsharp.compile("TestSpamZ()"))

QIRは、prepare_z_allとmeasure_z_allへの呼び出しを保持します。 プログラムを実行すると、 qodec ランタイムはこれらの名前を最上位の命令セットと照合し、各論理呼び出しをそのガジェットに置き換えます。

組み込みのデコーダーを使用して実行する

run_qirを呼び出し、qodecを渡します。 decoderがNoneされている場合、ランタイムは組み込みの最小重みPauliデコーダーを使用します。

from qdk.simulation import run_qir

results = run_qir(
    qir,
    qodec=protocol,
    type="stabilizer",
    shots=100,
    seed=42,
)

print(results[:5])

qodec を実行するには、次のシミュレーター オプションを使用します。

type 値 Behavior
None または "stabilizer" スタビライザー バックエンドを使用する
"cpu" サポートされているプログラムで状態ベクトル バックエンドを使用する
"gpu" qodec ではサポートされていません

ランタイムは、最初の命令がそのブロックを作成する場合を除き、最初の使用前に各プログラム量子ビットを準備します。 準備命令には出力ブロックがありますが、入力ブロックはありません。また、論理 Z を安定させるアクションがあります。

シングル量子ビット Pauli デコーダーの修正と qodec フレームの更新では、ノイズのない Pauli フレームが使用され、ノイズのある物理ゲートではありません。

シミュレーターのノイズを設定する

ノイズは、qodec 命令名ではなく、シミュレーター操作に対して定義します。 ランタイムは各ボトムレイヤー命令を 1 つ以上のシミュレーター操作に変換するため、これらの操作にはノイズ設定が適用されます。

C4チュートリアルでは、次のマッピングを使用します。

下位レイヤーの命令 シミュレーター操作 NoiseConfig フィールド
R Z+ に準備、またはリセットする mresetz
H Hadamard h
CX 制御された X cx
M Z 基準測定 mresetz

単純な 1% ノイズ モデルを作成:

from qdk.simulation import NoiseConfig

noise = NoiseConfig()
for gate in (noise.h, noise.cx):
    gate.set_depolarizing(0.01)
noise.mresetz.x = 0.01

ノイズありで実験を実行します:

results = run_qir(
    qir,
    qodec=protocol,
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

mresetz のノイズは、測定後または準備後に量子ビットに作用します。 これによって、シミュレーターがすでに記録した測定ビットが反転されることはありません。 記録されたビット フリップを調査するには、 qodec 読み取りフォールト モデルとガジェット分析を使用します。

コントロールショットの失敗

on_shot_failure を使用して、受け入れられたショットを qodec フラグ、デコーダー、または読み出しで生成することができない場合に、ランタイムがどのように応答するかを選択します。

Value Behavior
"discard" 返されるリストから失敗したショットを省略します。 この値が既定値です。
"raise" 最初のショットが失敗したら停止します。
"retry" 失敗したショットを再起動し、最大 max_retries 回追加で再試行します。

"discard" ポリシーでは、shots引数は試行されたショットをカウントするため、返されるリストのアイテム数はshotsよりも少なくなります。

フラグは、論理プログラムがそのフラグを受け取らない場合にのみ、ショット ポリシーをトリガーします。 Q# @Measurement()操作がフラグ用の追加のResultを返した場合、プログラムはフラグを受け取り、ショットを拒否するかどうかを決定します。

TestSpamZの例では、プログラムは最初の配列内の準備フラグを返します。 そのため、on_shot_failure="discard" 設定では、返されたフラグが One の行は削除されません。

デコーダーを選択

エラーのデコード方法を選択するには、decoder パラメーターを使用して、エンコードされた各レイヤーに対してデコーダー準備用の callable を渡します。

QDKには、次のデコーダーがあります。

デコーダ この用途:
デフォルトシンドロームデコーダー シンドロームの証拠から最小重みの Pauli 補正を推論します。 decoder=None を使用してください。
フレームデコーダー 物理的な障害を推測することなく、ノイズのないフレームを追跡します。 prepare_frame_decoder を使用してください。
deq デコーダー deqを使用して回路レベルの障害モデルをデコードします。 prepare_deq_decoder を使用してください。

既定の deq 構成を使用する

deq と deq-runtime をインストールして、prepare_deq_decoder を渡します。

from qdk.simulation.decoders import prepare_deq_decoder

results = run_qir(
    qir,
    qodec=protocol,
    decoder=prepare_deq_decoder,
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

既定では、 deq アダプターはリレー信念伝達とウィンドウ コーディネーターを使用します。 これらの設定を変更するには、ランタイム ファクトリを使用します。

モノリシック deq ランタイムを構成する

deq設定を直接選択するには、prepare_deq_decoderにランタイム ファクトリを渡します。

from functools import partial
from deq.runtime import Runtime
from qdk.simulation import decoders


def make_runtime(seed: int) -> Runtime:
    return Runtime(
        decoder="black-box-relay-bp",
        decoder_config={"parallel": 1, "seed": seed},
        coordinator="monolithic",
        controller="jit",
    )


results = run_qir(
    qir,
    qodec=protocol,
    decoder=partial(
        decoders.prepare_deq_decoder,
        runtime_factory=make_runtime,
    ),
    noise=noise,
    type="stabilizer",
    shots=1000,
    seed=42,
    on_shot_failure="discard",
)

各ファクトリ呼び出しから新しい Runtime を返します。 QDK は、各ランタイムで開始および終了します。

Note

deq アダプターには次のものが必要です。

  • 1つのエンコード済みレイヤー
  • 測定に依存しない Clifford 実行トレース
  • スタビライザー バックエンド
  • 量子ビット損失のないノイズ モデル

deq アダプターは再試行ポリシーをサポートしていません。 サポートされていないプログラムでは、別のデコーダーにフォールバックするのではなく、エラーが発生します。

信頼度の低い deq 結果を拒否する

deq アダプターは、各論理読み出しのスコアを返します。 スコアが低いほど、選択した修正に対する選好が強いことを示します。 スコアは、校正された失敗の確率ではありません。

信頼度の低い結果を拒否するには、次の例のように、デコーダーを準備するときに max_readout_score を渡します。

from functools import partial
from qdk.simulation import decoders

scored_decoder = partial(
    decoders.prepare_deq_decoder,
    max_readout_score=0.2,
)

results = run_qir(
    qir,
    qodec=protocol,
    decoder=scored_decoder,
    noise=noise,
    type="stabilizer",
    shots=1000,
    on_shot_failure="discard",
)

ランタイムは、返されるすべての論理読み取りアウトにしきい値以下のスコアがある場合にのみ、ショットを受け入れます。 しきい値は、未加工フラグまたは未使用の中間読み出しには適用されません。

スコアベースの棄却は、受理されたショットの論理エラー率を下げることができます。 ただし、これはまた、採用されるショットの数を減らし、デコーダーの作業負荷も増やします。

OpenQASM プログラムをコンパイルする

標準OpenQASM操作では、標準QIR操作と同じQ#ニーモニック規則が使用されます。

from qdk import TargetProfile, openqasm

source = """
OPENQASM 3.0;
include "stdgates.inc";
qubit q;
bit c;
x q;
c = measure q;
"""

qir = openqasm.compile(
    source,
    target_profile=TargetProfile.Adaptive_RI,
)

print(qir)

qodecを使用してこのプログラムを実行するには、互換性のあるブロック オペランドと 1 つの測定結果を含む、qodec の最上位の命令セットに __quantum__qis__x__body と __quantum__qis__m__body が含まれていることを確認してください。

foo というニーモニックを使用する foo という名前の OpenQASM intrinsic ゲートまたはサブルーチンです。 量子ビット引数はブロック オペランドにバインドされます。 float、int、およびbool引数は、宣言順に命令パラメーターにバインドされます。

OpenQASM には、 qodec 呼び出しに関して次の制限があります。

  • カスタム組み込み関数は QIRResultを返すことはできません。 1 つの測定結果を取得するには、標準の measure 文を使用します。
  • OpenQASM プログラムでは、カスタム測定を実行したり、1 回の呼び出しから複数の結果を受け取ったり、返されたフラグを受け取ったりすることはできません。 このような場合は、 Q#@Measurement() 操作を使用します。
  • ランタイムは、OpenQASM プログラムが受信できないフラグを制御しており、これらのフラグによってショットが却下されることがあります。

マッピングの詳細な例については、次の QDK ノートブックを参照してください。

要件と制限事項を確認する

大規模な実験を実行する前に、これらの要件、制限事項、結果の違いを確認してください。

必要条件:

  • QIR 内のすべての量子操作の呼び出しは、最上位レイヤーの qodec 命令に対応付けられる必要があります。
  • プロトコルを実行する前に監査します。 qodec ランタイムは、ユーザーの記述した式を信頼し、それらを検証は行いません。

制限事項:

  • GPU シミュレーションでは、 qodec 実行はサポートされていません。
  • 既定のガジェット フォールト セットは指数関数的に増加する可能性があるため、正確な距離検索には時間がかかる場合があります。
  • deqパッケージとStim パッケージには、プラットフォームの制限があります。 詳細については、「QDK をインストールしてエラーを修正する」を参照してください。

結果の違い:

  • 正確な出力数はデコーダーによって異なる場合があります。
  • 破棄ポリシーと再試行ポリシーは、返される結果の分布を変更できます。

ノイズ スイープ、deq のデコード、信頼度ベースの却下を使用した完全な Q# の実験については、「QEC プロトコルの詳細手順」を参照してください。