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.12.7

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ソースと一致しない回路を生成していたプログラムを拒否するトランスパイル時のチェックを追加しました。以下の各パターンは、トランスパイル時に専用のエラーを送出します。

新機能

辞書の係数を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)]

バグ修正

ドキュメント

さらに詳しく