注釈
このページは docs/tutorials/04_qgans_for_loading_random_distributions.ipynb から生成されました。
qGANによるランダム分布の書き込み¶
\(k\) 次元のデータサンプルが与えられた場合、量子敵対的生成ネットワーク(qGAN) を使用して、データの基礎となるランダム分布を学習し、それを量子状態に直接ロードします。
ここで、 \(p_{\theta}^{j}\) は、基底状態 \(\big| j\rangle\) の発生確率を表します。
qGAN学習の目的は、状態 \(\big| g_{\theta}\rangle\) を生成することです。ここで、 \(p_{\theta}^{j}\) は、 \(j\in \left\{0, \ldots, {2^n-1} \right\}\) の場合、 学習データ \(X=\left\{x^0, \ldots, x^{k-1} \right\}\) の基礎となる分布に近い確率分布を表します。
詳細については、「 Quantum Generative Adversarial Networks for Learning and Loading Random Distributions Zoufal, Lucchi, Woerner [2019] 」を参照してください。
学習済みの qGAN の実用例として、金融デリバティブの価格設定については、チュートリアルの「 Option Pricing with qGANs 」を参照してください。
[1]:
import numpy as np
seed = 71
np.random.seed = seed
import matplotlib.pyplot as plt
%matplotlib inline
from qiskit import QuantumRegister, QuantumCircuit, BasicAer
from qiskit.circuit.library import TwoLocal
from qiskit.utils import QuantumInstance, algorithm_globals
from qiskit_machine_learning.algorithms import NumPyDiscriminator, QGAN
algorithm_globals.random_seed = seed
学習データのロード¶
まず \(k\) 次元の学習データ・サンプルを読み込む必要があります (ここでは k=1 です) 。
次にデータの分解能、すなわち最小・最大データ値と、各データ次元を表現するために用いられる量子ビット数を設定します。
[2]:
# Number training data samples
N = 1000
# Load data samples from log-normal distribution with mean=1 and standard deviation=1
mu = 1
sigma = 1
real_data = np.random.lognormal(mean=mu, sigma=sigma, size=N)
# Set the data resolution
# Set upper and lower data values as list of k min/max data values [[min_0,max_0],...,[min_k-1,max_k-1]]
bounds = np.array([0.0, 3.0])
# Set number of qubits per data dimension as list of k qubit values[#q_0,...,#q_k-1]
num_qubits = [2]
k = len(num_qubits)
qGANの初期化¶
qGAN は、量子生成器 \(G_{\theta}\) 、いわゆるansatzと、ニューラル・ネットワークの 古典識別器 \(D_{\phi}\) で構成されています。
量子生成器を実装するために、一様分布を入力に取るような、\(R_Y\) 回転と \(CZ\) ゲートからなる深さ \(1\) のansatzを用います。特に \(k>1\) の場合、生成器のパラメーターは慎重に選ばれなければなりません。例えば、回路が深いほどより複雑な構造を表現できるので、回路の深さは \(>1\) であるべきです。
ここで使用する古典識別器は、 NumPyによるニューラル・ネットワークの実装に基づいています。また、 Qiskit のインストール時にはデフォルトでインストールされていない PyTorch ベースの識別器もあります。詳細は「 Optional Install 」を参照してください。
ここでは、両方のネットワークが ADAM 最適化アルゴリズムによって更新されます (ADAM は qGAN のデフォルトのオプティマイザーです) 。
[3]:
# Set number of training epochs
# Note: The algorithm's runtime can be shortened by reducing the number of training epochs.
num_epochs = 10
# Batch size
batch_size = 100
# Initialize qGAN
qgan = QGAN(real_data, bounds, num_qubits, batch_size, num_epochs, snapshot_dir=None)
qgan.seed = 1
# Set quantum instance to run the quantum generator
quantum_instance = QuantumInstance(
backend=BasicAer.get_backend("statevector_simulator"), seed_transpiler=seed, seed_simulator=seed
)
# Set entangler map
entangler_map = [[0, 1]]
# Set an initial state for the generator circuit as a uniform distribution
# This corresponds to applying Hadamard gates on all qubits
init_dist = QuantumCircuit(sum(num_qubits))
init_dist.h(init_dist.qubits)
# Set the ansatz circuit
ansatz = TwoLocal(int(np.sum(num_qubits)), "ry", "cz", entanglement=entangler_map, reps=1)
# Set generator's initial parameters - in order to reduce the training time and hence the
# total running time for this notebook
init_params = [3.0, 1.0, 0.6, 1.6]
# You can increase the number of training epochs and use random initial parameters.
# init_params = np.random.rand(ansatz.num_parameters_settable) * 2 * np.pi
# Set generator circuit by adding the initial distribution infront of the ansatz
g_circuit = ansatz.compose(init_dist, front=True)
# Set quantum generator
qgan.set_generator(generator_circuit=g_circuit, generator_init_params=init_params)
# The parameters have an order issue that following is a temp. workaround
qgan._generator._free_parameters = sorted(g_circuit.parameters, key=lambda p: p.name)
# Set classical discriminator neural network
discriminator = NumPyDiscriminator(len(num_qubits))
qgan.set_discriminator(discriminator)
qGAN の学習¶
学習では、識別器と生成器のパラメーターが、以下の損失関数に基づいて交互に更新されます。
および
ここで \(m\) はバッチ・サイズを、\(g^l\) は量子生成器によって生成されたデータ・サンプルを表しています。
このノートブックの目的のために、既知の初期点 (init_params
) を指定することで、学習をより簡単にしていることに注意してください。このような予備知識がないと、学習が時間を要するかもしれません。
[4]:
# Run qGAN
result = qgan.run(quantum_instance)
[5]:
print("Training results:")
for key, value in result.items():
print(f" {key} : {value}")
Training results:
params_d : [ 0.03697158 0.61015372 -0.48103428 ... -0.1661673 -0.20186384
-0.08584337]
params_g : [2.95229918 0.9522102 0.55218478 1.64793094]
loss_d : 0.6925
loss_g : [0.7246]
rel_entr : 0.107
学習過程と結果¶
次に、学習における生成器と識別器の損失関数の推移、および学習分布と目標分布の間の相対エントロピーの推移をプロットします。
最後に、訓練分布の累積分布関数 (cumulative distribution function、CDF) と目標分布の CDF も比較します。
[6]:
# Plot progress w.r.t the generator's and the discriminator's loss function
t_steps = np.arange(num_epochs)
plt.figure(figsize=(6, 5))
plt.title("Progress in the loss function")
plt.plot(
t_steps, qgan.g_loss, label="Generator loss function", color="mediumvioletred", linewidth=2
)
plt.plot(
t_steps, qgan.d_loss, label="Discriminator loss function", color="rebeccapurple", linewidth=2
)
plt.grid()
plt.legend(loc="best")
plt.xlabel("time steps")
plt.ylabel("loss")
plt.show()

