Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Qamomile v0.15.0

破壊的変更

Clifford+T推定モードを廃止

今までのQamomileではリソース推定(QKernel.estimate_resources()等)でbasis=GateBasis.CLIFFORD_Tprecisionを指定すると、対応する論理演算をClifford+Tゲートへ変換する合成モデルに基づいたリソースを推定できました。一方で、現在のQamomileのアルゴリズム層へ注力する上でこのモードは一旦不必要だと判断し、v0.15.0ではこの変換モードを廃止しました。これに伴い、GateBasisqamomile.circuit.estimator.ResourceEstimatorConfig、各リソース推定APIのbasis引数とprecision引数を公開APIから削除しました。

測定とリセットを独立したリソースとして集計

これまで測定数とリセット数を個別に取得するフィールドはなく、リセットはgatesに含まれていました。v0.15.0からは、測定をmeasurements、リセットをresetsとしてそれぞれ独立して確認できます。

import qamomile.circuit as qmc


@qmc.qkernel
def measurement_and_reset() -> qmc.Bit:
    qubit = qmc.h(qmc.qubit("qubit"))
    qubit = qmc.reset(qubit)
    return qmc.measure(qubit)


resources = measurement_and_reset.estimate_resources()

assert resources.gates.total == 1
assert resources.measurements.total == 1
assert resources.resets.total == 1

多重制御の既定モデルをCLEAN_ANCILLA_TOFFOLIへ変更

これまで多重制御ゲートは、制御量子ビット数にかかわらず1つの抽象ゲートとして集計していました。v0.15.0からは、初期状態が|0>の補助量子ビットとToffoliゲートを使うリソース推定用の固定モデルで集計するため、ゲート数と必要量子ビット数が増える場合があります。多重制御を従来と同様に抽象ゲートとして集計するには、ControlDecomposition.ABSTRACTを指定します。

import qamomile.circuit as qmc


@qmc.qkernel
def double_controlled_h() -> qmc.Bit:
    controls = qmc.qubit_array(2, "controls")
    target = qmc.qubit("target")
    controls, target = qmc.control(qmc.h, num_controls=2)(controls, target)
    return qmc.measure(target)


default = double_controlled_h.estimate_resources()
abstract = double_controlled_h.estimate_resources(
    control_decomposition=qmc.ControlDecomposition.ABSTRACT,
)

assert default.gates.total == 3
assert default.gates.toffoli == 2
assert default.width.clean_ancilla_qubits == 1
assert abstract.gates.total == 1
assert abstract.width.clean_ancilla_qubits == 0

OracleのコストコールバックをOpaqueCostContextによる定義1回分の基礎コストへ変更

Oracleのcostに指定するコールバックの引数をOpaqueCallContextからOpaqueCostContextへ変更しました。コールバックは、Oracle自身が宣言した制御量子ビットを含む、定義を1回適用するための基礎コストを返します。qmc.control()で追加された制御やqmc.inverse()はリソース推定側で反映されるため、controlstransformなど呼び出し地点の情報を使ってコストを調整していたコールバックは、target_shapesdefinition_control_qubitstarget_qubitsなどを使う形に更新してください。

例えば、対象量子ビットごとに1つの1量子ビットゲートを使うOracleは、次のようにcostを定義できます。

import qamomile.circuit as qmc


def linear_gate_cost(
    context: qmc.OpaqueCostContext,
) -> qmc.ResourceEstimate:
    num_targets = context.target_qubits
    return qmc.ResourceEstimate(
        gates=qmc.GateResources(
            total=num_targets,
            single_qubit=num_targets,
        ),
        control_decomposition=context.control_decomposition,
    )


oracle = qmc.Oracle(
    "linear_gate_oracle",
    signature=qmc.CallableSignature(
        inputs=[qmc.Vector[qmc.Qubit]],
        outputs=[qmc.Vector[qmc.Qubit]],
    ),
    cost=linear_gate_cost,
)

EstimateQualityResourceEstimate.to_dict()の公開形式を刷新

EstimateQuality.UPPER_BOUNDEstimateQuality.MODELEDを削除し、EXACTCONSERVATIVE(過小評価しない)、UNKNOWNで、推定値が選択した回路モデルに対して持つ保証を表すようにしました。リソースモデルを使ったかどうかはEstimateDerivation、数学的な近似の有無はApproximationStatusとして分離しています。ResourceEstimate.to_dict()も、新しい集計項目に加えてderivationapproximationcontrol_decompositionrequirementsを含む形式へ変更しました。

Oracleの同一性判定と明示的シグネチャーの契約を変更

v0.15.0で追加されたTransformedOracleは、元のOracleと追加した制御、inverseの有無を組み合わせて比較します。同じ属性で別々に生成したOracleが同じ処理を表すとは限らないため、元のOracleはインスタンス単位で比較します。これにより、同じOracleへ同じ変換を適用したTransformedOracleは等しく、別のOracleから生成したものは等しくなりません。OracleTransformedOracleは、どちらも辞書のキーやsetの要素として利用できます。

