[3]:
#!pip install qulacs
#!pip install qulacsvis
#!pip install matplotlib
#!pip install numpy
#!pip install scipy
from utility import *
[4]:
import matplotlib.pyplot as plt
import numpy as np
import time
import random
from qulacs import QuantumState
from qulacs import QuantumCircuit
from qulacs.gate import DenseMatrix
from qulacs.circuit import QuantumCircuitOptimizer
from qulacsvis import circuit_drawer

変分法を用いて量子演算を特定の演算で分解する#

  • エンタングルメント状態の生成

  • エンタングルメント忠実度の計算

  • ParametericQuantumCircuit クラスを使う

[5]:
from IPython.display import Image
Image("./fujii_fig01.png",width = 600)
[5]:
../_images/notebooks_01_03_VariationalGateDecomposition_3_0.png

最大エンタングル状態の生成#

[6]:
Image("./fujii_fig02.png",width = 600)
[6]:
../_images/notebooks_01_03_VariationalGateDecomposition_5_0.png
[7]:
nqubits = 2
mes = QuantumState(nqubits)
H(0).update_quantum_state(mes)
CNOT(0,1).update_quantum_state(mes)

0,1基底での確率分布は、

[8]:
show_distribution(mes)
../_images/notebooks_01_03_VariationalGateDecomposition_8_0.png

${ |+:nbsphinx-math:rangle `, |-:nbsphinx-math:rangle } :math:`基底での測定は、H$を作用させてから0,1測定すればいいので

[9]:
H(0).update_quantum_state(mes)
H(1).update_quantum_state(mes)
show_distribution(mes)
../_images/notebooks_01_03_VariationalGateDecomposition_10_0.png

基底を変えても完全相関があることがわかる。2n qubitの最大エンタングル状態を生成する関数を作っておく。

[10]:
def maximally_entangled_state(nqubits):
    state = QuantumState(2*nqubits)
    for i in range(nqubits):
        H(i).update_quantum_state(state)
        CNOT(i,i+nqubits).update_quantum_state(state)
    return state
[11]:
four_qubit_mes = maximally_entangled_state(2)
show_distribution(four_qubit_mes)
../_images/notebooks_01_03_VariationalGateDecomposition_13_0.png

エンタングルメント忠実度を計算する#

RandomUnitaryを用いてターゲットとなるランダムな量子演算を作っておく

[12]:
Image("./fujii_fig02b.png",width = 600)
[12]:
../_images/notebooks_01_03_VariationalGateDecomposition_16_0.png
[13]:
from qulacs.gate import RandomUnitary
target_list = [0]
target_unitary = RandomUnitary(target_list)
print(target_unitary)
 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
 (0.730061,-0.207663)   (0.638504,0.127274)
  (0.457701,0.463029) (-0.228458,-0.723823)

パラメータ付きの量子回路をParametricQuantumCircuitadd_parametric_RX_gateで追加する生成する

[14]:
nqubits = 2 # 2qubitの空間考えているので2qubit分確保しておく

#パラメータ付きの量子回路クラス
ansatz = ParametricQuantumCircuit(nqubits)

#パラメータを回路構成後に変更できる演算
ansatz.add_parametric_RZ_gate(0,0.0)
ansatz.add_parametric_RX_gate(0,0.0)
ansatz.add_parametric_RZ_gate(0,0.0)

#通常の量子演算
ansatz.add_gate(target_unitary)

#量子回路の可視化
circuit_drawer(ansatz, "mpl")
../_images/notebooks_01_03_VariationalGateDecomposition_19_0.png

エンタングルメント忠実度を計算する。mes_copy = \(\langle \text{MES}|\), mes = \(I \otimes (U^{\dagger} R_z(\alpha) R_x(\beta) R_z(\gamma))|\text{MES}\rangle\)として、inner_product関数を実行

[15]:
mes = maximally_entangled_state(1)
mes_copy = mes.copy()
ansatz.update_quantum_state(mes)

from qulacs.state import inner_product
ent_fidelity = abs(inner_product(mes_copy,mes))**2
print(ent_fidelity)
0.279817995782748

エンタングルメント忠実度を計算する関数を作っておく

[16]:
from qulacs.state import inner_product

def entanglement_fidelity(ansatz,nqubits):
    mes = maximally_entangled_state(int(nqubits/2))
    mes_copy = mes.copy()
    ansatz.update_quantum_state(mes)

    ent_fidelity = abs(inner_product(mes_copy,mes))**2
    return ent_fidelity
[17]:
entanglement_fidelity(ansatz,2)
[17]:
0.279817995782748

忠実度を最適化することで、演算の分解を見つける#

[18]:
Image("./fujii_fig02c.png",width = 600)
[18]:
../_images/notebooks_01_03_VariationalGateDecomposition_26_0.png

パラメータ付き回路のパラメータを変化させて、ターゲットの量子演算となるパラメータを見つけよう。i番目のパラメータの変更は、set_parameter(i,new_parameter)

[19]:
def cost(parameters):
    num_paras = ansatz.get_parameter_count()

    #パラメータを変更
    for i in range(num_paras):
        ansatz.set_parameter(i,parameters[i])

    return 1 - entanglement_fidelity(ansatz,nqubits)
[20]:
cost([random.random() for i in range(3)])
[20]:
0.9105093927112391

scipyの最適化関数をつかって、パラメータを変分的に最適化しよう

[21]:
import scipy.optimize

cost_history = []

#パラメータの初期値
init_theta_list = [random.random() for i in range(ansatz.get_parameter_count())]
cost_history.append(cost(init_theta_list))

method = "BFGS"
options = {"disp": True, "maxiter": 50, "gtol": 1e-6}

opt = scipy.optimize.minimize(cost, init_theta_list,
               method=method,
               callback=lambda x: cost_history.append(cost(x)))

plt.rcParams["font.size"] = 18
plt.plot(cost_history)
plt.xlabel("Iteration")
plt.ylabel("1-(entanglement fidelity)")
plt.show()
../_images/notebooks_01_03_VariationalGateDecomposition_31_0.png
[22]:
print("fidelity",opt.fun,"parameters",opt.x)

fidelity 4.3253178816371474e-11 parameters [2.63910172 1.41796404 2.04467989]
[23]:
cost([-0.1523479  , 2.03515578,-2.1503557 ])
[23]:
0.9976341047597013

できた回路が同じであることを確認しておく(エルミート共役をとるので、パラメータの順序を逆にし、マイナスをつけることを忘れない)

\[(R_z(\alpha) R_x(\beta) R_z(\gamma))^{\dagger} = R_z(-\gamma) R_x(-\beta) R_z(-\alpha))\]
[24]:
nqubits = 1
state0 = QuantumState(nqubits)
state0.set_Haar_random_state()
state1 = state0.copy()

