3-1. 世界最高速シミュレータQulacsの使い方

量子アルゴリズムを実際に実行するのに第2章ではSymPyを用いたが、SymPyは代数的な計算に特化している分、大規模・高速な計算は不得手である。この節では、世界最高クラスの動作速度を持つ量子コンピュータのシミュレータ Qulacs の使い方を紹介する。Qulacsの内部はC++で実装されており非常に高速で動作するが、Pythonインタフェースを通して簡単に実装することができる。

※なお、量子コンピュータのシミュレータとしては他にもIBMのQiskit, Rigetti ConmputingのPyQuilクラウド量子コンピュータあり), GoogleのCirq, MicrosoftのQ# がある。PyQuilについては、こちらの記事も参考にしてみてほしい。

Qulacsのインストール

Qulacs は pip を使って簡単にインストールすることができる。詳しくはQulacsのドキュメントを参照されたい。

[ ]:
## Google Colaboratoryの場合 ・ Qulacsがインストールされていないlocal環境の場合のみ実行してください
!pip install qulacs

## Google Colaboratory / (Linux or Mac)のjupyter notebook 環境の場合にのみ実行してください。
## Qulacsのエラーが正常に出力されるようになります。
!pip3 install wurlitzer
%load_ext wurlitzer

Qulacsの使い方(1):量子状態

量子状態の作成

Qulacsでは、以下のコードで\(n\)量子ビットの量子状態 (QuantumStateクラス) を生成できる。生成した量子状態は \(|0\rangle^{\otimes n}\) に初期化されている。

[1]:
from qulacs import QuantumState

# 5-qubitの状態を生成
n = 5
state = QuantumState(n)

# |00000>に初期化
state.set_zero_state()

\(n\)が非常に大きい場合など、メモリが不足している場合は量子状態を生成できない。

量子状態のデータの取得

QuantumState.get_vector()を用いると、量子状態を表す \(2^n\) の長さの配列を取得できる。特にGPUで量子状態を作成したり、大きい \(n\) では非常に重い操作になるので注意。

[2]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)
state.set_zero_state()

# 状態ベクトルをnumpy arrayとして取得
data = state.get_vector()
print(data)
[1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j]

量子状態の初期化

生成した量子状態は、二進数を用いて初期化(set_computational_basis)したり、ランダムな状態に初期化(set_Haar_random_state)することができる。

[3]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)
state.set_zero_state()

# |00101> に初期化
state.set_computational_basis(0b00101)
print(state.get_vector())

# ランダムな初期状態を生成
state.set_Haar_random_state()
print(state.get_vector())

# シードを指定してランダムな初期状態を生成
seed = 0
state.set_Haar_random_state(seed)
print(state.get_vector())
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j]
[-0.02437065-0.24658958j -0.01613144+0.13503688j -0.18994795-0.12319831j
  0.06504845+0.16830255j -0.21598901+0.18730307j -0.16812459-0.02947785j
 -0.19220647-0.09961786j  0.10572396+0.00775966j -0.0680691 -0.02781797j
 -0.13307298+0.16938736j  0.03958908+0.10715579j  0.01297565-0.13433703j
 -0.06164082-0.07189014j -0.07461534+0.18664457j  0.12610337-0.12079184j
  0.04721677+0.05207131j -0.01081212+0.24851703j  0.02671697-0.00200128j
 -0.03405007-0.3028626j   0.04368712-0.047096j    0.27087639+0.20331916j
 -0.11928844+0.02439986j  0.09817583+0.22850291j -0.08822065-0.01851942j
 -0.08319959+0.12819258j  0.00568074-0.02627961j  0.08279638-0.10588521j
  0.04417022-0.01161947j -0.05749897-0.22828609j -0.00629634+0.04739956j
 -0.09164645-0.10147242j  0.13382742+0.04209609j]
