進捗のようなもの

やったこと書きます

IBM Quantum Challenge 2020 (4th anniversary) 振り返りEx04編

私が参加した、 IBM Quantum Challengeの課題について今回は振り返って行きたいと思います。今回はEx04についてです。
Ex01~Ex03に関しては↓で書きました。

sjsy.hatenablog.com

問題に入る前に

この↓のツイートは開催終了後にアップされたものですが、めちゃくちゃ驚きました。

IBMの中の人が想定していた回路の最小コスト45を上回るコスト45の解答が参加者から出てきました。(コストについては後で説明します。もちろん小さい値ほど良い結果です。)
ただでさえ難かった(少なくとも私にとっては)最終問題を予想を上回る解答を4日間の短い時間で導きだしたことに感動しました。コンテストならでは(?)の体験ができました。


\def\bra#1{\mathinner{\left\langle{#1}\right|}}
\def\ket#1{\mathinner{\left|{#1}\right\rangle}}
\def\braket#1#2{\mathinner{\left\langle{#1}\middle|#2\right\rangle}}

Exercise 4: Circuit Decomposition

最終問題は、量子コンピュータのゲート操作がユニタリ行列の演算であることに注目した問題でした。課題は与えられた16\times16のユニタリ行列を量子回路に実装することです。任意のユニタリ行列はシングル量子ビットの回転ゲートU3ゲートとCNOT(CX)ゲートだけで構成できることが知られており、ユニタリ行列UU3ゲートとCNOT(CX)ゲートに分解して回路を実装します。ちなみに、IBM Quantum Experienceでは量子回路はU3ゲートとCNOT(CX)ゲートにtranspileされて実行されているそうです。
分解する際に考えなければいけないのは回路のコストです。できるだけ少ないゲート操作で近似することができれば効率的に計算でき、ノイズも少なくより正しい結果を得ることができます。ここではコストを次の計算式で求めます。

 \text{cost} = 10 \cdot n_{cx} + n_{u3}

ここで、n_{cx}CX(CNOT)ゲートの数、n_{u3}U3ゲートの数を表します。1ビットゲートは2ビットゲートよりもはるかに高い忠実度を持っているので、CX(CNOT)ゲートはU3ゲートの10倍のコストになっています。この課題ではU3ゲートとCNOT(CX)ゲートのみで構成され、コストが1600以下の回路に最適化することで正解となります。
ただし、ユニタリ行列Uを近似して求めるユニタリVは誤差\varepsilon = 0.01 \lVert U - V\rVert_2 \leq \varepsilonを満たす必要があります。

解答のアプローチ

解答のアプローチは主に2つあると思います。

  1. ユニタリ行列Uの対称性に着目した解析的な解法
  2. 変分量子回路を用いた回転ゲートパラメタ調整による解法

1つ目の方法は、ユニタリ行列の対称性に着目してとてもスマート解法です。おそらくIBMの中の人もこの解法を想定していると思います(個人の推測ですが)。 2つ目の方法は、Quantum Circuit Learning (量子回路学習, QCL)などに用いられる変分量子回路を利用して、U3ゲートの角度パラメタを調整します。この方法は、入力サイズに対して指数的にパラメタが増えるため、一般的には現実的な方法ではありません。今回はユニタリ行列の対称性があるので、なんとか解ける問題になっていると思っています。2つ目の解法につていは、研究も行われているようで参考になりそうな文献を列挙しておきます。

ユニタリ行列Uの対称性に着目した解析的な解法

ここでは、コンテスト2位の方が公開されている解法を参考に問題を振り返っていきます。スマートな解法を丁寧に分かりやすく解説しています。

github.com

準備

必要なパッケージのインポートと、ユニタリ行列Uのshapeを確認しておきます。

from qiskit import QuantumCircuit, execute, Aer
from qiskit.compiler import transpile, assemble

