[PYTHON] Créer un backend Blueqat ~ Partie 2

Bibliothèque Blueqat pour une programmation quantique facile

Se développe. https://github.com/Blueqat/Blueqat

Dernière fois

Création d'un backend Blueqat ~ Partie 1 a expliqué comment créer un backend de simulateur qui peut accepter l'entrée OpenQ ASM. Cette fois, je vais vous expliquer comment faire un backend dans le cas général où il ne l'est pas.

Comment tout faire vous-même

Au minimum, vous pouvez créer un backend en héritant de Backend et en implémentant la méthode run. Il réimplémente également la méthode copy si une copie de l'objet backend lui-même n'est pas pratique pour copy.deepcopy.

La méthode run est sous la formerun (self, gates, n_qubits, * args, ** kwargs). gates est la liste des objets de porte quantique, n_qubits est le nombre de bits quantiques, * args, ** kwargs est lorsque l'utilisateur appelle Circuit (). Run () L'argument de run est passé.

Si vous implémentez cela, vous pouvez faire un backend, mais comme il est trop lancé au développeur, nous avons préparé le flux suivant. Cela semble déroutant, mais si vous suivez le flux, cela peut être un peu plus facile à mettre en œuvre. De plus, lors de la réimplémentation de la méthode run, vous pouvez utiliser celle que vous avez préparée.

Comment suivre le flux que vous avez préparé

Flux global

Par défaut, l'appel de la méthode run appelle le code suivant.

    def _run(self, gates, n_qubits, args, kwargs):
        gates, ctx = self._preprocess_run(gates, n_qubits, args, kwargs)
        self._run_gates(gates, n_qubits, ctx)
        return self._postprocess_run(ctx)

    def _run_gates(self, gates, n_qubits, ctx):
        """Iterate gates and call backend's action for each gates"""
        for gate in gates:
            action = self._get_action(gate)
            if action is not None:
                ctx = action(gate, ctx)
            else:
                ctx = self._run_gates(gate.fallback(n_qubits), n_qubits, ctx)
        return ctx

Ce que les développeurs backend devraient implémenter

est.

À propos de CTX

Remarquez la variable intitulée «ctx» ici. Cette variable est une variable pour maintenir l'état du début à la fin de run. (Si vous n'en avez pas besoin, vous pouvez utiliser None, mais je pense qu'il y a peu de cas où vous n'en avez pas du tout besoin.) Puisque le backend lui-même est également un objet ordinaire, vous pouvez lui donner un état en définissant self.foo = ... etc., mais cela causera des bogues, etc., veuillez donc utiliser ctx autant que possible.

Comme exemple de ce qu'il faut enregistrer dans ctx

--Nombre de bits quantiques --Vecteur d'état au milieu du calcul

Etc. (Attention à ne pas savoir pendant l'exécution à moins que vous ne laissiez le nombre de bits quantiques dans ctx)

Aussi, si vous regardez le code ci-dessus, faites attention à ctx

--Créez un objet ctx avec _preprocess_run --Lors de l'appel de l'action pour chaque porte, un objet ctx est passé. Une action devrait également être implémentée pour renvoyer un objet ctx --_postprocess_run reçoit ctx et renvoie le résultat de run

C'est devenu un flux.

définition de l'action

Les circuits quantiques sont réalisés en disposant des portes. Une méthode d'implémentation backend typique consiste à appliquer les portes dans une ligne en séquence.

L'opération d'application d'une porte est appelée ici action. Pour implémenter une action, ajoutez simplement une méthode,

def gate_{Nom de la porte}(self, gate, ctx):
    #Quelque chose d'implémenté
    return ctx

Faire.

En tant que fonctionnalité de Blueqat, il y avait une notation de tranche telle que Circuit (). H [:], mais il existe également une méthode pour séparer la notation de tranche, et pour idx dans gate.target_iter (quantité nombre de bits) Si vous aimez) , vous pouvez obtenir l'indice de 1 porte de bit quantique. De plus, l'indice de 2 portes de bits quantiques telles que CNOT devrait être pour c, t dans gate.control_target_iter (nombre de bits quantiques). Le nombre de bits quantiques est nécessaire ici afin de savoir combien de bits quantiques appliquer lorsque vous faites quelque chose comme Circuit (). H [:].

Action à définir

Toutes les portes ne doivent pas être implémentées. Par exemple, des portes T et des portes S peuvent être réalisées s'il y a une porte Z rotative (RZ), donc du côté Blueqat, si elle n'est pas implémentée, veillez à utiliser une autre porte à la place. Je suis.

De plus, s'il n'y a pas de porte alternative, ce sera une erreur si elle est utilisée, mais ce ne sera pas une erreur si elle n'est pas utilisée, vous pouvez donc créer un backend qui ne prend en charge que certaines portes. (Par exemple, si vous implémentez uniquement la porte X, la porte CX et la porte CCX, vous pouvez créer un backend spécialisé pour les circuits logiques classiques. Vous pouvez l'implémenter avec juste une opération de bit, afin de créer quelque chose qui fonctionne à grande vitesse.)

Les portes qui doivent être implémentées peuvent être triées à l'avenir, mais à l'heure actuelle Les mesures sont X, Y, Z, H, CZ, CX, RX, RY, RZ, CCZ, U3. (Les mesures définissent les actions de la même manière que les portes)

Voir l'exemple de mise en œuvre

Un backend légèrement inhabituel est QasmOutputBackend. Ce n'est pas un simulateur, mais un backend pour convertir les circuits quantiques de Blueqat en OpenQ ASM.

ctx contient une liste de lignes OpenQASM et le nombre de bits quantiques. De plus, chaque action ajoute une ligne à la liste.

Cliquez ici pour le code complet.

class QasmOutputBackend(Backend):
    """Backend for OpenQASM output."""
    def _preprocess_run(self, gates, n_qubits, args, kwargs):
        def _parse_run_args(output_prologue=True, **_kwargs):
            return { 'output_prologue': output_prologue }

        args = _parse_run_args(*args, **kwargs)
        if args['output_prologue']:
            qasmlist = [
                "OPENQASM 2.0;",
                'include "qelib1.inc";',
                f"qreg q[{n_qubits}];",
                f"creg c[{n_qubits}];",
            ]
        else:
            qasmlist = []
        return gates, (qasmlist, n_qubits)

    def _postprocess_run(self, ctx):
        return "\n".join(ctx[0])

    def _one_qubit_gate_noargs(self, gate, ctx):
        for idx in gate.target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername} q[{idx}];")
        return ctx

    gate_x = _one_qubit_gate_noargs
    gate_y = _one_qubit_gate_noargs
    gate_z = _one_qubit_gate_noargs
    gate_h = _one_qubit_gate_noargs
    gate_t = _one_qubit_gate_noargs
    gate_s = _one_qubit_gate_noargs

    def _two_qubit_gate_noargs(self, gate, ctx):
        for control, target in gate.control_target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername} q[{control}],q[{target}];")
        return ctx

    gate_cz = _two_qubit_gate_noargs
    gate_cx = _two_qubit_gate_noargs
    gate_cy = _two_qubit_gate_noargs
    gate_ch = _two_qubit_gate_noargs
    gate_swap = _two_qubit_gate_noargs

    def _one_qubit_gate_args_theta(self, gate, ctx):
        for idx in gate.target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername}({gate.theta}) q[{idx}];")
        return ctx

    gate_rx = _one_qubit_gate_args_theta
    gate_ry = _one_qubit_gate_args_theta
    gate_rz = _one_qubit_gate_args_theta

    def gate_i(self, gate, ctx):
        for idx in gate.target_iter(ctx[1]):
            ctx[0].append(f"id q[{idx}];")
        return ctx

    def gate_u1(self, gate, ctx):
        for idx in gate.target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername}({gate.lambd}) q[{idx}];")
        return ctx

    def gate_u2(self, gate, ctx):
        for idx in gate.target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername}({gate.phi},{gate.lambd}) q[{idx}];")
        return ctx

    def gate_u3(self, gate, ctx):
        for idx in gate.target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername}({gate.theta},{gate.phi},{gate.lambd}) q[{idx}];")
        return ctx

    def gate_cu1(self, gate, ctx):
        for c, t in gate.control_target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername}({gate.lambd}) q[{c}],q[{t}];")
        return ctx

    def gate_cu2(self, gate, ctx):
        for c, t in gate.control_target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername}({gate.phi},{gate.lambd}) q[{c}],q[{t}];")
        return ctx

    def gate_cu3(self, gate, ctx):
        for c, t in gate.control_target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername}({gate.theta},{gate.phi},{gate.lambd}) q[{c}],q[{t}];")
        return ctx

    def _three_qubit_gate_noargs(self, gate, ctx):
        c0, c1, t = gate.targets
        ctx[0].append(f"{gate.lowername} q[{c0}],q[{c1}],q[{t}];")
        return ctx

    gate_ccx = _three_qubit_gate_noargs
    gate_cswap = _three_qubit_gate_noargs

    def gate_measure(self, gate, ctx):
        for idx in gate.target_iter(ctx[1]):
            ctx[0].append(f"measure q[{idx}] -> c[{idx}];")
        return ctx

Voir _preprocess_run

    def _preprocess_run(self, gates, n_qubits, args, kwargs):
        def _parse_run_args(output_prologue=True, **_kwargs):
            return { 'output_prologue': output_prologue }

        args = _parse_run_args(*args, **kwargs)
        if args['output_prologue']:
            qasmlist = [
                "OPENQASM 2.0;",
                'include "qelib1.inc";',
                f"qreg q[{n_qubits}];",
                f"creg c[{n_qubits}];",
            ]
        else:
            qasmlist = []
        return gates, (qasmlist, n_qubits)

La première chose que nous faisons est d'analyser les options.