signature=num_control_qubitsの双方に制御量子ビットを記述すると、Oracle自身が要求する制御とqmc.control()で追加される制御が二重管理になります。このため、signature=にはtargetのinput/outputだけを記述し、num_control_qubitsで宣言した制御はQamomileが内部signatureの先頭へ追加する契約に変更しました。arityや型が実際のtarget呼び出しと一致しないsignatureはValueErrorになります。制御量子ビットを持つOracleは、Vector形式ではなく、制御量子ビットをcontrols=へ渡すscalar形式で呼び出してください。

機能強化

回路幅・種類別深さ・推定根拠を追跡できるリソース推定

リソース推定では、同時に使用する最大量子ビット数を表すqubitsに加え、入力、すべての確保箇所、補助量子ビットを含む静的な回路幅をcircuit_qubitsで取得できるようになりました。深さは全体のdepthとは別にgate_depthmeasurement_depthreset_depthなどを確認できます。種類別の深さは回路全体の依存関係を保ったまま算出されるため、それぞれを足しても全体の深さにはなりません。

また、derivationで構造解析とモデル値を区別し、qualityapproximationassumptionsから、推定値の保証、数学的近似の有無、推定に使った前提を個別に確認できます。trace=Trueを指定すると、演算や量子カーネルごとの導出過程も保持できます。

例えば、前述のdouble_controlled_hでは、次のように各項目と導出過程へアクセスできます。

resources = double_controlled_h.estimate_resources(trace=True)

assert resources.derivation is qmc.EstimateDerivation.STRUCTURAL
assert resources.quality is qmc.EstimateQuality.CONSERVATIVE
assert resources.approximation is qmc.ApproximationStatus.EXACT

assumptions = [
    (assumption.message, assumption.source)
    for assumption in resources.assumptions
]

assert resources.trace is not None
trace_text = resources.trace.render()

Amazon Braketサポート

qamomile[braket]として、BraketTranspilerBraketExecutorBraketExecutionOptionsを追加しました。Qamomileの量子カーネルをAmazon BraketネイティブのCircuitへ変換し、実行時パラメータはFreeParameterとしてBraketのinputsへ渡せます。デバイスを省略するとLocalSimulatorを使用し、AwsDeviceを渡すと同じ実行APIでAWS上のデバイスを利用できます。

import math

import qamomile.circuit as qmc
from qamomile.braket import BraketTranspiler


@qmc.qkernel
def parameterized_bell(theta: qmc.Float) -> qmc.Vector[qmc.Bit]:
    qubits = qmc.qubit_array(2, "qubits")
    qubits[0] = qmc.ry(qubits[0], theta)
    qubits[0], qubits[1] = qmc.cx(qubits[0], qubits[1])
    return qmc.measure(qubits)


transpiler = BraketTranspiler()
executable = transpiler.transpile(parameterized_bell, parameters=["theta"])
sample = executable.sample(
    transpiler.executor(),
    shots=32,
    bindings={"theta": math.pi},
).result()

assert sample.results == [((1, 1), 32)]

サンプリングに加え、Hamiltonianの期待値計算ではExact()ShotBased(shots)を利用できます。現在のBraketTranspilerは静的なゲートモデル回路を対象としているため、測定結果に依存するifwhile、リセット、測定後に同じ量子ビットを再利用する途中測定は変換できません。Amazon Braketの一部デバイスが提供するexperimentalなdynamic circuit機能は、v0.15.0では対象としていません。また、QPU上のExact()と、デバイス種別を問わずTargetPrecisionはサポートしていません。課金を伴うtaskの意図しない再提出を避けるため、batch retryの既定値は0です。

エンジン共通の非同期実行・状態管理・送信済みジョブへの再接続に対応

Jobの基底クラスを、エンジン共通のExecutionHandleを保持する形へ拡張しました。result(timeout=...)result_async(timeout=...)、正規化されたstatus()、provider固有のraw_status()、キャンセル、metadata、native taskを共通APIから利用できます。qBraidでもジョブ送信後にprovider側の完了を待たずにJobを返し、同じライフサイクルで状態確認や結果取得を行えるようになりました。

ExecutionCapabilitiesでは、非同期実行、期待値計算、キャンセル、送信済みリモートジョブへの再接続、batch実行、実行精度など、executorが対応する機能を確認できます。期待値計算の精度はExact()ShotBased(shots)TargetPrecision(precision)で表し、ExecutableProgram.run(estimation=...)から実行ごとに指定できます。

再接続に対応するエンジンでは、job.snapshot()で認証情報を含まない参照を保存し、ExecutableProgram.restore()で同じ型の結果変換を備えたjobとして送信済みリモートジョブへ再接続できます。実行時のbindingsはsnapshotに含まれないため、再接続時に再度指定します。既存の同期executorは完了済みhandleとして扱われるため、従来のexecute()実装をそのまま利用できます。