from may4_challenge.ex4 import get_unitary, check_circuit, submit_circuit

U = get_unitary()
# print(U)
print("U has shape", U.shape)
U has shape (16, 16)  

transpile

まずは、ユニタリ行列{U}を量子回路に接続してtranspilingして、どんな回路になるか見てみましょう。 任意のユニタリ行列を回路に接続するには、iso()を使います。 transpilingとは、H,X,CZゲートなどの一般的なゲートを抽象的な回転ゲート(今回はU3ゲート)とCNOT(CX)ゲートに変換することです。transpile()を使用します。オプションにあるoptimization_levelは最適化の設定ですが、一番重い最適化の3は実行するバックエンドの情報を含めた最適化らしくコストが大きくなる場合もあるので、今回は一段落としたoptimization_level=2に設定しています。詳細は公式ドキュメントを参照してください。
今回は使用しませんでしたが、Qiskitには色々な種類のtranspilerが用意されてるみたいなので、気になったら↓のチュートリアルを参照してみてください。 https://www.qiskit.org/documentation/locale/ja_JP/tutorials/advanced/terra/4_transpiler_passes_and_passmanager.htmlwww.qiskit.org

本題に戻ってコードです。

qc1 = QuantumCircuit(4)
qc1.iso(U, [0,1,2,3], [])
qc1 = transpile(qc1, basis_gates = ['u3', 'cx'], optimization_level=2)
qc1.draw(output='mpl')

f:id:SJSY:20200519214800p:plain

check_circuit(qc1)
Circuit stats:
||U-V||_2 = 9.761271800590147e-14
(U is the reference unitary, V is yours, and the global phase has been removed from both of them).
Cost is 1662

Something is not right with your circuit: the cost of the circuit is too high (above 1600)

ユニタリ行列{U}とほぼ等価な回路を作成できましたが、めちゃくちゃデカイ回路になってしまいました。コストも1600オーバーでアウトです。

ユニタリ行列を観察

次は多すぎるゲートを削減できないか、ユニタリ行列{U}を解析してみます。
視覚的に感覚を掴むため、行列のヒートマップを作成してみます。

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

fig, (ax1, ax2, ax3, ax4) = plt.subplots(1, 4, figsize=(16, 3))
sns.heatmap(np.real(U), vmin=-1, vmax=1, cmap='hot', square=True, ax=ax1)
ax1.set_title('Real part')
sns.heatmap(np.imag(U), vmin=-1, vmax=1, cmap='hot', square=True, ax=ax2)
ax2.set_title('Imaginary part')
sns.heatmap(np.abs(U), vmin=0, vmax=1, cmap='hot', square=True, ax=ax3)
ax3.set_title('Magnitude')
sns.heatmap(np.angle(U), vmin=-np.pi, vmax=np.pi, cmap='hot', square=True, ax=ax4)
ax4.set_title('Phase')
plt.show()

f:id:SJSY:20200521180627p:plain

幾何学模様なっていて、明らかに対称性がありますね!!!

対角化

参照元の解法では、一旦QFT(Quantum Fourier transform, 量子フーリエ変換)のワンクッション置いてますが、QFTはショートカットします。
ここで考えるのは、この問題のポイントでもあるユニタリ行列{U}の対角化です。実は、HUHのようにユニタリ行列{U}Hゲートで両側を挟んであげると(Hadamard空間に写してあげると)対角化することができます。 対角化するためにHゲートを使うということをどうやって思いついたか説明できませんが(ここは直感なのかな?)、コンテスト開催中のQiskitでは「HUHをつくってみろ」というヒントが上がっていました。

# 1ビットのH行列
H = 1/np.sqrt(2)*np.array([[1,1],[1,-1]]) 
# 4ビットのH行列
H4 = np.kron(H,np.kron(H,np.kron(H,H))) 
HUH = np.matmul(H4,np.matmul(U,H4))

