回路とゲート

回路の構築

Circuit は操作のリストを保持します。ゲートは属性、 量子ビットは [...] で選択し、パラメータ付きゲートはインデックスの前に 呼び出しでパラメータを渡します。すべてチェーンできます:

import math
from blueqat import Circuit

Circuit().h[0].cx[0, 1].rz(math.pi / 4)[1].m[:]

量子ビット0は常に状態ベクトルインデックスの最下位ビットです( '10' は 量子ビット1が1、量子ビット0が0。QiskitのStatevectorと同じ規約です)。

ゲートセット

1量子ビットゲート

i, x, y, z, h, s, sdg, t, tdg, sx, sxdg, phase(theta) (別名 p, r ), rx(theta), ry(theta), rz(theta), u(theta, phi, lam[, gamma]), mat1(matrix) (任意の2x2ユニタリ)。

2量子ビットゲート

cx (別名 cnot ), cy, cz, ch, swap, iswap, iswapdg, cphase(theta) (別名 cp, cr ), crx, cry, crz, cu(theta, phi, lam[, gamma]), rxx(theta), ryy(theta), rzz(theta), zz, zzdg, exch(theta) (ハイゼンベルク交換 パルス。Exchange-Onlyスピン量子ビット を参照)。

3量子ビットゲート

ccx (別名 toffoli ), ccz, cswap

その他の操作

m / measurem(key="name") でキー付き中間測定), reset, barrier

パラメータを取らないゲートにパラメータを渡すと ValueError になります (例: x(0.5)[0] は黙って無視されず拒否されます)。

回路の情報取得

c = Circuit(3).h[:].cx[0, 1].cx[1, 2].m[:]
c.n_qubits      # 3
c.depth()       # 4  (並列ゲートは1段と数える。barrierは数えない)
c.count_ops()   # Counter({'h': 3, 'cx': 2, 'measure': 3})

測定確率(微分可能・指定量子ビットへの周辺化に対応)とハミルトニアン 期待値:

from blueqat.utils import Z

Circuit(2).h[0].cx[0, 1].probs()          # tensor([0.5, 0., 0., 0.5])
Circuit(2).h[0].cx[0, 1].probs([1])       # 量子ビット1の周辺確率
Circuit(1).rx(0.4)[0].expect(1.0 * Z[0])  # <Z> = cos(0.4)

逆回路

dagger() はエルミート共役(ゲートを逆順に して共役化)を返します。測定とリセットには逆操作がないため例外になります が、 dagger(ignore_measurement=True) なら除去して続行します:

c = Circuit(3)  # ... 構築 ...
identity = c + c.dagger()   # |0...0> に逆計算で戻る

OpenQASM 2.0

qasm = Circuit(2).h[0].cx[0, 1].to_qasm()

from blueqat.circuit_funcs import from_qasm
c = from_qasm(qasm)

JSONシリアライズ

回路はバージョン付きのJSON互換スキーマでラウンドトリップできます (クラウド送信のワイヤ形式でもあります):

from blueqat.circuit_funcs.json_serializer import serialize, deserialize

data = serialize(Circuit(2).h[0].cx[0, 1])
c = deserialize(data)

回路描画

run(backend='draw') でmatplotlibによる回路図を描画します。登録済みの 全ゲートが描画可能で、未知の(ユーザー登録)ゲートは UserWarning 付き で省略されます。

名前付きゲートブロック

実際のアルゴリズムはサブルーチンの入れ子です — Shorの位数発見は 「初期化・制御剰余乗算・逆QFT」で構成され、それぞれがさらに小さな部品から できています。名前付きブロックはその構造を回路オブジェクトに保持したまま、 実行には一切影響しません(各バックエンドは内部のゲートを透過的に見ます):

c = Circuit(7)
with c.block("order-finding"):
    with c.block("superposition"):
        c.h[4, 5, 6]
    with c.block("c-U^1"):
        c.cswap[4, 2, 3].cswap[4, 1, 2].cswap[4, 0, 1]
    # ライブラリ回路をブロックとして配置 (量子ビット4..6へシフト)
    c.append_block("IQFT", qft_circuit(3).dagger(), offset=4)

print(c.tree())
# Circuit(7)
# └─ order-finding
#    ├─ superposition
#    │  └─ h[4, 5, 6]
#    ├─ c-U^1
#    │  └─ ...
#    └─ IQFT
#       └─ ...

ブロックは任意に入れ子でき、 repr()tree() に現れ、 dagger() では鏡像ブロック ( "order-finding†" )として保存されます。 depth() / count_ops() は中身のゲートを数え、 flatten() / JSONシリアライズはブロックを プレーンなゲート列に展開します(フラットなワイヤ形式には階層は残りません)。 回路描画ではブロックは関与する量子ビットにまたがる1つの名前付きの箱として 描かれます。回路全体が単一のブロックに包まれている場合は自動でその中に 降り、子ブロックが箱として表示されます。 expand_blocks=n で n 階層 ぶん展開、 expand_blocks=True で全ゲートまで展開できます。 完全なプログラム例は examples/shor_15.py を参照してください。

アンシラ量子ビット

c = Circuit(4).h[:]
with c.ancilla() as a:        # 新しい量子ビットを確保
    c.cx[0, a[0]]
    c.cx[0, a[0]]
# ブロックを出るとアンシラは |0> にリセットされます (reset=True がデフォルト)

マクロとカスタムゲート

関数を回路メソッドとして、あるいはゲートクラスをゲートセットに登録できます:

from blueqat import BlueqatGlobalSetting
from blueqat.decorators import circuitmacro

@circuitmacro
def bell(c, a, b):
    return c.h[a].cx[a, b]

Circuit(2).bell(0, 1)

BlueqatGlobalSetting.register_gate('mygate', MyGateClass)