QISKit: コンパイラーが空気を読んでくれる時くれない時

目次

環境

  • OS: ArchLinux
  • Python 3.6.5
  • QISKit 0.5.4

QISKit を使う利点

IBM量子コンピュータ を QISKit 経由で使うことの利点は、実機の物理的な制限を(多少)意識しなくても良いことだったり、Toffoli ゲート (ccx) や SWAP ゲート を実機でも使えることなどだと思う。*1

例えば、5 qubit の量子コンピュータである ibmqx4 の場合、CNOT をかけられる方向は下図のようである。

(IBM Q 5 Tenerife V1.x.x Version Log より。矢印の始点が controlled qubit 、終点が target qubit を表す。)

この図からわかるように、ibmqx4 の場合 { \mathrm{CNOT}_{01}  } を作用させることはできない*2。 そのため、Composer でそのような CNOT ゲート を置こうとすると "You can't drop the gate there" と言われてしまう。

f:id:goropikarikun:20180621121411p:plain

一方で QISKit を経由すると、 { \mathrm{CNOT}_{01}  } を含んでいても実行できる。

これがなぜできるかというと、実機で実行される前に ibmqx4 でも実行できるような等価な回路に変換されるためである。

QISKit を使った場合の CNOT ゲート

q = QuantumRegister(2)
c = ClassicalRegister(2)
qc = QuantumCircuit(q,c)

qc.cx(q[0], q[1])

qc.measure(q, c)
circuit_drawer(qc)

backend = 'ibmqx4'
job_exp = execute(qc, backend=backend, shots=1024, max_credits=3)

lapse = 0
interval = 10
while not job_exp.done:
    print('Status @ {} seconds'.format(interval * lapse))
    print(job_exp.status)
    time.sleep(interval)
    lapse += 1
print(job_exp.status)

plot_histogram(job_exp.result().get_counts(qc))

backend = 'ibmqx4'
circuit = qiskit.compile(qc, backend=backend)   
print(circuit['circuits'][0]['compiled_circuit_qasm'])  
circuit_drawer(load_qasm_string(circuit['circuits'][0]['compiled_circuit_qasm']))

f:id:goropikarikun:20180621142025p:plain
コンパイル前の回路図
f:id:goropikarikun:20180621142029p:plain
f:id:goropikarikun:20180621142034p:plain
コンパイル後の回路図

\begin{align} \mathrm{CNOT}_{01} &= (H \otimes H) \mathrm{CNOT}_{10} (H \otimes H) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} \end{align} f:id:goropikarikun:20180621194434p:plain
の関係を使っていい感じに変換してくれる。

( { \mathrm{CNOT}_{13}  } のように直接つながっていないものは QISKit 使ってもよしなにしてくれないけれども。。。)

QISKit を使った場合の Toffoli ゲート

q = QuantumRegister(3)
c = ClassicalRegister(3)
qc = QuantumCircuit(q,c)

qc.x(q[0])
qc.x(q[1])
qc.ccx(q[0], q[1], q[2])

qc.measure(q, c)
circuit_drawer(qc)

backend = 'ibmqx4'
job_exp = execute(qc, backend=backend, shots=1024, max_credits=3)

lapse = 0
interval = 10
while not job_exp.done:
    print('Status @ {} seconds'.format(interval * lapse))
    print(job_exp.status)
    time.sleep(interval)
    lapse += 1
print(job_exp.status)

plot_histogram(job_exp.result().get_counts(qc))

backend = 'ibmqx4'
circuit = qiskit.compile(qc, backend=backend)
print(circuit['circuits'][0]['compiled_circuit_qasm'])
circuit_drawer(load_qasm_string(circuit['circuits'][0]['compiled_circuit_qasm']))

f:id:goropikarikun:20180621143102p:plain
f:id:goropikarikun:20180621143105p:plain
f:id:goropikarikun:20180621143110p:plain

Composer を使って Toffoli ゲート を実装しようとすると1個1個ゲートをドラッグ・アンド・ドロップしなければならないため大変面倒、かつ、再利用性に乏しい*3 けれど、QISKit を使えば Toffoli ゲート も勝手にいい感じに実装してくれる。

ゲートの省略

Z ゲートを2回作用させると { Z^2  = I  } となるので、このような場合はゲートを省略したほうが良い*4がそのへんの最適化も QISKit 経由だと勝手にやってくれる。

q = QuantumRegister(2)
c = ClassicalRegister(2)
qc = QuantumCircuit(q,c)

qc.z(q[0])
qc.z(q[0])

qc.measure(q, c)
circuit_drawer(qc)

backend = 'ibmqx4'
circuit = qiskit.compile(qc, backend=backend)
print(circuit['circuits'][0]['compiled_circuit_qasm'])
circuit_drawer(load_qasm_string(circuit['circuits'][0]['compiled_circuit_qasm']))