for i in range(len(HUH)):
        for j in range(len(HUH[i])):
                if -1e-14 < np.real(HUH[i,j]) < 1e-14:
                    HUH[i,j] = 0 + 1j*(np.imag(HUH[i,j]))
                if -1e-14 < np.imag(HUH[i,j]) < 1e-14:
                    HUH[i,j] = np.real(HUH[i,j]) + 1j*0

fig, (ax1, ax2, ax3, ax4) = plt.subplots(1, 4, figsize=(16, 3))
sns.heatmap(np.real(HUH), vmin=-1, vmax=1, cmap='hot', square=True, ax=ax1)
ax1.set_title('Real part')
sns.heatmap(np.imag(HUH), vmin=-1, vmax=1, cmap='hot', square=True, ax=ax2)
ax2.set_title('Imaginary part')
sns.heatmap(np.abs(HUH), vmin=0, vmax=1, cmap='hot', square=True, ax=ax3)
ax3.set_title('Magnitude')
sns.heatmap(np.angle(HUH), vmin=-np.pi, vmax=np.pi, cmap='hot', square=True, ax=ax4)
ax4.set_title('Phase')
plt.show()

f:id:SJSY:20200521180908p:plain

対角成分だけが残り、しっかりと対角化できています。

HUHで回路作成

HUHを回路に接続してコストが減少しているか確かめてみます。HUHはHadamard空間の行列のため、前後にHゲートをつけて元の空間に戻してあげる必要があります。

qc2 = QuantumCircuit(4)
qc2.h([0,1,2,3])
qc2.iso(HUH, [0,1,2,3], [])
qc2.h([0,1,2,3])
qc2 = transpile(qc2, basis_gates = ['u3', 'cx'], optimization_level=2)
qc2.draw(output='mpl')

f:id:SJSY:20200519230105p:plain

check_circuit(qc2)
Circuit stats:
||U-V||_2 = 7.555325382443748e-15
(U is the reference unitary, V is yours, and the global phase has been removed from both of them).
Cost is 194

Great! Your circuit meets all the constrains.

大きくゲートを削減でき、コスト1600以下になってチャレンジクリアです!

対角行列を見る

ここからは、どこまでコストを削減できるか考えていきましょう。対角化した行列の左から3番目のプロットをみると対角化成分がすべて1であり、正規化された対角行列という事がわかります。まずは、正規化された対角演算子がどのような作用をするか2\times2行列を例に見てます。

f:id:SJSY:20200521190258p:plain

この演算子はそれぞれの量子状態に次のような操作をすることを表しています。

\begin{aligned}
\ket{00} &\rightarrow \ket{00}  \\
\ket{01} &\rightarrow -\ket{01}   \\
\ket{10} &\rightarrow \frac{1}{\sqrt{2}} \left(1 + i \right) \ket{10}  \\
\ket{11} &\rightarrow \ket{11} 
\end{aligned}

つまり、各量子状態に位相を付加する作用を持っています。

\begin{aligned}
\ket{00} &\rightarrow e^{i0}\ket{00}  \\
\ket{01} &\rightarrow e^{i\pi}\ket{01}   \\
\ket{10} &\rightarrow e^{i\frac{\pi}{4}} \ket{10}  \\
\ket{11} &\rightarrow e^{i0}\ket{11} 
\end{aligned}

では、HUHでは位相がどのようになっているか見てみましょう。

np.diagonal(HUH)
array([ 1.        +0.j        ,  0.70710678+0.70710678j,
       -0.70710678-0.70710678j,  0.        +1.j        ,
        0.        +1.j        ,  0.70710678-0.70710678j,
       -0.70710678+0.70710678j, -1.        +0.j        ,
       -0.70710678+0.70710678j, -1.        +0.j        ,
       -1.        +0.j        ,  0.70710678+0.70710678j,
       -0.70710678-0.70710678j,  0.        +1.j        ,
        0.        +1.j        , -0.70710678+0.70710678j])