target_unitary.update_quantum_state(state0)

RZ(0,-1.0*opt.x[2]).update_quantum_state(state1)
RX(0,-1.0*opt.x[1]).update_quantum_state(state1)
RZ(0,-1.0*opt.x[0]).update_quantum_state(state1)

abs(inner_product(state0,state1))**2

[24]:
0.9999999999611422

(全体の位相因子が異なる場合がある。)

応用編:CNOT演算を分解する#

2量子ビット以上のパウリの回転演算子はadd_parametric_multi_Pauli_rotation_gate(index_list, pauli_id_list, angle) で追加できる。pauli_id_listはX、Y、Z、をそれぞれ1,2,3として並べたリスト。

[25]:
Image("./fujii_fig03.png",width = 600)
[25]:
../_images/notebooks_01_03_VariationalGateDecomposition_38_0.png
パウリ行列\(I=\sigma_0, X=\sigma_1, Y=\sigma_2, Z=\sigma_3\)の添え字(0-3)をqulacsではpauli_idと呼んでいる。
add_parametric_multi_Pauli_rotation_gate関数の引数は次の通り。
  • index_list: 量子ビットのインデックスのリスト

  • pauli_ids: \(e^{i\theta U}\)\(U\)\(U\)をパウリ行列のテンソル積するときの、pauli_idのリスト。例えば、\(Z \otimes X = \sigma_3 \otimes \sigma_1\)の場合は、[3,1]

  • angle: \(e^{i\theta U}\)\(\theta\)

index_listpauli_idsの長さは同じにする。

[26]:
nqubits = 4 # 4qubitの空間考えているので4qubit分確保しておく

#パラメータ付きの量子回路クラス
ansatz = ParametricQuantumCircuit(nqubits)

#パラメータを回路構成後に変更できる演算
ansatz.add_parametric_RZ_gate(0,0.0)
ansatz.add_parametric_multi_Pauli_rotation_gate([0,1],[3,1], 0.0)
ansatz.add_parametric_RX_gate(1,0.0)

#ターゲットの量子演算
ansatz.add_gate(CNOT(0,1))
[27]:
cost_history = []

#パラメータの初期値
init_theta_list = [random.random() for i in range(ansatz.get_parameter_count())]
cost_history.append(cost(init_theta_list))

method = "BFGS"
options = {"disp": True, "maxiter": 50, "gtol": 1e-6}

opt = scipy.optimize.minimize(cost, init_theta_list,
               method=method,
               callback=lambda x: cost_history.append(cost(x)))

plt.rcParams["font.size"] = 18
plt.plot(cost_history)
plt.xlabel("Iteration")
plt.ylabel("1-(entanglement fidelity)")
plt.show()
../_images/notebooks_01_03_VariationalGateDecomposition_41_0.png
[28]:
opt.x
[28]:
array([ 1.57078545, -1.57080084,  1.57079334])
[ ]: