[PYTHON] Qiskit: Implémentation de Quantum Boltsman Machine

introduction

J'ai posté un article avec le même contenu l'autre jour, mais je me suis endormi et je l'ai supprimé. Je suis désolé.

Cette fois, j'ai implémenté une machine quantique de Boltzmann en utilisant Qiskit. Voici le document de référence. A quantum algorithm to train neural networks using low-depth circuits De plus, la machine de Boltzmann utilisant la machine de recuit quantique est présentée dans l'article de M. minato, je voudrais donc m'en tenir à cela. Apprendre RBM Boltzmann de D-Wave à Quantum Gate Machine

On dit que les machines Boltsman restreintes sont la plus petite unité de réseaux neuronaux. À propos, l'auteur n'est pas un expert en apprentissage automatique, je vais donc laisser l'explication détaillée à d'autres personnes.

QABoM

Cet article l'introduit sous le nom d'algorithme QABoM (Quantum Approximate Boltzmann Machine). Je n'ai pas le temps d'entrer dans les détails, je voudrais donc vous présenter le flux de QABoM présenté en annexe.

L'index à utiliser est le suivant. ・ V: couche visible ・ H: couche cachée ・ U: couche visible + cachée

step 0

Define the full and partial initial Hamiltonians.

H_I = \sum_{j \in u} Z_j \\
H_{\tilde{I}} = \sum_{j \in h} Z_j 

En regardant la formule, plein signifie visible + caché, et partiel signifie caché. prochain Define the full and partial mixer Hamiltonians.

H_M = \sum_{j \in u} X_j \\
H_{\tilde{M}} = \sum_{j \in h} X_j 

Définissez le poids $ J_ {jk} ^ 0 $ et le biais $ B_j ^ 0 $, qui sont les variables de la machine de Boltzmann. Répétez les étapes 1 à 5 ci-dessous pour l'époque 1 et plus.

step 1

Nous définissons l'hamiltonien à coût complet et l'hamiltonien à coût partiel à l'époque n.

\hat{H}_C^n = \sum_{j,k \in u} J_{jk}^n \hat{Z}_j \hat{Z}_k + \sum_{j \in u} B_j^n \hat{Z}_j \\
\hat{H}_{\tilde{C}}^n = \sum_{j,k \in u} J_{jk}^n \hat{Z}_j \hat{Z}_k + \sum_{j \in h} B_j^n \hat{Z}_j

step 2 Unclamped Thermalization

QAOA est exécuté en utilisant les formules définies aux étapes 0 et 1.

a

Initialiser les paramètres d'impulsion-> $ \ gamma, \ beta $

b