書き直すと \begin{bmatrix} 1, \frac{1}{\sqrt{2}} \left(1 + i \right), \frac{-1}{\sqrt{2}} \left(1 + i \right), i, i, \frac{1}{\sqrt{2}} \left(1 - i \right), \frac{-1}{\sqrt{2}} \left(1 - i \right), -1, \frac{-1}{\sqrt{2}} \left(1 - i \right), -1, -1, \frac{1}{\sqrt{2}} \left(1 + i \right), \frac{-1}{\sqrt{2}} \left(1 + i \right), i, i, \frac{-1}{\sqrt{2}} \left(1 - i \right) \end{bmatrix} これを位相の角度に書き直すと
\begin{bmatrix} 0, \frac{\pi}{4}, \frac{-3\pi}{4}, \frac{\pi}{2}, \frac{\pi}{2}, \frac{-\pi}{4}, \frac{3\pi}{4}, \pi, \frac{3\pi}{4}, \pi, \pi, \frac{\pi}{4}, \frac{-3\pi}{4}, \frac{\pi}{2}, \frac{\pi}{2}, \frac{-\pi}{4} \end{bmatrix}
まずは、1つだけビットが反転した状態(\ket{0001},\ket{0010},\ket{0100},\ket{1000})に注目すると、位相は \begin{bmatrix} \frac{\pi}{4}, \frac{-3\pi}{4}, \frac{\pi}{2}, \frac{3\pi}{4} \end{bmatrix}
になっています。この角度をそれぞれ、対応する量子ビットU3ゲートで対応させます。

qcp = QuantumCircuit(4)
qcp.u3(0,np.pi/4,0,0)
qcp.u3(0,-3*np.pi/4,0,1)
qcp.u3(0,np.pi/2,0,2)
qcp.u3(0,3*np.pi/4,0,3)
qcp.draw(output='mpl')

f:id:SJSY:20200521193722p:plain

この回路のユニタリ行列がどうなっているか見てみます。

U_simulator = Aer.get_backend('unitary_simulator')
result = execute(qcp, backend = U_simulator).result()
P = result.get_unitary()
np.diagonal(P) 
array([ 1.00000000e+00+0.00000000e+00j,  7.07106781e-01+7.07106781e-01j,
       -7.07106781e-01-7.07106781e-01j,  0.00000000e+00-1.00000000e+00j,
        6.12323400e-17+1.00000000e+00j, -7.07106781e-01+7.07106781e-01j,
        7.07106781e-01-7.07106781e-01j,  1.00000000e+00-6.12323400e-17j,
       -7.07106781e-01+7.07106781e-01j, -1.00000000e+00+2.22044605e-16j,
        1.00000000e+00+0.00000000e+00j,  7.07106781e-01+7.07106781e-01j,
       -7.07106781e-01-7.07106781e-01j, -2.22044605e-16-1.00000000e+00j,
        0.00000000e+00+1.00000000e+00j, -7.07106781e-01+7.07106781e-01j])

また、位相を角度に直してみます。
\begin{bmatrix} 0, \frac{\pi}{4}, \frac{-3\pi}{4}, \frac{-\pi}{2}, \frac{\pi}{2}, \frac{3\pi}{4}, \frac{-\pi}{4}, 0, \frac{3\pi}{4}, \pi, 0, \frac{\pi}{4}, \frac{-3\pi}{4}, \frac{-\pi}{2}, \frac{\pi}{2}, \frac{-\pi}{4} \end{bmatrix} つぎに、この得られた角度とHUHの角度との差を取ってみます。
\begin{bmatrix} 0, 0, 0, \pi, 0, \pi, \pi, \pi, 0, 0, \pi, 0, 0, \pi, 0, 0 \end{bmatrix}
ここで、位相が\piズレてるのは、3,5,6,7,10,13番目なので、対応する量子状態は \ket{0011}, \ket{0101}, \ket{0110}, \ket{0111}, \ket{1010}, \ket{1101}
です。この量子状態の位相を\piズラす回路はCZゲートを使うと次のようになります。
こういう回路の繋ぎ方を試行錯誤して考える時、IBM Quantum ExperienceのCircuit Composerが便利ですね。

f:id:SJSY:20200521201705p:plain

f:id:SJSY:20200521201719p:plain

\ket{0011}, \ket{0101}, \ket{0110}, \ket{0111}, \ket{1010}, \ket{1101} の位相を\piズラせました。

これまでの結果から、ユニタリ行列Uの回路は次のようになります。

qc4 = QuantumCircuit(4)
qc4.h([0,1,2,3])
qc4.barrier()
qc4.cz(0,1)
qc4.cz(0,2)
qc4.cz(1,2)
qc4.cz(1,3)
qc4.barrier()
qc4.u3(0,np.pi/4,0,0)
qc4.u3(0,np.pi/2,0,2)
qc4.u3(0,-3*np.pi/4,0,1)
qc4.u3(0,3*np.pi/4,0,3)
qc4.barrier()
qc4.h([0,1,2,3])
qc4.draw(output='mpl')

f:id:SJSY:20200521202320p:plain

transpile()で回路を単純化してもらいましょう。

qc5 = QuantumCircuit(4)
qc5.h([0,1,2,3])
qc5.cz(0,1)
qc5.cz(0,2)
qc5.cz(1,2)
qc5.cz(1,3)
qc5.u3(0,np.pi/4,0,0)
qc5.u3(0,np.pi/2,0,2)
qc5.u3(0,-3*np.pi/4,0,1)
qc5.u3(0,3*np.pi/4,0,3)
qc5.h([0,1,2,3])
qc5 = transpile(qc5, basis_gates = ['u3', 'cx'], optimization_level=2,)
qc5.draw(output='mpl')

f:id:SJSY:20200521202713p:plain

気になるコストは……

check_circuit(qc5)
Circuit stats:
||U-V||_2 = 1.4545997371336807e-15
(U is the reference unitary, V is yours, and the global phase has been removed from both of them).
Cost is 46

46!!!IBM中の人が想定していたベストスコアを叩き出せました!!!

コスト45を目指して

ここからは、あと一つ回転ゲートを減らすことを考えます。

公式リポジトリに上がっている↓のメモを参考に考えていきます。

github.com

点線で囲った部分に注目します。目標はq_2にある回転ゲートの削減です。

f:id:SJSY:20200521212224p:plain

回路の変換には↓の論文が参考になります。

arxiv.org

おなじみのCircuit Composerで考えます。

f:id:SJSY:20200521213641p:plain

右側を変換します。

f:id:SJSY:20200521213946p:plain

次は左側の2つのCZゲートを変換します。

f:id:SJSY:20200521214328p:plain

真ん中のCXゲートは打ち消せます。

f:id:SJSY:20200521214959p:plain

論文を参考にしつつ、CZゲートとCXゲートを変換していきます。

f:id:SJSY:20200521215630p:plain

重なっているHゲートは消しておきます。

f:id:SJSY:20200521215833p:plain

q_2に残っているHゲートは本体の回路にあるHゲートと打ち消せるのでq_2の回転ゲートは削除できました。
これを本体に戻してtanspile()してあげます。

qc6 = QuantumCircuit(4)

qc6.h([1,3])
qc6.cx(0,2)
qc6.cx(1,0)
qc6.h(0)
qc6.u3(0,np.pi/2,0,0)
qc6.h(0)
qc6.cx(0,2)
qc6.h(0)
qc6.u3(0,np.pi*3/2,0,0)
qc6.u3(0,np.pi/4,0,0)
qc6.h(2)

