回路最適化

optimize() は3つののぞき穴的な書き換えを不動点まで 適用します。恒等なゲートの削除、隣接する逆ゲートの相殺、同じ軸まわりの 隣接回転の統合です。

from blueqat import Circuit
from blueqat.optimize import optimize

optimize(Circuit(2).h[0].x[1].h[0])              # Circuit(2).x[1]
optimize(Circuit(2).rz(0.3)[0].x[1].rz(0.4)[0])  # rz(0.7)[0] . x[1]
optimize(Circuit(2).cx[0, 1].cx[0, 1])           # 空

「隣接」とはその量子ビット上で隣接という意味です。上の2つの h は 間に別の量子ビットへの x があっても相殺します。

やらないこと

どの書き換えも大域位相まで含めてユニタリを厳密に保存します。そのため 回転を落とすのは、その回転が真に恒等になる周期の倍数のときだけです ( rx ・ ry ・ rz と2量子ビットのパウリ回転は 4*pi 、 p と exch は 2*pi )。前者は 2*pi では -I であり、 落とすと状態ベクトルの符号が黙って反転してしまいます。

ゲートの照合はターゲットの順序込みで行いますが、ターゲットについて対称な ゲートは順序を無視します。 cz[0, 1] は cz[1, 0] と相殺しますが、 cx[0, 1] と cx[1, 0] は恒等ではないので相殺しません。

角度が requires_grad の torch.Tensor である回転は、値が0でも 決して落としません。値が0でも勾配は0とは限らず、削除すると回路が 短くなるだけでなくオプティマイザから見えるものが変わってしまうため です。統合は問題なく、微分可能なまま保たれます。

バリア・測定・リセットは削除せず、それらを跨いだ並べ替えもしません。

ブロックとスライスは( flatten() と同じく) 先に展開されます。書き換えは個々のゲート適用に対して働くためです。

Exchange-Only回路

Exchange-Onlyスピン量子ビットで効いてくるコストはパルス数です。 最適化は2度効きます。1度目は論理回路の段階で、パルス列が生成される前に丸ごと 消えます。2度目はパルスの段階で、同じスピンペアへの連続パルスが融合します:

import blueqat.eo
from blueqat.optimize import optimize

logical = Circuit(2).x[0].x[0].cx[0, 1].cx[0, 1].h[1]

len(logical.run(backend='eo').ops)              # 65 パルス
len(optimize(logical).run(backend='eo').ops)    # 3

論理回路

そのまま

先に最適化

x x cx cx h

65

3

s s s s

4

0(パルス段で)

rz rz rz

3

1

h h

6

0

ここで注意したいのは、パルスの最適化では論理的に自明な列を消せないと いうことです。 h h は符号化された量子ビット上では恒等ですが、その6パルスは 物理空間全体では恒等ではありません。消せるのは論理段だけです。論理段を先に 走らせる理由がこれです。