Puisqu'il peut être exécuté avec l'option ʻoutput_prologue comme Circuit (). Run (backend = 'qasm_output', output_prologue = False) `, l'option est analysée. Cette option est True par défaut, mais sera ajoutée au début d'OpenQ ASM si False est spécifié.

OPENQASM 2.0;
include "qelib1.inc";
qreg q[Nombre de bits];
creg c[Nombre de bits];

Est omis.

Ensuite, ctx est une liste de lignes OpenQASM et un tuple de bits quantiques.

Il renvoie gates et ctx, mais gates renvoie simplement ce qui a été passé en argument. J'ai décidé de retourner des portes avec _preprocess_run parce que je voulais manipuler la colonne de porte pour l'optimisation, etc., mais s'il n'y a pas besoin de le faire, l'argument reçu est retourné tel quel.

Voir l'action

    def _one_qubit_gate_noargs(self, gate, ctx):
        for idx in gate.target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername} q[{idx}];")
        return ctx

    gate_x = _one_qubit_gate_noargs
    gate_y = _one_qubit_gate_noargs
    gate_z = _one_qubit_gate_noargs

    #Omis car il y en a beaucoup

    def gate_cu3(self, gate, ctx):
        for c, t in gate.control_target_iter(ctx[1]):
            ctx[0].append(f"{gate.lowername}({gate.theta},{gate.phi},{gate.lambd}) q[{c}],q[{t}];")
        return ctx

Je vais le faire comme ça. Puisqu'il est difficile de faire des portes x, y, z une par une, je les porte sur le côté. Si vous ne pouvez pas le porter sur le côté, implémentez-le soigneusement comme gate_cu3. Ce que nous faisons, c'est que ctx était ([liste de lignes], nombre de bits quantiques), alors ajoutez une nouvelle ligne à la liste de lignes avecctx [0] .append (...) Je fais juste ça.

Voir _postprocess_run

    def _postprocess_run(self, ctx):
        return "\n".join(ctx[0])

Il renvoie simplement une liste de lignes sous forme de chaîne délimitée par un saut de ligne. Ce résultat est le résultat de «run».

Résumé

La dernière fois, nous avons examiné la méthode d'implémentation backend pour les systèmes de traitement capables de lire OpenQASM, mais cette fois, nous avons regardé la méthode d'implémentation backend plus générale.

Chez Blueqat, nous visons à prendre en charge autant que possible du côté bibliothèque et à permettre aux développeurs et aux utilisateurs de faire ce qu'ils veulent, et il est également possible de créer facilement un backend en s'appuyant sur le mécanisme fourni par Blueqat. Vous pouvez, et vous pouvez même implémenter la méthode complète run sans vous embarquer.

N'importe qui peut créer le backend lui-même, et vous pouvez même utiliser votre propre simulateur avec l'interface Blueqat. Tout le monde, essayez d'implémenter le backend.

Recommended Posts

Créer un backend Blueqat ~ Partie 1
Créer un backend Blueqat ~ Partie 2
Faisons un plug-in backend pour Errbot
Faisons un jeu de squash
Faire un décorateur de fonction
Faire une matrice de distance
Je vais créer un mot de passe!
Créer un bouton Nyan
Créez un jeu à la Tetris!
Créer un décodeur Base64
Comment faire un jeu de tir avec toio (partie 1)
Faisons un robot Discord.
Créez un plug-in tky2jgd sans praticité avec QGIS Partie 2
Comment créer un laboratoire de piratage - Kali Linux (2020.1) VirtualBox 64 bits Partie 2-
Créer un plug-in tky2jgd sans praticité avec QGIS Partie 1
[Django] Créer un menu déroulant
Créer un LINE BOT (chat)
Créer un bookmarklet en Python
Faites une loterie avec Python
Faire de Responder un démon (service)
Faire un feu avec kdeplot
Faire une impression de forage de calcul
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 1
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 2
Créez un thermomètre avec Raspberry Pi et rendez-le visible sur le navigateur Partie 4
Comment faire un test unitaire Part.1 Modèle de conception pour l'introduction
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 3
Faisons une application WEB pour l'annuaire téléphonique avec flacon Partie 4
Faisons une rumba distante [Matériel]
Comment faire une traduction japonais-anglais
Créer un identifiant Santa à partir d'une image du Père Noël
Jouez avec une tortue avec des graphiques de tortue (partie 1)
Faisons une rumba distante [Logiciel]
Créer une boîte à Tweet pour Pepper
Faisons une interface graphique avec python.
Faites un son avec le notebook Jupyter
Faisons un service de vente au comptant 2
Créer un outil de reconnaissance de visage à l'aide de TensorFlow
Comment créer un bot slack
Faisons une rupture de bloc avec wxPython
Faisons un service de vente au comptant 1
Comment créer un robot - Avancé
Comment créer une fonction récursive
Rendre la compilation C un peu plus facile
python / Créer un dict à partir d'une liste.
[Python] Faire de la fonction une fonction lambda
Créer un système de recommandation avec python
[Blender] Comment créer un plug-in Blender
Créer un filtre avec un modèle django
[LINE bot] Je suis un ranger! Partie 2
Faisons un graphe avec python! !!
Faisons un spacon avec xCAT
Comment créer un robot - Basic
Créer un itérateur de modèle avec PySide
Faire un joli graphique avec plotly
Créez un générateur de rideaux avec Blender