[ 0.09232558+0.06460115j  0.14544754-0.10413526j  0.11300793-0.02455806j
  0.00811251+0.2426378j  -0.01116588+0.23770313j -0.10691448+0.0487731j
 -0.01654446+0.17073103j  0.22250403+0.01934699j  0.04728154+0.22585226j
  0.04475383+0.20375993j -0.10592159+0.10428549j -0.10175932-0.04016904j
  0.04241271+0.08723859j  0.18205362+0.06190871j  0.14103367-0.12925877j
 -0.08269267+0.08879486j -0.14479848-0.0183179j  -0.32601567+0.06762062j
  0.03482754+0.04464901j  0.09181499+0.05497985j  0.06870746+0.12628442j
 -0.00624006-0.21793139j -0.11181371+0.2659879j  -0.04589826+0.00891387j
 -0.04058365+0.30265587j -0.13894575-0.04392724j -0.03499327+0.0184768j
  0.05033425-0.07376874j  0.07124237+0.15451312j  0.09319498+0.08341551j
 -0.03002195-0.14677347j -0.05309219+0.10184815j]

量子状態のデータのコピーとロード

量子状態を複製(copy)したり、他の量子状態のデータをロード(load)できる。

[4]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)
state.set_computational_basis(0b00101)

# コピーして新たな量子状態を作成
second_state = state.copy()
print(second_state.get_vector())

# 量子状態を新たに作成し、既存の状態のベクトルをコピー
third_state = QuantumState(n)
third_state.load(state)
print(third_state.get_vector())
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j
 0.+0.j 0.+0.j]

量子状態に関する計算

上で挙げた以外にも、量子状態(QuantumState)には種々の処理が可能である。

[5]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)
state.set_Haar_random_state()

# normの計算 (qulacs v0.1.8 で get_norm から get_squared_norm に名前が変更になりました)
norm = state.get_squared_norm()
print("squared_norm : ", norm)

# Z基底で測定した時のentropyの計算
entropy = state.get_entropy()
print("entropy : ",entropy)

# index-th qubitをZ基底で測定して0を得る確率の計算
index = 3
zero_probability = state.get_zero_probability(index)
print("prob_meas_3rd : ",zero_probability)

# 周辺確率を計算 (以下は0,3-th qubitが0、1,2-th qubitが1と測定される確率の例)
value_list = [0,1,1,0,2]
marginal_probability = state.get_marginal_probability(value_list)
print("marginal_prob : ",marginal_probability)
squared_norm :  1.0
entropy :  3.0719687986623603
prob_meas_3rd :  0.5740657157322318
marginal_prob :  0.04265173032311748

量子状態の内積

inner_product関数で内積を計算できる。

[6]:
from qulacs import QuantumState
from qulacs.state import inner_product

n = 5
state_bra = QuantumState(n)
state_ket = QuantumState(n)
state_bra.set_Haar_random_state()
state_ket.set_computational_basis(0)

# 内積値の計算
value = inner_product(state_bra, state_ket)
print(value)
(0.07448994812927281-0.1223698589819414j)

量子状態の解放

del を用いて量子状態を強制的にメモリから解放することができる。del せずとも利用されなくなったタイミングで解放されるが、メモリがシビアな際に便利である。

[7]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)

# 量子状態を開放
del state

量子状態の詳細情報の取得

QuantumStateクラスのオブジェクトを直接 print すると、量子状態の情報が出力される。

[8]:
from qulacs import QuantumState

n = 5
state = QuantumState(n)

print(state)
 *** Quantum State ***
 * Qubit Count : 5
 * Dimension   : 32
 * State vector :
(1,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)
(0,0)

Qulacsの使い方(2):量子ゲート

量子ゲートの生成と作用

デフォルトで実装されている量子ゲートはqulacs.gateモジュールで定義される。

[9]:
import numpy as np
from qulacs import QuantumState
from qulacs.gate import X, RY, DenseMatrix

n = 3
state = QuantumState(n)
state.set_zero_state()
print(state.get_vector())

# 1st-qubitにX操作 (|000> -> |010>)
index = 1
x_gate = X(index)
x_gate.update_quantum_state(state)
print(state.get_vector())

# 1st-qubitをYパウリでpi/4.0回転
angle = np.pi / 4.0
ry_gate = RY(index, angle)
ry_gate.update_quantum_state(state)
print(state.get_vector())

