Qamomile v0.12.6では、回路の逆操作を作るqmc.inverseを追加しました。ビルトインゲート、@qkernel全体、QFT/IQFTをそのまま逆操作に変換でき、得られた逆操作は元と同じように呼び出せます。また、計算基底のレジスタを2^nを法として±1シフトする算術プリミティブqmc.modular_increment / qmc.modular_decrementを追加しました。さらに、sample() / run()実行まわりのjob型・結果型はqamomile.circuitから直接importできるようになりました。不具合他王として、runtime parameterの式をゲート角度に使ったときのトランスパイル、bindingsで与えたVector要素の値解決、qmc.expvalが参照する量子ビットの対応付け、最適化converterのHUBOインスタンス対応もしています。
pip install qamomile==0.12.6新機能¶
qmc.inverseによる回路の逆操作¶
qmc.inverse(target)は、ビルトインゲート関数、@qkernel、stdlibのQFT関数の逆操作を返します。固定ゲートは対応する随伴ゲートになり(qmc.inverse(qmc.s)はqmc.sdg、qmc.hやqmc.cxのような自己逆ゲートはそれ自身)、回転ゲート(rx、ry、rz、p、cp、rzz)は角度の符号が反転します。qmc.inverse(qmc.qft)はqmc.iqftをそのまま返すため、composite gateをネイティブに出力できるSDKではその経路が維持されます。Vector[Qubit]へのbroadcastも順方向のゲートと同じように動きます(#445)。
@qkernelに対しては、元の量子カーネルと同じ引数で呼び出せるwrapperが返り、本体を逆順にしつつ各操作を逆にして適用します。入れ子の量子カーネル呼び出し、composite gate、qmc.control(...)の結果、qmc.pauli_evolve(発展時間の符号を反転)、qmc.rangeループ(unrollせずに反復順を逆転)も再帰的に逆変換されます。qmc.inverse(...)を呼ぶ量子カーネル自体を逆変換すると、入れ子のinverseは打ち消されて元の操作列に戻ります。
import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def layer(q: qmc.Qubit, angle: qmc.Float) -> qmc.Qubit:
q = qmc.h(q)
q = qmc.rz(q, angle)
return q
@qmc.qkernel
def roundtrip(angle: qmc.Float) -> qmc.Bit:
q = qmc.qubit("q")
q = layer(q, angle)
q = qmc.inverse(layer)(q, angle) # uncomputeでqは|0>に戻る
return qmc.measure(q)
executable = QiskitTranspiler().transpile(roundtrip, bindings={"angle": 0.37})if / while / for ... in qmc.items(...)の制御フローを含む量子カーネルや、inverse wrapperのトレース時点でループ範囲がsymbolicなままの量子カーネルはサポートしておらず、NotImplementedErrorになります(bindingsで与えたループ範囲は問題なく解決されます)。内部で量子ビットを確保する量子カーネルも拒否されます。
modular incrementとmodular decrement¶
qmc.modular_incrementとqmc.modular_decrementは、little-endianの量子ビットレジスタ(q[0]が最下位ビット)に|j> -> |j ± 1 mod 2^n>を適用します。構成はQFTを使わず、Xゲートとmulti-controlled Xゲートだけで組み立てられています。制御版は専用APIではなく、qmc.control(qmc.modular_increment, num_controls=1)のように既存の制御機構との組み合わせで作ります。どちらの量子カーネルもqamomile.circuit.algorithm.arithmeticからimportできます(#453)。
import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def plus_one(n: qmc.UInt) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(n, name="q")
q[1] = qmc.x(q[1]) # |j=2>を準備(little-endian)
q = qmc.modular_increment(q) # |j=2> -> |j=3>
return qmc.measure(q)
executable = QiskitTranspiler().transpile(plus_one, bindings={"n": 3})QURI Partsでのサポートは現状部分的で、通常のシフトは2量子ビット以下のレジスタ、制御版は1量子ビットのレジスタに限られます。
qamomile.circuit配下のjob型・結果型¶
SampleResult、SampleJob、RunJob、ExpvalJob、Job、JobStatusをqamomile.circuitから再エクスポートしました。これらはsample() / run()実行まわりの型です。executable.sample()はSampleJobを返し、その.result()がSampleResultになります。executable.run()はRunJobまたはExpvalJobを返し、Job / JobStatusは共通の基底クラスとステータスのenumです。qmc.SampleResultのような型注釈のために内部パスqamomile.circuit.transpiler.jobをimportする必要はなくなりました。内部パスも後方互換性のため引き続きimportできます(#407)。
import qamomile.circuit as qmc
def best_bitstring(result: qmc.SampleResult) -> tuple[int, ...]:
"""最も多くサンプルされた測定結果を返す。"""
((value, _count),) = result.most_common(1)
return valueバグ修正¶
runtime parameterの式をゲート角度に使っても、誤った
MultipleQuantumSegmentsErrorにならなくなりました。測定に依存する制御フローを持たない純粋な量子カーネルで、qmc.rx(q, -phase)、symbolicなmulti-control phaseへのtheta=-phase、(phase * 2) - 1のような連鎖した式をゲート角度に使うと、トランスパイル時にプログラムが複数のquantum segmentへ誤って分割されていました。これらの式はquantum segmentの内側に保持され、emit時に各SDKのparameter式へ畳み込まれるようになりました(#455)。bindingsで与えたVector要素の値がemit時に解決されるようになりました。bindingsで与えたVectorの要素やスライスビューの要素を、ゲート角度(手書きのcontrolled-RY半角パターン-angle[i] / 2など)、ループ範囲、補助量子カーネルの引数、controlled QPEのパラメータに使うと、これまでemit時に値を解決できず失敗していました。QPEを個別ゲートへ分解する経路でも、こうした配列要素から位相を取り出せませんでした。いずれもルートの配列まで遡って解決されるようになり、runtime parameterの配列やsymbolicなindexはsymbolicなまま保持されます(#454、#457、#458)。より多くのレジスタ配置で
qmc.expvalが正しい量子ビットを参照するようになりました。タプル形式のqmc.expval((q[i],), obs)は、inlineされた量子カーネルを経由しても呼び出し元レジスタとの対応を保つようになりました(#448)。また、Vector全体に対するobservableのPauli indexは、レジスタが物理量子ビット0から始まらない配置でも正しい量子ビットへ対応付けられるようになりました(#449)。パラメータ化したスライスへのcontrolled composite gateがQiskitで実行できるようになりました。古典の
UInt幅を受け取りq[:m]スライスにQFTを適用する量子カーネルをqmc.control(...)すると、これまでEmitErrorになっていました。入れ子のブロックをゲートへ変換できるSDK(Qiskit)では実行できるようになり、その変換ができずゲート単位の合成に回る場合は引き続き拒否されます(#453)。最適化converterがHUBOの
ommx.v1.Instance入力を受け付けるようになりました。問題の正規化がto_hubo()経由で変換するようになったため、3次以上の項を持つ目的関数も、分かりにくいRuntimeErrorで失敗せずにHUBO対応のconverter(QAOAConverterなど)へ渡るようになりました。HUBO非対応のconverterは明確なValueErrorを送出します(#459)。
ドキュメント¶
チュートリアル04 — 制御ゲートでは、却下パターンの節を「rejectされるパターンとedge case」として再構成しました。
UIntスライスを使う量子カーネルへのcontrolled QFTは、EmitErrorを期待するケースではなく、Qiskitで実行されるサポート例になりました(#453)。量子誤り訂正入門では、
SampleResultを公開パスのqamomile.circuitからimportするようになりました(#407)。