f:id:goropikarikun:20180621161132p:plain
コンパイル

f:id:goropikarikun:20180621161135p:plain
コンパイル後の回路では Z ゲートが省略された。

QISKit が空気を読んでくれない時

恒等演算子への変換編

先の例では Z ゲートが省略されたが、ここでおもむろに Qubit の数を 1 に減らしてみる。

q = QuantumRegister(1)
c = ClassicalRegister(1)
qc = QuantumCircuit(q,c)

qc.z(q[0])
qc.z(q[0])

qc.measure(q, c)
circuit_drawer(qc)

backend = 'ibmqx4'
circuit = qiskit.compile(qc, backend=backend)
print(circuit['circuits'][0]['compiled_circuit_qasm'])
circuit_drawer(load_qasm_string(circuit['circuits'][0]['compiled_circuit_qasm']))

f:id:goropikarikun:20180621162243p:plain
コンパイル

f:id:goropikarikun:20180621162249p:plain
コンパイル

するとどうだろう。何故か Z ゲートが省略されなくなってしまった。 私は IBM Q の実機でどのようにゲート操作を実現しているのか知らないので、この違いがどれほどのエラーを引き起こすのかは知らないが、Qubit の数を減らすと最適化のされ方が変わるというのは不思議である。

SWAP ゲート編

f:id:goropikarikun:20180621195036p:plain
SWAP ゲートは CNOT ゲートを3つ使って上図のように実装できる*5。 これを ibmqx4 向けにコンパイルすると次のようになる。

q = QuantumRegister(2)
c = ClassicalRegister(2)
qc = QuantumCircuit(q,c)

qc.swap(q[0],q[1])

qc.measure(q, c)
circuit_drawer(qc)

backend = 'ibmqx4'
circuit = qiskit.compile(qc, backend=backend)
print(circuit['circuits'][0]['compiled_circuit_qasm'])
circuit_drawer(load_qasm_string(circuit['circuits'][0]['compiled_circuit_qasm']))

f:id:goropikarikun:20180621195446p:plain
コンパイル
f:id:goropikarikun:20180621195442p:plain
コンパイル

コンパイル後の回路を Hadamard ゲートで書き直すと次のようになっている。 f:id:goropikarikun:20180621200804p:plain
これは
f:id:goropikarikun:20180621201011p:plain
{ \mathrm{CNOT}_{01} = (H \otimes H) \mathrm{CNOT}_{10} (H \otimes H) } を使って変換しているわけですが、両端の Hadamard ゲートがない
f:id:goropikarikun:20180621201408p:plain
でも SWAP ゲートになるので QISKit のコンパイラが吐き出した OpenQASM のコードには無駄がある。
常に Hadamard ゲートが少ない方の SWAP になるように QISKit のコードを編集しようと思ったが該当箇所がどこなのかわからなかったので諦めました。

まとめ

  • シミュレーターを使うならばコンパイル後の回路について気にしなくても良い。
  • 実機で実験する場合、コンパイル後の回路図も見ておいたほうがよさそう。

余談

慶應大学が使える 20 qubits の量子コンピュータは写真を見る限り斜め方向にも CNOT が掛けられるようなので回路が組みやすそうですね。まぁ、私のような無学な人間には一生縁のない大学ですが。
- 慶應大の量子コンピューター研究拠点「IBM Qハブ」が注目の理由 —— 量子ネイティブ人材育成、三菱UFJら4社参画 | BUSINESS INSIDER JAPAN
- IBM Q Experience

参考

Jupyter Notebook

gistedf4a2674d1cb98c65fcf83abf6e3cae

*1:他にもあるだろうが、私みたいな素人目線からのメリットはこんな感じ

*2: { \mathrm{CNOT}_{ij}  } は i が controlled qubit, j が target qubit を表す。

*3:OpenQASM をコピペすれば多少再利用性が上がりますが。

*4:ゲートの数が増えるとその分ゲートエラーが増える。それと長くなるとデコヒーレンスするので量子性が失われていく。

*5:$$ \mathrm{CNOT}_{01} = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ \end{bmatrix} $$

\begin{align} \mathrm{CNOT}_{10} &= (H \otimes H) \mathrm{CNOT}_{01} (H \otimes H) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ \end{bmatrix} \end{align}

\begin{align} \mathrm{SWAP} &= \mathrm{CNOT}_{01} \mathrm{CNOT}_{10} \mathrm{CNOT}_{01} \\ &= \mathrm{CNOT}_{10} \mathrm{CNOT}_{01} \mathrm{CNOT}_{10} \\ &= \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ \end{bmatrix} \end{align}