# 2nd-qubitにゲート行列で作成したゲートを作用
dense_gate = DenseMatrix(2, [[0,1],[1,0]])
dense_gate.update_quantum_state(state)
print(state.get_vector())

# ゲートの解放
del x_gate
del ry_gate
del dense_gate
[1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.+0.j 0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]
[0.38268343+0.j 0.        +0.j 0.92387953+0.j 0.        +0.j
 0.        +0.j 0.        +0.j 0.        +0.j 0.        +0.j]
[0.        +0.j 0.        +0.j 0.        +0.j 0.        +0.j
 0.38268343+0.j 0.        +0.j 0.92387953+0.j 0.        +0.j]

事前に定義されているゲートは以下の通りである。

  • single-qubit Pauli operation: Identity, X, Y, Z

  • single-qubit Clifford operation : H, S, Sdag, T, Tdag, sqrtX, sqrtXdag, sqrtY, sqrtYdag

  • two-qubit Clifford operation : CNOT, CZ, SWAP

  • single-qubit Pauli rotation : RX, RY, RZ

  • General Pauli operation : Pauli, PauliRotation

  • IBMQ basis-gate : U1, U2, U3

  • General gate : DenseMatrix

  • Measurement : Measurement

  • Noise : BitFlipNoise, DephasingNoise, IndepenedentXZNoise, DepolarizingNoise

回転ゲートであるRX, RY, RZ, PauliRotationは所定のパウリ演算子\(P\)について、引数\(\theta\)に対して\(\exp(i\frac{\theta}{2}P)\)という操作を行う。それぞれのゲートの詳細はAPIドキュメントを参照されたい。

量子ゲートの合成

続けて作用する量子ゲートを合成し、新たな単一の量子ゲートを生成できる。

[10]:
import numpy as np
from qulacs import QuantumState
from qulacs.gate import X, RY, merge

n = 3
state = QuantumState(n)
state.set_zero_state()

index = 1
x_gate = X(index)
angle = np.pi / 4.0
ry_gate = RY(index, angle)

# ゲートを合成して新たなゲートを生成
# 第一引数が先に作用する
x_and_ry_gate = merge(x_gate, ry_gate)
x_and_ry_gate.update_quantum_state(state)
print(state.get_vector())
[0.38268343+0.j 0.        +0.j 0.92387953+0.j 0.        +0.j
 0.        +0.j 0.        +0.j 0.        +0.j 0.        +0.j]

量子ゲートのゲート行列の和

実際の量子コンピュータでこの操作を行うことは一般に難しいが、量子ゲートのゲート要素の和を取ることができる。 (現状ではcontrol-qubitがある場合の和は動作が未定義なので利用しないことを勧める。)

[11]:
import numpy as np
from qulacs import QuantumState
from qulacs.gate import P0,P1,add, merge, Identity, X, Z

gate00 = merge(P0(0),P0(1))
gate11 = merge(P1(0),P1(1))
# |00><00| + |11><11|
proj_00_or_11 = add(gate00, gate11)
print(proj_00_or_11)

gate_ii_zz = add(Identity(0), merge(Z(0),Z(1)))
gate_ii_xx = add(Identity(0), merge(X(0),X(1)))
proj_00_plus_11 = merge(gate_ii_zz, gate_ii_xx)
# ((|00>+|11>)(<00|+<11|))/2 = (II + ZZ)(II + XX)/4
proj_00_plus_11.multiply_scalar(0.25)
print(proj_00_plus_11)
 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 1 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(1,0) (0,0) (0,0) (0,0)
(0,0) (0,0) (0,0) (0,0)
(0,0) (0,0) (0,0) (0,0)
(0,0) (0,0) (0,0) (1,0)

 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 1 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(0.5,0)   (0,0)   (0,0) (0.5,0)
  (0,0)   (0,0)   (0,0)   (0,0)
  (0,0)   (0,0)   (0,0)   (0,0)
(0.5,0)   (0,0)   (0,0) (0.5,0)