[7]:
# Plot progress w.r.t relative entropy
plt.figure(figsize=(6, 5))
plt.title("Relative Entropy")
plt.plot(
np.linspace(0, num_epochs, len(qgan.rel_entr)), qgan.rel_entr, color="mediumblue", lw=4, ls=":"
)
plt.grid()
plt.xlabel("time steps")
plt.ylabel("relative entropy")
plt.show()

[8]:
# Plot the CDF of the resulting distribution against the target distribution, i.e. log-normal
log_normal = np.random.lognormal(mean=1, sigma=1, size=100000)
log_normal = np.round(log_normal)
log_normal = log_normal[log_normal <= bounds[1]]
temp = []
for i in range(int(bounds[1] + 1)):
temp += [np.sum(log_normal == i)]
log_normal = np.array(temp / sum(temp))
plt.figure(figsize=(6, 5))
plt.title("CDF (Cumulative Distribution Function)")
samples_g, prob_g = qgan.generator.get_output(qgan.quantum_instance, shots=10000)
samples_g = np.array(samples_g)
samples_g = samples_g.flatten()
num_bins = len(prob_g)
plt.bar(samples_g, np.cumsum(prob_g), color="royalblue", width=0.8, label="simulation")
plt.plot(
np.cumsum(log_normal), "-o", label="log-normal", color="deepskyblue", linewidth=4, markersize=12
)
plt.xticks(np.arange(min(samples_g), max(samples_g) + 1, 1.0))
plt.grid()
plt.xlabel("x")
plt.ylabel("p(x)")
plt.legend(loc="best")
plt.show()

[9]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | None |
Terra | 0.17.0.dev0+346ffa8 |
Aer | 0.8.0 |
Ignis | 0.6.0.dev0+d6f1ad7 |
Aqua | None |
IBM Q Provider | 0.13.0.dev0+10f19e0 |
System information | |
Python | 3.8.8 (default, Feb 24 2021, 13:46:16) [Clang 10.0.0 ] |
OS | Darwin |
CPUs | 6 |
Memory (Gb) | 16.0 |
Wed Mar 31 23:30:54 2021 CEST |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.