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

Qamomile v0.14.0では、Pauli、Ising-Z、周期シフト向けのLCU block encodingと、量子特異値変換(QSVT)を行うqmc.qsvtを追加しました。また、定数加算などの量子算術回路を追加し、Shorの位数探索を論理量子ビット数を抑えた反復型へ刷新しました。さらに、Ekerå–Håstad法の量子部分を構築するqmc.ekera_hastad_factoringを追加しました。

pip install qamomile==0.14.0

破壊的変更

新機能

LCU block encodingとQSVT

qamomile.linalgに、次元が2のべき乗である一般の複素正方行列向けのPauliLCUと、1次元または多次元の周期シフト演算子向けのPeriodicShiftLCUを追加しました。対応するblock encoding APIとして、qmc.pauli_lcu_block_encodingqmc.periodic_shift_lcu_block_encodingを追加しました。このほか、Ising-Z、identity、既存の複数のblock encodingを組み合わせるAPIも追加しました。各APIはnormalization、signal register幅、system register幅、および再利用可能なunitary量子カーネルを持つインスタンスを返します。

qmc.qsvtは、呼び出し側が与えたprojector phase列を任意のLCUBlockEncodingに適用します。位相値はトランスパイル時のbindingsで固定できます。また、phase_countbindingsで固定し、parameters=["phases"]を指定すると、位相列をruntime parameterとして残せます。qmc.qsvtは、目的の多項式に対する位相の生成や、他のQSP conventionからの変換には対応していません。必要な位相列はユーザーが事前に用意する必要があります(#598#608#611#618)。

import numpy as np

import qamomile.circuit as qmc
from qamomile.linalg import PauliLCU
from qamomile.qiskit import QiskitTranspiler

@qmc.qkernel
def transform(
    encoding: qmc.LCUBlockEncoding,
    phases: qmc.Vector[qmc.Float],
) -> tuple[qmc.Vector[qmc.Bit], qmc.Vector[qmc.Bit]]:
    signal = qmc.qubit_array(encoding.num_signal_qubits, "signal")
    system = qmc.qubit_array(encoding.num_system_qubits, "system")
    signal, system = qmc.qsvt(signal, system, phases, encoding)
    return qmc.measure(signal), qmc.measure(system)

matrix = np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)
block_encoding = qmc.pauli_lcu_block_encoding(PauliLCU.from_matrix(matrix))

transpiler = QiskitTranspiler()
executable = transpiler.transpile(
    transform,
    bindings={
        "encoding": block_encoding,
        "phases": [0.0, 0.0],
    },
)

算術回路と量子ビット幅を抑えた素因数分解回路

qmc.stdlibに定数加算、制御付き定数加算、定数modular addition、量子レジスタをaddressとして表を参照し、対応する値を別の量子レジスタにXORする関数を追加しました。また、既存のqmc.shor_order_findingを更新し、modular multiplicationをwindowed方式に変更しました。

qmc.ekera_hastad_factoringは、同程度のビット長を持つ2つの素数の積に対して、Ekerå–Håstad法の量子計算部分を構築します。返り値は素因数ではなく、素因数を復元する古典計算へ渡す測定結果のビット列です。回路は測定後にresetした量子ビットを再利用することで、同時に使用する論理量子ビット数を抑えています。測定結果から素因数を求める古典的な後処理は含まれません(#613)。

import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler

order_finding = qmc.shor_order_finding(
    base=2,
    modulus=15,
    precision=2,
)
short_dlp = qmc.ekera_hastad_factoring(
    generator=2,
    modulus=21,
    window_size=1,
)

transpiler = QiskitTranspiler()
order_executable = transpiler.transpile(order_finding)
short_dlp_executable = transpiler.transpile(short_dlp)

チュートリアル05 — リソース推定も参照してください。

qmc.struct、固定長のBit配列、レジスタ幅の計算

qmc.structデコレータを使うと、ヘルパー関数で大規模なアルゴリズムを実装する際のコードを整理できます。ただし、量子カーネルの引数や返り値には使えません。qmc.bit_arrayは、ゼロで初期化した固定長の1次元Vector[Bit]を作り、測定結果をインデックスごとに格納して1つの値として返せます(#613#623)。

古典値演算としてqmc.log2qmc.ceilが追加されました。入力値が確定している場合は即座に計算され、未確定の場合はresource estimateでsymbolicなまま保持されます。回路を生成する前に、トランスパイル時のbindingsで指定する必要があります(#615)。

import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler

@qmc.struct
class Workspace:
    qubits: qmc.Vector[qmc.Qubit]

@qmc.qkernel
def collect_bits(size: qmc.UInt) -> qmc.Vector[qmc.Bit]:
    width = qmc.ceil(qmc.log2(size))
    workspace = Workspace(qmc.qubit_array(width, "workspace"))
    measured = qmc.measure(workspace.qubits)
    output = qmc.bit_array(width, name="output")
    for index in qmc.range(width):
        output[index] = measured[index]
    return output

transpiler = QiskitTranspiler()
executable = transpiler.transpile(
    collect_bits,
    bindings={"size": 9},
)

内部的な変更

量子カーネルのeffect情報

QKernel.effectsMEASUREMENTRESETFEED_FORWARDからなるKernelEffectのフラグ集合を返し、KernelEffect.NONEはunitaryな本体を表します。effectは入れ子のcallable definitionを通じて伝播し、serialization後も再構築されます(#613)。

import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler

@qmc.qkernel
def reset_and_read() -> tuple[qmc.Bit, qmc.Bit]:
    qubit = qmc.x(qmc.qubit("qubit"))
    qubit, before = qmc.measure_reset(qubit)
    after = qmc.measure(qubit)
    return before, after

effects = reset_and_read.effects
transpiler = QiskitTranspiler()
executable = transpiler.transpile(reset_and_read)

region dataflowの明示化

ifforwhileのstructured regionは、capture、carried value、引数、yieldを明示的に記録するようになりました。このinterfaceはProtobuf serialization後も保持され、lowering前に検証されます(#616)。

import qamomile.circuit as qmc
from qamomile.qiskit import QiskitTranspiler

@qmc.qkernel
def fill_register(size: qmc.UInt) -> qmc.Vector[qmc.Bit]:
    qubits = qmc.qubit_array(size, "qubits")
    for index in qmc.range(size):
        qubits[index] = qmc.x(qubits[index])
    return qmc.measure(qubits)

transpiler = QiskitTranspiler()
executable = transpiler.transpile(
    fill_register,
    bindings={"size": 3},
)

バグ修正

ドキュメント

さらに詳しく