特殊な量子ゲートと一般の量子ゲート

Qulacsにおける基本量子ゲートは以下の二つに分けられる。

  • 特殊ゲート:そのゲートの作用について、専用の高速化がなされた関数があるもの。

  • 一般ゲート:ゲート行列を保持し、行列をかけて作用するもの。

前者は後者に比べ専用の関数が作成されているため高速だが、コントロール量子ビットを増やすなど、量子ゲートの作用を変更する操作が後から行えない。こうした変更をしたい場合、特殊ゲートを一般ゲートに変換する必要があり、gate.to_matrix_gateで実現できる。

[12]:
import numpy as np
from qulacs import QuantumState
from qulacs.gate import to_matrix_gate, X
n = 3
state = QuantumState(n)
state.set_zero_state()

index = 0
x_gate = X(index)
x_mat_gate = to_matrix_gate(x_gate)

# 1st-qubitが0の場合だけゲートを作用
control_index = 1
control_with_value = 0
x_mat_gate.add_control_qubit(control_index, control_with_value)

x_mat_gate.update_quantum_state(state)
print(state.get_vector())
[0.+0.j 1.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+0.j]

量子ゲートのゲート行列の取得

生成した量子ゲートのゲート行列を取得できる。(control量子ビットなどはゲート行列に含まれない。)特にゲート行列を持たない種類のゲート(例えば\(n\)量子ビットのパウリ回転ゲート)などは取得に非常に大きなメモリと時間を要するので気を付けてること。

[13]:
from qulacs.gate import X, to_matrix_gate
gate = X(0)
print(gate)
print(to_matrix_gate(gate))
 *** gate info ***
 * gate name : X
 * target    :
 0 : commute X
 * control   :
 * Pauli     : yes
 * Clifford  : yes
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no

 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute X
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(0,0) (1,0)
(1,0) (0,0)

一般的な量子ゲートの実現

qulacs.gate.DenseMatrixを使うと、一般の行列からゲートを生成することができる。

[14]:
from qulacs.gate import DenseMatrix

# 1-qubit gateの場合
gate = DenseMatrix(0, [[0,1],[1,0]])
print(gate)

# 2-qubit gateの場合
gate = DenseMatrix([0,1], [[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]])
print(gate)
 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(0,0) (1,0)
(1,0) (0,0)

 *** gate info ***
 * gate name : DenseMatrix
 * target    :
 0 : commute
 1 : commute
 * control   :
 * Pauli     : no
 * Clifford  : no
 * Gaussian  : no
 * Parametric: no
 * Diagonal  : no
 * Matrix
(1,0) (0,0) (0,0) (0,0)
(0,0) (1,0) (0,0) (0,0)
(0,0) (0,0) (0,0) (1,0)
(0,0) (0,0) (1,0) (0,0)

Qulacsの使い方(3):量子回路

量子回路の構成

量子回路(QuantumCircuitクラス)は量子ゲートの集合として表され、例えば以下のように量子回路を構成できる。

[15]:
from qulacs import QuantumState, QuantumCircuit
from qulacs.gate import Z
n = 5
state = QuantumState(n)
state.set_zero_state()

# 量子回路を定義
circuit = QuantumCircuit(n)

# 量子回路にhadamardゲートを追加
for i in range(n):
    circuit.add_H_gate(i)

# ゲートを生成し、それを追加することもできる。
for i in range(n):
    circuit.add_gate(Z(i))

# 量子回路を状態に作用
circuit.update_quantum_state(state)

print(state.get_vector())
[ 0.1767767+0.j -0.1767767-0.j -0.1767767-0.j  0.1767767+0.j
 -0.1767767-0.j  0.1767767+0.j  0.1767767+0.j -0.1767767-0.j
 -0.1767767-0.j  0.1767767+0.j  0.1767767+0.j -0.1767767-0.j
  0.1767767+0.j -0.1767767-0.j -0.1767767-0.j  0.1767767+0.j
 -0.1767767-0.j  0.1767767+0.j  0.1767767+0.j -0.1767767-0.j
  0.1767767+0.j -0.1767767-0.j -0.1767767-0.j  0.1767767+0.j
  0.1767767+0.j -0.1767767-0.j -0.1767767-0.j  0.1767767+0.j
 -0.1767767-0.j  0.1767767+0.j  0.1767767+0.j -0.1767767-0.j]