qc6.cz(1,3)
qc6.u3(0,-3*np.pi/4,0,1)
qc6.u3(0,3*np.pi/4,0,3)
qc6.h([0,1,2,3])
qc6 = transpile(qc6, basis_gates = ['u3', 'cx'], optimization_level=2,)
qc6.draw(output='mpl')

f:id:SJSY:20200521220428p:plain

コストは……

check_circuit(qc6)
Circuit stats:
||U-V||_2 = 1.4000864719689736e-15
(U is the reference unitary, V is yours, and the global phase has been removed from both of them).
Cost is 45

45!!!最小スコア達成できました!!!

変分量子回路を用いた回転ゲートパラメタ調整による解法

続いて、もう一つの別解について見ていきます。
コンテスト中、私が使ったアプローチです。参考になるか分かりませんが私が挑戦中に考えていたことも合わせて記しておきます。

まず問題を見た瞬間に、私は「適当に回路作って、回転ゲートの角度は最適化のアルゴリズム使って、ちゃちゃっと調整すればイケるんじゃね」と、何も思考せずこのアプローチを選びました。

最初の回路を作成

まずはベースとなる回路を考えます。ベストな回路が提案されているかもしれないので、まずは[quantum circuit controlled rotation]のような関連ワードでググりました。さらに、回路を見つけたいので画像のタブを眺めてみます。

f:id:SJSY:20200522204352p:plain

上の方にあるControlled-Uゲートが使えそうだったので、

arxiv.org

https://www.appi.keio.ac.jp/Itoh_group/abe/pdf/qc5.pdf

を参考にして、"それっぽい回路"を作りました。

ではコードです。

import numpy as np
from qiskit.compiler import transpile
from qiskit import QuantumCircuit
from may4_challenge.ex4 import check_circuit, submit_circuit
from may4_challenge.ex4 import get_unitary

U = get_unitary()

def make_qc(params):
    qc = QuantumCircuit(4)
    qc.u3(params[0],params[1],0,0)
    
    qc.u3(params[2],params[3],0,1)
    qc.cx(0,1)
    qc.u3(params[4],params[5],0,1)

    qc.u3(params[6],params[7],0,2)
    qc.cx(1,2)
    qc.u3(params[8],params[9],0,2)
    qc.cx(0,2)
    qc.u3(params[10],params[11],0,2)
    qc.cx(1,2)
    qc.u3(params[12],params[13],0,2)

    qc.u3(params[14],params[15],0,3)
    qc.cx(2,3)
    qc.u3(params[16],params[17],0,3)
    qc.cx(1,3)
    qc.u3(params[18],params[19],0,3)
    qc.cx(2,3)
    qc.u3(params[20],params[21],0,3)
    qc.cx(0,3)
    qc.u3(params[22],params[23],0,3)
    qc.cx(2,3)
    qc.u3(params[24],params[25],0,3)
    qc.cx(1,3)
    qc.u3(params[26],params[27],0,3)
    qc.cx(2,3) 
    
    qc.u3(params[28],params[29],0,2)
    qc.cx(1,2)
    qc.u3(params[30],params[31],0,2)
    qc.cx(0,2)
    qc.u3(params[32],params[33],0,2)
    qc.cx(1,2)

    
    qc.u3(params[34],params[35],0,1)
    qc.cx(0,1)

    qc.u3(params[36],params[37],0,0)
    qc.u3(params[38],params[39],0,1)
    qc.u3(params[40],params[41],0,2)
    qc.u3(params[42],params[43],0,3)
    
    return qc

np.random.seed(0)
params = np.random.uniform(0,0.1,44)

qc = make_qc(params)
qc.draw(output='mpl')

f:id:SJSY:20200522210658p:plain

パラメタが多くなると最適化の収束が大変そうだと思ったので、U3ゲートの3つのパラメタのうち2つの角度だけを変数に設定しました。 初期値はランダムな値を設定しています。

回路最適化

