Qamomile v0.12.7では、係数のPython辞書を@qkernelのruntime parameterとして渡し、量子カーネル内でループ変数によって添字参照できるようになりました。angles[i]のように量子カーネル内で記述でき、実行ごとに別の値を指定し直せます。あわせてUIntハンドルに%演算子を追加しました。また本リリースでは、これまでトランスパイルは通るのに誤った回路を生成していたいくつかの書き方、たとえば分岐内で以前の量子ビットをconsumeしないまま変数へ新しい量子ビットを割り当てる、同じVector[Qubit]を量子カーネル呼び出しの2つの引数に渡す、といったものを、トランスパイル時にそれぞれ専用のエラーで拒否するようにしました。制御化したqmc.pauli_evolveをQURI Partsでも使えるようにしました。またHamiltonianの定数項の大域位相を正しく保持するように修正し、定数項を持つHamiltonianの制御化した発展がQiskit・QURI Parts・CUDA-Qのいずれでも正しく動くようになりました。
pip install qamomile==0.12.7破壊的変更¶
本リリースでは、これまでトランスパイルは通るもののPythonソースと一致しない回路を生成していたプログラムを拒否するトランスパイル時のチェックを追加しました。以下の各パターンは、トランスパイル時に専用のエラーを送出します。
分岐やループ本体で、以前の量子ビットをconsumeしないまま変数へ新しい量子ビットを割り当てると
QubitRebindErrorを送出します。 測定結果に基づくifやfor/whileの本体でq = qmc.qubit(...)と書くと、以前はトランスパイルが通り、qの以前の状態が黙って失われていました(#561)。@qmc.qkernel def kernel() -> qmc.Bit: q = qmc.qubit("q") p = qmc.qubit("p") cond = qmc.measure(p) if cond: q = qmc.qubit("fresh") # QubitRebindError: qの以前の状態をconsumeしていない return qmc.measure(q)ループの反復をまたいで古典変数を更新すると
ValidationErrorを送出します。@qkernelのfor本体は一度しかトレースしないため、total = total + iのような書き方では、右辺がループ前の値に固定されるという不具合が発生していました(#552)。ループ内で同じ配列の別要素を読む古典配列要素の代入は
ValidationErrorを送出します。 ループ内でarr[i] = arr[i - 1]のように書くと、以前は各反復がarrのループ前の値を読み、それ以前の反復での書き込みを反映しないまま、エラーも出さずに誤った回路になっていました(#545)。多次元のQubit配列の確保を事前に拒否するようになりました。
qmc.qubit_array((2, 3), ...)などのrank2以上の量子shapeは、以前は受理してしまい、その後で誤った回路を生成したり後段でクラッシュしたりしていました。今は確保時にNotImplementedErrorを送出します。古典のMatrix/Tensor引数は影響しません(#558)。型やshapeの合わない量子カーネル引数を渡すと
TypeErrorを送出します。 スカラーのQubitをVector[Qubit]パラメータに渡すと、以前は無関係なAttributeErrorが漏れ出ていました。またQubitをFloatパラメータに渡すと黙ってrx(0)を生成していました。今は通常の量子カーネル呼び出し・qmc.control・qmc.inverseのいずれの呼び出し箇所でも、宣言した型に照らして引数を検証し、合わなければエラーを送出します(#473)。通常の
intが必要な箇所にPythonのboolを渡すとTypeErrorを送出します。boolはintのサブクラスであるため、q[True]、q[False:2]、qmc.uint(True)は以前は1/0として黙って受理していました。配列の添字、slice境界、qmc.uintではboolを拒否します(#539)。同じQubit配列を量子カーネル呼び出しの2つの引数に渡すと
QubitConsumedErrorを送出します。sub(qs, qs)、または重なり合う2つのviewを渡すと、以前は両方のパラメータが同じ物理量子ビットに写像され、誤った回路を生成するか、量子プログラミングSDKが出す生のduplicate qubitエラーとして後から表面化していました。今は呼び出し箇所でaliasを拒否します(#543)。
新機能¶
辞書の係数をruntime parameterとして扱う¶
Dict[K, Float]のカーネル引数をruntime parameterとして保持し、カーネル内でキーによって添字参照できるようになりました。qmc.range / qmc.itemsのループ変数によるアクセスも可能です。参照した各キーは、生成回路の"<dict>[<key>]"という名前のruntime parameter(例: angles[0]、coeffs[(0, 1)])になるため、同じトランスパイル済みexecutableに対して、実行ごとに別の辞書値を指定し直せます。引数をparameters=[...]に列挙して宣言します。symbolicなキー要素はUIntハンドルである必要があり、コンパイル時にbindした辞書に対して完全に定数のキーはコンパイル時に定数へ畳み込みます(#556、#562)。
import numpy as np
import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def rx_by_dict(n: qmc.UInt, angles: qmc.Dict[qmc.UInt, qmc.Float]) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(n, name="q")
for i in qmc.range(n):
q[i] = qmc.rx(q[i], angle=angles[i]) # runtime parameterの辞書を添字で参照する
return qmc.measure(q)
transpiler = QiskitTranspiler()
executable = transpiler.transpile(rx_by_dict, bindings={"n": 3}, parameters=["angles"])
# executable.parameter_names -> ['angles[0]', 'angles[1]', 'angles[2]']
# 実行ごとに辞書値を指定し直す:
# executable.sample(transpiler.executor(), shots=256,
# bindings={"angles": {0: np.pi, 1: 0.0, 2: np.pi}})UIntのmodulo演算子¶
UIntハンドルが%演算子(およびint % UInt)をサポートしました。これによりqmc.rangeループ内のi % 2のような添字式が、以前はトレース時にTypeErrorになっていましたが、直接トランスパイルできるようになりました(#466)。
import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def even_indices() -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(4, name="q")
for i in qmc.range(4):
if i % 2 == 0: # ループ変数でmoduloを使う
q[i] = qmc.x(q[i])
return qmc.measure(q)
transpiler = QiskitTranspiler()
executable = transpiler.transpile(even_indices)[((1, 0, 1, 0), 256)]qmc.pauli_evolveがtargetより小さいHamiltonianを受け付ける¶
qmc.pauli_evolveが、targetのVector[Qubit]より少ない量子ビットに作用するHamiltonianを受け付けるようになりました。各Pauli項は配列要素を位置で指し(添字iの項はq[i]に作用)、Hamiltonianのサイズを超える量子ビットは恒等で発展します。targetより多い量子ビットに作用するHamiltonianは、トランスパイル時に明確なEmitErrorで失敗します(#471)。
import qamomile.circuit as qmc
import qamomile.observable as qm_o
from qamomile.qiskit import QiskitTranspiler
@qmc.qkernel
def evolve(h: qmc.Observable, t: qmc.Float) -> qmc.Vector[qmc.Bit]:
q = qmc.qubit_array(2, name="q")
q = qmc.h(q)
q = qmc.pauli_evolve(q, h, t) # hは1量子ビットに作用し、q[1]は恒等で発展する
return qmc.measure(q)
transpiler = QiskitTranspiler()
executable = transpiler.transpile(evolve, bindings={"h": qm_o.Z(0), "t": 0.5})内部的な変更¶
Hamiltonianのserialization round-trip¶
IR serializerがbind済みのHamiltonianを扱えるようになりました。演算子データを埋め込んだコンパイル済みBlock、たとえばPauli HamiltonianのリストにbindしたVector[Observable]引数をParamSlot.bound_valueに保持しているものは、以前はdump_json / load_json(およびmsgpackの対)を通すと黙って情報を落としていましたが、本リリースで各Hamiltonianを同一のまま復元できるようになりました(#475、#548)。
import qamomile.circuit as qmc
import qamomile.observable as qm_o
from qamomile.circuit.transpiler.passes.inline import InlinePass
from qamomile.circuit.ir.serialize import dump_json, load_json
@qmc.qkernel
def trotter(q: qmc.Vector[qmc.Qubit], hs: qmc.Vector[qmc.Observable], t: qmc.Float) -> qmc.Vector[qmc.Bit]:
for i in qmc.range(hs.shape[0]):
q = qmc.pauli_evolve(q, hs[i], t)
return qmc.measure(q)
hamiltonians = [1.2 * qm_o.Z(0), 0.8 * qm_o.X(0)]
block = InlinePass().run(trotter.build(hs=hamiltonians, parameters=["t"]))
restored = load_json(dump_json(block))
slot = next(s for s in restored.param_slots if s.name == "hs")
print(slot.bound_value)[Hamiltonian((Z0,): 1.2), Hamiltonian((X0,): 0.8)]バグ修正¶
制御化した
qmc.pauli_evolveがHamiltonianの定数項の大域位相を落とさなくなりました。 Hamiltonianの定数(恒等)項は大域位相exp(-i·gamma·c)に寄与しますが、今までemitterはこれを落としていました。単体の発展では観測できませんが、qmc.control下ではこの位相が制御が有効な部分空間での実際の相対位相になるため、定数項を持つHamiltonianの制御化した発展が誤った結果を生んでいました。定数をQiskit(#471、#547)とCUDA-Q(#540)で制御下でも再適用するようになり、共有の制御化loweringにより制御化したpauli_evolveをQURI Partsでもトランスパイルできるようになりました(#537)。実数でない定数は明確なEmitErrorを送出します。Vector[Qubit]全体を対象とする制御化した量子カーネルがすべての量子プログラミングSDKでemitできるようになりました。 共有の制御化Uフォールバックは以前、内側の各ゲートを単一のtargetに割り当て、複数targetの内側ブロックや入れ子の制御化U操作を拒否していました。内側の各量子ビットを呼び出し側のtargetへ写像するようになり、制御化した多量子ビットの量子カーネル(modular算術のシフトなど)がQiskitと同様にQURI PartsとCUDA-Qでもトランスパイルできます(#472)。symbolicな配列添字を制御量子ビットに持つ制御化ゲートが正しい配線を使うようになりました。 制御がbindingで決まる配列要素(
q[control_index])のとき、新しい量子ビットを確保するのではなく、qubit mapを通じて物理量子ビットを解決するようになりました。以前は制御の位置がずれ、黙って誤った回路を生成していました(#541)。Möttönenの振幅エンコーディング / 逆状態準備のwrapperが量子ビットを正しく配線します。 逆状態準備wrapperの量子ビット解決の不具合が、誤った量子ビット配線を生んでいました(#465)。
ループunroll中の範囲外の配列添字を即座に失敗させます。 ループのunroll中に範囲外またはshapeの合わない配列添字に出会うと、黙って誤った回路を生成するのではなく、明確な
EmitErrorを送出します(#477)。canonicalizeが古典パラメータのmanifestを保持します。 canonicalizeは以前Block.param_slotsを落としていましたが、これを保持するようになり、canonicalizeしたBlockのcontent hashやserializationがパラメータ契約を維持します(#476)。古典配列の要素からQubit配列のサイズを決める場合も正しく解決します。
qmc.qubit_array(sizes[i], ...)のようにQubit配列の本数を古典配列(Vector[UInt])の要素から決めるとき、以前はサイズを取り違えて誤った本数の量子ビットを確保することがありました。今はスライスや添字を経由した要素も正しくサイズとして解決し、サイズ0の配列も維持します。負のサイズや未対応の負の添字などの不正な指定は、明確なエラーで拒否します(#456)。qmc.castで作ったQFixed値が、IR変換を通しても正しい量子ビットを指し続けます。qmc.cast(q, qmc.QFixed, ...)はVector[Qubit]を固定小数点として読み替え、そのcastはどの量子ビットを束ねているかを内部で覚えています。以前はカーネルのinlineやcanonicalize、コンパイル時ifの解決を通すとこの対応付けを更新できず、別の量子ビットを指してしまうことがありました。今はこれらの変換を通しても正しい量子ビットを指し続けます(#468)。runtime parameterからループ回数を決めようとした場合に、明確なエラーを出します。
qmc.range(...)の回数は、ループをunrollするためにコンパイル時に確定している必要があります。実行時にしか値が決まらないruntime parameter(やその配列要素)を回数に使うと、以前は分かりにくい別のエラーで失敗していました。今は「その値をbindingsで与えてください」と促す明確なエラーを、トランスパイルの早い段階で出します(#557)。
ドキュメント¶
多次元量子フーリエ変換によるナノシート材料物性の推定 — 多次元QFTを材料科学の推定問題に適用する新しいアルゴリズム記事です(#405)。
CUDA-Q Support — QamomileのカーネルをCUDA-Q SDKでトランスパイル・実行するための新しい統合ページです(#440)。
Qiskit Support — Qiskit SDK向けの新しい統合ページです(#464)。
ドキュメントサイトを再設計し、ランディングページとカードのメタデータを刷新し、新しいインストールガイドを追加しました(#461)。