なお、add_gateで追加された量子回路は量子回路の解放時に一緒に解放される。従って、代入したゲートは再利用できない。引数として与えたゲートを再利用したい場合はgate.copyを用いて自身のコピーを作成するか、add_gate_copy関数を用いる必要がある。

量子回路のdepthの計算と最適化

量子ゲートをまとめて一つの量子ゲートとすることで、量子ゲートの数を減らすことができ、数値計算の時間を短縮できることがある。(もちろん、対象となる量子ビットの数が増える場合や、専用関数を持つ量子ゲートを合成して専用関数を持たない量子ゲートにしてしまった場合は、トータルで計算時間が減少するかは状況に依る。)

下記のコードではoptimize関数を用いて、量子回路の量子ゲートをターゲットとなる量子ビットが3つになるまで貪欲法で合成を繰り返している。

[16]:
from qulacs import QuantumCircuit
from qulacs.circuit import QuantumCircuitOptimizer
n = 5
depth = 10
circuit = QuantumCircuit(n)
for d in range(depth):
    for i in range(n):
        circuit.add_H_gate(i)

# depthを計算(depth=10)
print(circuit.calculate_depth())

# 最適化
opt = QuantumCircuitOptimizer()
# 作成を許す最大の量子ゲートのサイズ
max_block_size = 1
opt.optimize(circuit, max_block_size)

# depthを計算(depth=1へ)
print(circuit.calculate_depth())
10
1

量子回路の情報デバッグ

量子回路をprintすると、量子回路に含まれるゲートの統計情報などが表示される。

[17]:
from qulacs import QuantumCircuit
from qulacs.circuit import QuantumCircuitOptimizer
n = 5
depth = 10
circuit = QuantumCircuit(n)
for d in range(depth):
    for i in range(n):
        circuit.add_H_gate(i)
print(circuit)
*** Quantum Circuit Info ***
# of qubit: 5
# of step : 10
# of gate : 50
# of 1 qubit gate: 50
Clifford  : yes
Gaussian  : no


Qulacsの使い方(4):オブザーバブル

(量子力学のオブザーバブルに詳しくない読者は、この節は後で読めば良い)
詳しくは次の第4章で学ぶが、量子力学においては、物理量はエルミート演算子\(A\)で表され、オブザーバブルとも呼ばれる(エルミート演算子は\(A^\dagger=A\)を満たす)。状態\(|\psi\rangle\)について\(A\)の「射影測定」というものを行うと、\(|\psi\rangle\)をAの固有状態\(\{|a_i\rangle\}_i\)(固有値\(a_i\))で展開した係数
\[|\psi\rangle = \sum_i c_i |a_i\rangle\]

に応じた確率\(|c_i|^2\)で測定値\(a_i\)が得られ、その期待値は\(\langle\psi|A|\psi\rangle\)となる。

オブザーバブルの生成

Qulacsでは、オブザーバブルはパウリ演算子\(X,Y,Z\)の(直積の)集合として表現される。(エルミートな演算子は必ずパウリ演算子の直積の和で表されるので。)パウリ演算子は下記のように定義できる。

[18]:
from qulacs import Observable
n = 5
coef = 2.0
# 2.0 X_0 X_1 Y_2 Z_4というパウリ演算子を設定
Pauli_string = "X 0 X 1 Y 2 Z 4"
observable = Observable(n)
observable.add_operator(coef,Pauli_string)

オブザーバブルの評価

状態に対してオブザーバブルの期待値を評価できる。

[19]:
from qulacs import Observable, QuantumState

n = 5
coef = 2.0
Pauli_string = "X 0 X 1 Y 2 Z 4"
observable = Observable(n)
observable.add_operator(coef,Pauli_string)