最適化するにあたって目標関数を設定します。今回はちょうど \lVert U - V\rVert_2 \leq \varepsilonの目標値が設定されていたので、それをそのまま目標関数として拝借します。 ここを覗くと_validate()を使えば良さそうです。

from may4_challenge.ex4 import _validate

def loss(params):
    qc = make_qc(params)
    return _validate(qc)['delta']

# 初期値
loss(params)
1.9979253424790289

最適化アルゴリズムはQiskitにもいろいろありますが、私の場合はScipyのSLSQPがいい感じでした。

from scipy.optimize import minimize
import scipy

options = {'disp': True,'maxiter':1000}
result = minimize(loss, x0=params, method='SLSQP', options=options)
Optimization terminated successfully.    (Exit mode 0)
            Current function value: 0.9616194503294917
            Iterations: 829
            Function evaluations: 38692
            Gradient evaluations: 829

lossが0.96でした。局所解に落ちている可能性があるので、グローバル最適化をしてみます。Scipyのbasinhopping()を使用します。lossが小さくなることをお祈りしながら画面を見つめましょう。

result = scipy.optimize.basinhopping(loss,result['x'],stepsize=1, T=500, niter=30,
                                     minimizer_kwargs={'method':'SLSQP'}, 
                                     disp=True)
basinhopping step 0: f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 1: f 1.2423 trial_f 1.2423 accepted 1  lowest_f 0.958414
basinhopping step 2: f 1.78182 trial_f 1.78182 accepted 1  lowest_f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 3: f 1.48645 trial_f 1.48645 accepted 1  lowest_f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 4: f 1.41711 trial_f 1.41711 accepted 1  lowest_f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 5: f 1.41806 trial_f 1.41806 accepted 1  lowest_f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 6: f 1.91918 trial_f 1.91918 accepted 1  lowest_f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 7: f 1.43017 trial_f 1.43017 accepted 1  lowest_f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 8: f 1.9447 trial_f 1.9447 accepted 1  lowest_f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 9: f 1.72044 trial_f 1.72044 accepted 1  lowest_f 0.958414
warning: basinhopping: local minimization failure
basinhopping step 10: f 0.779125 trial_f 0.779125 accepted 1  lowest_f 0.779125
found new global minimum on step 10 with function value 0.779125
warning: basinhopping: local minimization failure
basinhopping step 11: f 0.770332 trial_f 0.770332 accepted 1  lowest_f 0.770332
found new global minimum on step 11 with function value 0.770332
warning: basinhopping: local minimization failure
basinhopping step 12: f 0.268215 trial_f 0.268215 accepted 1  lowest_f 0.268215
found new global minimum on step 12 with function value 0.268215
warning: basinhopping: local minimization failure
basinhopping step 13: f 0.00136049 trial_f 0.00136049 accepted 1  lowest_f 0.00136049
found new global minimum on step 13 with function value 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 14: f 0.00306066 trial_f 0.00306066 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 15: f 0.797419 trial_f 0.797419 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 16: f 1.71626 trial_f 1.71626 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 17: f 1.18878 trial_f 1.18878 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 18: f 0.79497 trial_f 0.79497 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 19: f 1.32662 trial_f 1.32662 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 20: f 1.11387 trial_f 1.11387 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 21: f 1.21394 trial_f 1.21394 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 22: f 0.765612 trial_f 0.765612 accepted 1  lowest_f 0.00136049
warning: basinhopping: local minimization failure
basinhopping step 23: f 0.000141734 trial_f 0.000141734 accepted 1  lowest_f 0.000141734
found new global minimum on step 23 with function value 0.000141734
warning: basinhopping: local minimization failure
basinhopping step 24: f 1.82943 trial_f 1.82943 accepted 1  lowest_f 0.000141734
warning: basinhopping: local minimization failure
basinhopping step 25: f 1.49653 trial_f 1.49653 accepted 1  lowest_f 0.000141734
warning: basinhopping: local minimization failure
basinhopping step 26: f 1.68299 trial_f 1.68299 accepted 1  lowest_f 0.000141734
warning: basinhopping: local minimization failure
basinhopping step 27: f 1.41449 trial_f 1.41449 accepted 1  lowest_f 0.000141734
warning: basinhopping: local minimization failure
basinhopping step 28: f 0.795348 trial_f 0.795348 accepted 1  lowest_f 0.000141734
warning: basinhopping: local minimization failure
basinhopping step 29: f 1.1086 trial_f 1.1086 accepted 1  lowest_f 0.000141734
basinhopping step 30: f 1.11632 trial_f 1.11632 accepted 1  lowest_f 0.000141734