Exécutez QAOA en utilisant $ H_M $ et $ H_C $ (Voir d'autres sites pour les commentaires QAOA ...) Trouvez le paramètre d'optimisation. Ici, la valeur attendue est calculée par $ \ hat {H} _C ^ n $.

c

Exécutez le circuit de paramètres avec le paramètre optimal entré, et trouvez les valeurs attendues $ \ <Z_j Z_k> $ et $ \ <Z_j> $.

step 3 Clamped thermalization

Pour chaque chaîne de données $ x $

a

Initialiser les paramètres d'impulsion-> $ \ gamma, \ beta $

b

Encodez $ x $ dans le calque visible.

c

Exécutez QAOA en utilisant $ H_M $ et $ H_C $ (Voir d'autres sites pour les commentaires QAOA ...) Trouvez le paramètre d'optimisation. Ici, la valeur attendue est prise par $ \ hat {H} _ {\ tilde {C}} ^ n $.

d

Exécutez le circuit de paramètres avec le paramètre optimal entré, et trouvez les valeurs attendues $ \ <Z_j Z_k> $ et $ \ <Z_j> $.

step 4

Nous mettrons à jour les paramètres. Où $ \ <\ bar {\ cdots}> _D $ est la moyenne des résultats de l'étape 3.

\delta J_{jk}^n = <\bar{Z_j Z_k}>_D -< Z_j Z_k> \\
\delta B_j^n = < \bar{Z_j}> - <Z_j>_D \\
J_j^{n+1} = J_j^n + \delta J_j^n \\
B_j^{n+1} = B_j^n + \delta B_j^n

step 5

epoch = n+1 back to step 1

code

Importation et initialisation de classe

python


# coding: utf-8

from qiskit.aqua.utils import tensorproduct
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.quantum_info.analysis import average_data

import numpy as np
from copy import deepcopy

from QAOA import QAOA
from my_functions import sigmoid

class QBM:

    def __init__(self, num_visible=2, num_hidden=1, steps=3,
                 tmep=1.0, quant_meas_num=None, bias=False, reduced=False):
        self.visible_units = num_visible  # v
        self.hidden_units = num_hidden  # h
        self.total_units = self.visible_units + self.hidden_units
        self.quant_meas_num = quant_meas_num
        self.qaoa_steps = steps
        self.beta_temp = tmep
        self.state_prep_angle = np.arctan(np.exp(-1 / self.beta_temp)) * 2.0
        self.param_wb = 0.1 * np.sqrt(6. / self.total_units)
        self.WEIGHTS = np.asarray(np.zeros(shape=[self.visible_units, self.hidden_units]))
        if bias:
            self.BIAS = np.asarray(np.zeros(shape=[self.hidden_units]))
        else:
            self.BIAS = None
        self.reduced = reduced
        self.obs = self.observable()
        self.obs_tilde = self.observable(tilde=True)
        self.data_point = None

Création d'observable. Vous pouvez créer $$ en définissant tilde = True.

python



    def Zi_Zj(self, q1, q2):
        I_mat = np.array([[1, 0], [0, 1]])
        Z_mat = np.array([[1, 0], [0, -1]])
        if q1 == 0 or q2 == 0:
            tensor = Z_mat
        else:
            tensor = I_mat
        for i in range(1, self.total_units):
            if i == q1 or i == q2:
                tensor = tensorproduct(tensor, Z_mat)
            else:
                tensor = tensorproduct(tensor, I_mat)
        return tensor

    def Zi(self, q):
        I_mat = np.array([[1, 0], [0, 1]])
        Z_mat = np.array([[1, 0], [0, -1]])
        if q == 0:
            tensor = Z_mat
        else:
            tensor = I_mat
        for i in range(1, self.total_units):
            if i == q:
                tensor = tensorproduct(tensor, Z_mat)
            else:
                tensor = tensorproduct(tensor, I_mat)
        return tensor

    def observable(self, tilde=False):
        visible_indices = [i for i in range(self.visible_units)]
        hidden_indices = [i + self.visible_units for i in range(self.hidden_units)]
        total_indices = [i for i in range(self.total_units)]

        obs = np.zeros((2**self.total_units, 2**self.total_units))

        for q1 in visible_indices:
            for q2 in hidden_indices:
                obs += -1.0 * self.Zi_Zj(q1, q2)

        if self.BIAS is not None:
            if tilde:
                for q in hidden_indices:
                    obs += -1.0 * self.Zi(q)
            elif not tilde:
                for q in total_indices:
                    obs += -1.0 * self.Zi(q)
        return obs

Créez le circuit de paramètres.

python



    def U_circuit(self, params, qc):
        visible_indices = [i for i in range(self.visible_units)]
        hidden_indices = [i + self.visible_units for i in range(self.hidden_units)]
        total_indices = [i for i in range(self.total_units)]

        p = 2

        alpha = self.state_prep_angle
        for i in total_indices:
            qc.rx(alpha, i)
        for i in visible_indices:
            for j in hidden_indices:
                qc.cx(i, j)

        beta, gamma = params[:p], params[p:]

        def add_U_X(qc, beta):
            for i in total_indices:
                qc.rx(-2**beta, i)
            return qc

        def add_U_C(qc, gamma):
            for q1 in visible_indices:
                for q2 in hidden_indices:
                    qc.cx(q1, q2)
                    qc.rz(-2**gamma, q2)
                    qc.cx(q1, q2)
            return qc

        for i in range(p):
            qc = add_U_C(qc, gamma[i])
            qc = add_U_X(qc, beta[i])

        return qc

Exécutez l'étape 2.

python



    def unclamped_circuit(self, params):
        qr = QuantumRegister(self.total_units)
        cr = ClassicalRegister(self.total_units)
        qc = QuantumCircuit(qr, cr)
        qc = self.U_circuit(params, qc)
        qc.measure(range(self.total_units), range(self.total_units))
        return qc

    def make_unclamped_QAOA(self):
        qaoa = QAOA(qc=self.unclamped_circuit, observable=self.obs, num_shots=10000, p=2)
        counts = qaoa.qaoa_run()
        return counts

J'utilise ici une bibliothèque auto-créée appelée QAOA, mais je la présenterai ci-dessous. Vient ensuite l'étape 3. La différence avec l'étape 2 est que le code des données est inclus.

python



    def clamped_circuit(self, params):
        qr = QuantumRegister(self.total_units)
        cr = ClassicalRegister(self.total_units)
        qc = QuantumCircuit(qr, cr)
        for i in range(len(self.data_point)):
            if self.data_point[i] == 1:
                qc.x(i)
        qc = self.U_circuit(params, qc)
        qc.measure(range(self.total_units), range(self.total_units))
        return qc

    def make_clamped_QAOA(self, data_point):
        self.data_point = data_point
        qaoa = QAOA(qc=self.clamped_circuit, observable=self.obs_tilde, num_shots=10000, p=2)
        counts = qaoa.qaoa_run()
        return counts

Enfin, l'exécution de l'apprentissage.

python



    def train(self, DATA, learning_rate=1, n_epochs=100, quantum_percentage=1.0, classical_percentage=0.0):

        assert (quantum_percentage + classical_percentage == 1.0)
        DATA = np.asarray(DATA)
        assert (len(DATA[0]) <= self.visible_units)

        for epoch in range(n_epochs):

            print('Epoch: {}'.format(epoch+1))

            visible_indices = [i for i in range(self.visible_units)]
            hidden_indices = [i + self.visible_units for i in range(self.hidden_units)]
            total_indices = [i for i in range(self.total_units)]

            new_weights = deepcopy(self.WEIGHTS)
            if self.BIAS is not None:
                new_bias = deepcopy(self.BIAS)

            counts = self.make_unclamped_QAOA()
            unc_neg_phase_quant = np.zeros_like(self.WEIGHTS)
            for i in range(self.visible_units):
                for j in range(self.hidden_units):
                    model_expectation = average_data(counts, self.Zi_Zj(visible_indices[i], hidden_indices[j]))
                    unc_neg_phase_quant[i][j] = model_expectation

            unc_neg_phase_quant *= (1. / float(len(DATA)))

            if self.BIAS is not None:
                unc_neg_phase_quant_bias = np.zeros_like(self.BIAS)
                for i in range(self.hidden_units):
                    model_expectation = average_data(counts, self.Zi(hidden_indices[i]))
                    unc_neg_phase_quant_bias[i] = model_expectation

                unc_neg_phase_quant_bias *= (1. / float(len(DATA)))

            pos_hidden_probs = sigmoid(np.dot(DATA, self.WEIGHTS))
            pos_hidden_states = pos_hidden_probs > np.random.rand(len(DATA), self.hidden_units)
            pos_phase_classical = np.dot(DATA.T, pos_hidden_probs) * 1. / len(DATA)

            c_pos_phase_quant = np.zeros_like(self.WEIGHTS)
            if self.BIAS is not None:
                c_pos_phase_quant_bias = np.zeros_like(self.BIAS)

            if not self.reduced:

                iter_dat = len(DATA)
                pro_size = len(DATA)
                pro_step = 1

                for data in DATA:
                    counts = self.make_clamped_QAOA(data)
                    ct_pos_phase_quant = np.zeros_like(self.WEIGHTS)

                    for i in range(self.visible_units):
                        for j in range(self.hidden_units):
                            model_expectation = average_data(counts, self.Zi_Zj(visible_indices[i], hidden_indices[j]))
                            ct_pos_phase_quant[i][j] = model_expectation
                    c_pos_phase_quant += ct_pos_phase_quant

                    if self.BIAS is not None:
                        ct_pos_phase_quant_bias = np.zeros_like(self.BIAS)
                        for i in range(self.hidden_units):
                            model_expectation = average_data(counts, self.Zi(hidden_indices[i]))
                            ct_pos_phase_quant_bias[i] = model_expectation
                        c_pos_phase_quant_bias *= ct_pos_phase_quant_bias

                    pro_bar = ('==' * pro_step) + ('--' * (pro_size - pro_step))
                    print('\r[{0}] {1}/{2}'.format(pro_bar, pro_step, pro_size), end='')
                    pro_step += 1

                c_pos_phase_quant *= (1. / float(len(DATA)))
                if self.BIAS is not None:
                    c_pos_phase_quant_bias *= (1. / float(len(DATA)))

            neg_visible_activations = np.dot(pos_hidden_states, self.WEIGHTS.T)
            neg_visible_probs = sigmoid(neg_visible_activations)

            neg_hidden_activations = np.dot(neg_visible_probs, self.WEIGHTS)
            neg_hidden_probs = sigmoid(neg_hidden_activations)

            neg_phase_classical = np.dot(
                neg_visible_probs.T, neg_hidden_probs) * 1. / len(DATA)

            new_weights += learning_rate * \
                           (classical_percentage * (pos_phase_classical - neg_phase_classical) +
                            quantum_percentage * (c_pos_phase_quant - unc_neg_phase_quant))

            if self.BIAS is not None:
                new_bias = new_bias + learning_rate * \
                           (quantum_percentage * (c_pos_phase_quant_bias - unc_neg_phase_quant_bias))

            self.WEIGHTS = deepcopy(new_weights)
            print(self.WEIGHTS)
            if self.BIAS is not None:
                self.BIAS = deepcopy(new_bias)
            with open("RBM_info.txt", "w") as f:
                np.savetxt(f, self.WEIGHTS)
                if self.BIAS is not None:
                    np.savetxt(f, self.BIAS)
            with open("RBM_history.txt", "a") as f:
                np.savetxt(f, self.WEIGHTS)
                if self.BIAS is not None:
                    np.savetxt(f, self.BIAS)
                f.write(str('*' * 72) + '\n')
            print('')

        print("Training Done! ")

    def transform(self, DATA):
        return sigmoid(np.dot(DATA, self.WEIGHTS))


if __name__ == '__main__':

    qbm = QBM(num_visible=6, num_hidden=2,bias=True)

    train_data = [[1, 1, 1, 1, 1, 1],
                  [1, 1, -1, -1, 1, 1], [1, -1, -1, -1, 1, 1], [1, 1, 1, -1, -1, -1]]

    qbm.train(DATA=train_data, n_epochs=100, quantum_percentage=1.0, classical_percentage=0.0)

    print(qbm.transform(train_data))

QAOA

La bibliothèque QAOA est une extension à usage général du code utilisé dans [Implémentation de QAOA sans Qiskit Aqua] précédemment introduite (https://qiita.com/hotaru_46/items/c4b51e3038d260ef1fc8).

QAOA.py


# coding: utf-8

from qiskit import BasicAer, execute
from qiskit.quantum_info.analysis import average_data

from scipy.optimize import minimize
import numpy as np
import random


def classica_minimize(cost_func, initial_params, options, method='powell'):
    result = minimize(cost_func, initial_params, options=options, method=method)
    return result.x


class QAOA:

    def __init__(self, qc, observable, num_shots, p=1, initial_params=None):
        self.QC = qc
        self.obs = observable
        self.SHOTS = num_shots
        self.P = p
        if initial_params is None:
            self.initial_params = [0.1 for _ in range(self.P * 2)]
        else:
            self.initial_params = initial_params

    def QAOA_output_layer(self, params):
        qc = self.QC(params)
        backend = BasicAer.get_backend('qasm_simulator')
        results = execute(qc, backend, shots=self.SHOTS).result()
        counts = results.get_counts(qc)
        expectation = average_data(counts, self.obs)
        return expectation

    def minimize(self):
        initial_params = np.array(self.initial_params)
        opt_params = classica_minimize(self.QAOA_output_layer, initial_params,
                                       options={'maxiter':500}, method='powell')
        return opt_params

    def qaoa_run(self):
        opt_params = self.minimize()
        qc = self.QC(opt_params)
        backend = BasicAer.get_backend('qasm_simulator')
        results = execute(qc, backend, shots=self.SHOTS).result()
        counts = results.get_counts(qc)
        return counts

if __name__ == '__main__':
    pass

Résultat du calcul

Cette fois, nous avons mené des expériences informatiques en utilisant les mêmes données sur la machine classique de Boltzmann restreinte et les avons comparées. La machine Boltzmann classique restreinte a été implémentée par une autre personne, je l'ai donc utilisée comme référence. Implémentation de la machine de contrainte Boltzmann avec python

Environnement d'exécution

OS: macOS Catalina ver 10.15.5 CPU: intel Core i7 Mémoire: 16 Go

Paramètres

Taux d'apprentissage: 0,2 epochs: 100

Résultats de la machine Boltzmann classique

[[7.57147606e-220 4.69897282e-106]
 [1.00548918e-308 1.00000000e+000]
 [1.00000000e+000 3.79341879e-197]
 [1.00000000e+000 7.56791638e-202]
 [1.00000000e+000 2.85625356e-196]
 [1.00000000e+000 2.51860403e-243]
 [0.00000000e+000 1.00000000e+000]
 [9.02745172e-142 1.00000000e+000]]

Résultats QABoM

[[0.59991237 0.68602485]
 [0.74497707 0.89553788]
 [0.26436565 0.113528  ]
 [0.2682931  0.1131361 ]
 [0.58273232 0.3268427 ]
 [0.41413436 0.14647751]
 [0.74056361 0.94317095]
 [0.7317069  0.8868639 ]]

Soit dit en passant, cela prend presque une demi-journée à calculer. (J'ai oublié de mesurer ...) Pour le moment, le même résultat que le RBM classique a été obtenu. Cependant, la solution n'est pas aussi claire que la solution classique.

Résumé

Cette fois, j'ai présenté l'implémentation de QABoM qui a été publiée dans le journal. Je pense que je vais faire un peu plus d'expériences informatiques et mettre à jour l'article.

Je suis désolé pour mon pauvre japonais. Nous nous efforcerons d'améliorer nos compétences linguistiques.

Liste de références

QABoM: A quantum algorithm to train neural networks using low-depth circuits RBM: Implémentation de la machine de contrainte Boltzmann avec python

Recommended Posts

Qiskit: Implémentation de Quantum Boltsman Machine
Qiskit: Implémentation des états d'hypergraphes quantiques
Qiskit: mise en œuvre de l'apprentissage des circuits quantiques (QCL)
Implémentation informatique quantique de Quantum Walk 2
Implémentation informatique quantique de Quantum Walk 3
Implémentation informatique quantique de Quantum Walk 1
Implémentation informatique quantique de la marche quantique à 3 états
[Avec une explication simple] Implémentation Scratch d'une machine Boltsman profonde avec Python ②
[Avec une explication simple] Implémentation Scratch d'une machine Boltzmann profonde avec Python ①
Qiskit: Implémentation de QAOA sans Qiskit Aqua
Algorithme d'apprentissage automatique (implémentation de la classification multi-classes)
[Entretien de codage] Implémentation de la machine cryptographique Enigma (Python)
Qiskit: Réalisation de neurones artificiels avec des circuits quantiques (implémentation)
Téléportation quantique avec Qiskit!
À propos des tests dans la mise en œuvre de modèles d'apprentissage automatique
Implémentation de la séquence de Fibonacci
Qiskit: Transformée de Fourier quantique
Implémentation de TF-IDF à l'aide de gensim
Implémentation de MathJax sur Sphinx
Bases de l'apprentissage automatique (mémoire)
Explication et mise en œuvre de SocialFoceModel
Mise en œuvre de la théorie des jeux - Le dilemme du prisonnier -
Mise en œuvre d'une analyse de composants indépendante
Implémentation Python du filtre à particules
Importance des ensembles de données d'apprentissage automatique
Implémentation du tri rapide en Python
Apprentissage par renforcement profond 2 Mise en œuvre de l'apprentissage par renforcement
Implémentation de Scale-Space pour SIFT