state = QuantumState(n)
state.set_Haar_random_state()
# 期待値の計算
value = observable.get_expectation_value(state)
print(value)
-0.027383733754500428

Qulacsの使い方(5):変分量子回路

量子回路をParametricQuantumCircuitクラスとして定義すると、通常のQuantumCircuitクラスの関数に加え、変分法を用いて量子回路を最適化するのに便利ないくつかの関数を利用することができる。これは第5章で学ぶ変分量子回路を実装する時に非常に役に立つ。

変分量子回路の利用例

一つの回転角を持つ量子ゲート(X-rot, Y-rot, Z-rot, multi_qubit_pauli_rotation)はパラメトリックな量子ゲートとして量子回路に追加することができる。パラメトリックなゲートとして追加された量子ゲートについては、量子回路の構成後にパラメトリックなゲート数を取り出したり、後から回転角を変更することが可能である。

[20]:
from qulacs import ParametricQuantumCircuit
from qulacs import QuantumState
import numpy as np

n = 5
depth = 10

# construct parametric quantum circuit with random rotation
circuit = ParametricQuantumCircuit(n)
for d in range(depth):
    for i in range(n):
            angle = np.random.rand()
            circuit.add_parametric_RX_gate(i,angle)
            angle = np.random.rand()
            circuit.add_parametric_RY_gate(i,angle)
            angle = np.random.rand()
            circuit.add_parametric_RZ_gate(i,angle)
    for i in range(d%2, n-1, 2):
            circuit.add_CNOT_gate(i,i+1)

# add multi-qubit Pauli rotation gate as parametric gate (X_0 Y_3 Y_1 X_4)
target = [0,3,1,4]
pauli_ids = [1,2,2,1]
angle = np.random.rand()
circuit.add_parametric_multi_Pauli_rotation_gate(target, pauli_ids, angle)

# get variable parameter count, and get current parameter
parameter_count = circuit.get_parameter_count()
param = [circuit.get_parameter(ind) for ind in range(parameter_count)]

# set 3rd parameter to 0
circuit.set_parameter(3, 0.)

# update quantum state
state = QuantumState(n)
circuit.update_quantum_state(state)

# output state and circuit info
print(state)
print(circuit)
 *** Quantum State ***
 * Qubit Count : 5
 * Dimension   : 32
 * State vector :
  (-0.0673932,0.0932352)
   (0.0793816,0.0803179)
 (-0.0240998,-0.0720735)
    (0.0267232,0.103591)
  (-0.089328,-0.0454438)
  (-0.0273612,-0.172908)
   (0.0753719,-0.185454)
    (0.120598,0.0489211)
(-0.0433311,-0.00542669)
   (0.407492,-0.0683546)
    (0.0712898,0.029486)
   (-0.0374001,0.100097)
    (0.0395997,0.166802)
    (0.113313,0.0278069)
  (0.00456149,0.0702255)
    (-0.121551,0.306851)
  (-0.0113109,0.0106071)
    (0.158906,0.0897413)
   (0.276642,0.00709558)
   (-0.163862,0.0615158)
  (-0.0507503,0.0898438)
    (0.221342,0.0332379)
   (-0.125741,-0.130305)
    (0.0463867,0.225922)
   (0.0493533,-0.127222)
   (-0.235716,0.0564754)
   (0.0206978,-0.129814)
     (0.108871,0.107555)
  (0.0917828,-0.0557612)
     (0.180461,0.121747)
   (0.0456678,0.0580318)
    (0.0311144,0.203219)

*** Quantum Circuit Info ***
# of qubit: 5
# of step : 41
# of gate : 171
# of 1 qubit gate: 150
# of 2 qubit gate: 20
# of 3 qubit gate: 0
# of 4 qubit gate: 1
Clifford  : no
Gaussian  : no

*** Parameter Info ***
# of parameter: 151

まとめ

以上、駆け足でQulacsの使い方を見てきた。Qulacsは慣れれば非常に高速で、使い勝手の良いライブラリになっている。各種関数の詳細については、公式のAPIドキュメントチュートリアルも参照されたい。