また、result(timeout=...)は複数taskを含むjob全体の待機上限として扱われます。成功時の結果はcacheされ、Amazon Braketでは失敗もcacheされるため、同じjobのresult()を繰り返してもremote serviceへ不要な再問い合わせを行いません。

変換済みOracleにcontrolinverseを重ねて適用可能に

Oracleに対してqmc.inverse(oracle)qmc.control(qmc.inverse(oracle))qmc.inverse(qmc.control(oracle))、入れ子のqmc.control(...)を組み合わせられるようになりました。これらは公開型TransformedOracleとして表現され、open controlのpattern、inverse、固定リソースコストをserialization後も保持します。制御を追加できるのは固定幅のscalar-signature Oracleで、vector-signature Oracleへの制御は現在サポートしていません。また、num_controlsには具体的な正の整数を指定する必要があります。

Matrixを含む配列バインディングのshape・rank検証を強化

VectorMatrixTensorへ渡すlist、tuple、NumPy配列のshape検出を共通化しました。bindingsとして渡す配列では、配列が矩形であること、宣言したrankと一致すること、各要素が宣言型の値域に収まることを検証します。実行時パラメータでも共通のshape検出を利用し、raggedな配列や不正なshapeを一貫したValueErrorとして早い段階で報告します。

@qkernelの戻り値アノテーションの検証を強化

これまでも、@qkernelの戻り値についてスカラーと配列の別やPython tupleの要素数といった一部の構造は確認していましたが、BitQubitなどの値型、VectorMatrixTensorのrank、tuple要素の型と順序までは一貫して照合していませんでした。また、tuple[T]を宣言した量子カーネルでも、出力が1つの場合は呼び出し結果がT単体となり、宣言したtuple構造が保持されませんでした。

v0.15.0では、量子カーネルの型に基づく静的な契約チェックを強化し、戻り値アノテーションを実際に返す値の型と構造に照らしてbuild()時またはトランスパイル時に検証します。不一致はTypeErrorとして早い段階で報告され、戻り値なし、空tuple、スカラー、1要素のPython tupleも区別されます。このため、tuple[T]を宣言した量子カーネルの呼び出しは(value,)を返します。戻り値アノテーションを実際の値に合わせ、可変長のtuple[T, ...]は固定長tupleに、ネストしたPython tupleはqmc.Tupleに変更してください。

バグ修正

1量子ビットのGrover拡散演算子を修正

grover_search()に1量子ビットの検索レジスタを渡すと、拡散演算子が空の制御レジスタでmcxを構築するため、トランスパイルとリソース推定に失敗していました。1量子ビットの場合は、ゼロ制御の多重制御Zに相当するZゲートを直接適用するよう修正しました。これにより、1量子ビットの場合もリソース推定できるほか、各対応エンジンへトランスパイルして実行でき、マークされた状態の確率と期待値が理論値に一致します。

Trotter展開・測定・配列アクセスを含むリソース集計を修正

リソース推定で、Suzuki–Trotter展開をorderstep数に応じて集計し、測定結果に依存する分岐や配列要素ごとの依存関係、symbolicな添字・sliceの範囲を正しく追跡するよう修正しました。これにより、これらを含む量子カーネルでゲート数・幅・深さが不正確になったり、妥当な入力で推定できなかったりする問題を解消しました。

surface-code distanceと不正な数値入力の検証を修正

surface_code_estimate()でsurface-code distanceを3以上の奇数へ切り上げるよう修正し、non-Cliffordゲート数が0の回路も扱えるようにしました。論理リソース数について非数値・負値・非有限値を、物理モデル係数について非正値・非有限値を早い段階で拒否します。

Oracle・Grover・モジュラー算術のoverload・戻り値型・整数入力を修正

OracleVectorVectorView呼び出し、modmul_const()control有無、grover_iteration_count()の具体値・symbolic値に応じて、静的型検査が実際の戻り値型を正しく絞り込めるようoverloadを修正しました。Oraclenum_qubitsnum_control_qubitsではNumPy整数を受け入れてbool・float・負値を拒否し、grover_iteration_count()ではPython・NumPy整数を具体値として扱ってboolと0以下を拒否します。

未使用の実行時パラメータが要求される問題を修正

controlinverseを適用した量子カーネルで、回路に残らない実行時パラメータまで実行時に要求される問題を修正しました。実際に使うパラメータの順序は維持されます。

affine diagnosticに確保済みの量子ビット名を表示

確保時に名前を付けた量子ビットを再利用してQubitConsumedErrorになった場合、内部IDではなくqmc.qubit(...)に渡した名前を表示するよう修正しました。エラー箇所とソースコードの対応を把握しやすくなりました。

その他の変更