お祈りしたおかげで0.01以下になりました。コストを見てみましょう。

qc = make_qc(result['x'])
check_circuit(qc)
qc.draw(output='mpl')

f:id:SJSY:20200523085912p:plain

Circuit stats:
||U-V||_2 = 0.00014173388577980503
(U is the reference unitary, V is yours, and the global phase has been removed from both of them).
Cost is 172

Great! Your circuit meets all the constrains.
Your score is 172. The lower, the better!
Feel free to submit your answer and remember you can re-submit a new circuit at any time!

はい!チャレンジクリアです!!

回路のコスト削減

ここかからは、余分そうなゲートを削って行きます。
方針はコストがデカイCXゲートを消すことを考えます。どうやって消すかというと、回路からCXゲートを1つ削る→最適化をひたすら繰り返し、人力で消せるCXゲートを探索しました。
途中いろいろ試行錯誤して、最終的にコンテスト時間切れで↓のような回路になりました。

def make_qc(params):
    qc = QuantumCircuit(4)
    
    qc.u3(params[0],params[1],params[2],0)
    qc.u3(params[3],params[4],params[5],1)
    qc.u3(params[6],params[7],params[8],2)
    qc.u3(params[9],params[10],params[11],3)
    qc.cx(2,3)
    qc.u3(params[12],params[13],params[14],3)
    qc.cx(1,3)
    qc.cx(2,3)
    qc.u3(params[15],params[16],params[17],3)
    qc.cx(2,3)
    qc.u3(params[18],params[19],params[20],3)
    
    qc.cx(1,2)
    qc.cx(0,2)
    qc.u3(params[21],params[22],params[23],0)
    qc.u3(params[24],params[25],params[26],2)
    qc.cx(1,2)
    qc.u3(params[27],params[28],params[29],1)
    qc.u3(params[30],params[31],params[32],2) 
   
    return qc
    
qc = make_qc(result['x'])
check_circuit(qc)
qc.draw(output='mpl')

f:id:SJSY:20200523120026p:plain

Circuit stats:
||U-V||_2 = 1.5597049605037762e-15
(U is the reference unitary, V is yours, and the global phase has been removed from both of them).
Cost is 81

コスト81の129位でフィニッシュしました!
終了後に気づいたのですが、transpile()すると

qc = transpile(qc, optimization_level=3,basis_gates=['u3','cx'])
check_circuit(qc)
qc.draw(output='mpl')

f:id:SJSY:20200523122453p:plain

Circuit stats:
||U-V||_2 = 2.2956789054593593e-15
(U is the reference unitary, V is yours, and the global phase has been removed from both of them).
Cost is 73

さらにコスト73まで削減できました。見落としてましたね。

更に詰める(仮)

ここからどうやって、コスト削減できるかは思いついていないです。
ここから、ゲート削減できた方はぜひ教えて下さい!!!

おわりに

以上で、IBM Quantum Challenge 2020 (4th anniversary)の振り返りを終わりにしたいと思います。 今回のチャレンジも導入が丁寧で、得られる知識も多くて参加して良かったと思えるコンテストでした。 きっと次のIBM Quantum Challengeもあると思っているので、それまでに色々勉強していきたいと思います。