[PYTHON] Lecture du code source Qiskit ~ Terra: lecture de la création du circuit à l'ajout de portes et de mesures

Que faites-vous?

Je crée Blueqat, une bibliothèque de calcul quantique, et je prévois de lire le code source de Qiskit, une bibliothèque de calcul quantique. J'ai lu la partie qui m'inquiétait habituellement mais que je ne savais pas lire.

Comme le titre l'indique, cette fois, nous lirons le processus de création d'un circuit et d'ajouter des portes et des mesures au circuit. Donc, cette fois, je ne lirai que la partie très frontale, qui ne montre rien de tel que la méthode de calcul quantique.

Présentation de Qiskit

Qiskit est une bibliothèque informatique quantique open source développée par IBM.

Qiskit est divisé en paquets comme indiqué ci-dessous, mais lors de l'installation, il est moins gênant de les installer avec pip install qiskit que de les faire séparément.

paquet rôle
Qiskit Terra Ceci est le package principal. Il comprend une classe pour créer un circuit, une fonction pour transpiler le circuit pour la machine réelle, une fonction pour atteindre l'API et la lancer sur la machine réelle, etc.
Qiskit Aer Comprend un simulateur de circuit quantique, généralement appelé depuis Qiskit Terra
Qiskit Ignis Ceci est une bibliothèque pour ceux qui veulent lutter contre le bruit lors de l'exécution d'un circuit quantique sur une machine réelle. Je n'ai jamais utilisé
Qiskit Aqua Une bibliothèque qui facilite l'utilisation des algorithmes quantiques

Cette fois, je lis une partie de Qiskit Terra.

https://github.com/Qiskit/qiskit-terra

Plus précisément, le code écrit en README.md

from qiskit import *
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0,1], [0,1])
backend_sim = BasicAer.get_backend('qasm_simulator')
result = execute(qc, backend_sim).result()
print(result.get_counts(qc))

Parmi ceux-ci, lisez le flux jusqu'à qc.measure ([0,1], [0,1]).

Continuez à lire la branche principale sur GitHub. L'ID de validation pour le moment est e7be587, mais il est mis à jour assez souvent et peut changer à la fin de l'article. Notez s'il vous plaît.

Ajout de la classe QuantumCircuit et des méthodes dynamiques

Jetez un coup d'œil à qiskit / circuit / quantumcircuit.py.

Saviez-vous tous qu'il n'y a rien qui devrait être là? Notez le code ci-dessus.

qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0,1], [0,1])

Je devrais avoir QuantumCircuit.h, QuantumCircuit.cx, QuantumCircuit.measure, mais je ne le trouve nulle part.

Puisqu'il y a tellement de ces portes quantiques, je comprends vraiment le désir de les définir séparément. Alors, où est-il défini?

À ʻimport, qiskit.extensions. * ʻEst chargé et la porte y est ajoutée. Les principales portes sont situées dans qiskit / extensions / standard. La mesure sera également ajoutée au Circuit Quantum sur qiskit / circuit / measure.py. (Je voulais que vous mettiez ces choses ensemble en un seul endroit)

Créer un circuit quantique

Maintenant que nous avons résolu le mystère des portes et des méthodes de mesure, revenons au sujet principal.

qc = QuantumCircuit(2, 2)

Je lirai.

Lisez QuantumCircuit .__ init__ sur qiskit / circuit / quantumcircuit.py.

    def __init__(self, *regs, name=None):
        if name is None:
            name = self.cls_prefix() + str(self.cls_instances())
            # pylint: disable=not-callable
            # (known pylint bug: https://github.com/PyCQA/pylint/issues/1699)
            if sys.platform != "win32" and isinstance(mp.current_process(), mp.context.ForkProcess):
                name += '-{}'.format(mp.current_process().pid)
        self._increment_instances()

        if not isinstance(name, str):
            raise CircuitError("The circuit name should be a string "
                               "(or None to auto-generate a name).")

        self.name = name

        # Data contains a list of instructions and their contexts,
        # in the order they were applied.
        self._data = []

        # This is a map of registers bound to this circuit, by name.
        self.qregs = []
        self.cregs = []
        self.add_register(*regs)

        # Parameter table tracks instructions with variable parameters.
        self._parameter_table = ParameterTable()

        self._layout = None

Hmm, vous avez besoin d'un nom pour le circuit. Si vous ne le spécifiez pas, il sera joint sans autorisation.

Aussi, c'est terrible, mais j'étais inquiet en tant que personne épuisée chaque jour par la programmation.

            raise CircuitError("The circuit name should be a string "
                               "(or None to auto-generate a name).")

S'il y a beaucoup de caractères dans une ligne, je me fâche contre pylint, mais je le divise probablement en deux lignes pour l'éviter. Je pense toujours: "Cela ne me dérange pas si je me fâche. Est-il possible d'améliorer la lisibilité en divisant ce qui est initialement affiché sur une ligne en deux lignes? Est-il censé rechercher des messages avec grep?" .. C'est pour les adultes.

Le reste est l'initialisation du contenu. Lisons également ʻadd_register`.

    def add_register(self, *regs):
        """Add registers."""
        if not regs:
            return

        if any([isinstance(reg, int) for reg in regs]):
            # QuantumCircuit defined without registers
            if len(regs) == 1 and isinstance(regs[0], int):
                # QuantumCircuit with anonymous quantum wires e.g. QuantumCircuit(2)
                regs = (QuantumRegister(regs[0], 'q'),)
            elif len(regs) == 2 and all([isinstance(reg, int) for reg in regs]):
                # QuantumCircuit with anonymous wires e.g. QuantumCircuit(2, 3)
                regs = (QuantumRegister(regs[0], 'q'), ClassicalRegister(regs[1], 'c'))
            else:
                raise CircuitError("QuantumCircuit parameters can be Registers or Integers."
                                   " If Integers, up to 2 arguments. QuantumCircuit was called"
                                   " with %s." % (regs,))

        for register in regs:
            if register.name in [reg.name for reg in self.qregs + self.cregs]:
                raise CircuitError("register name \"%s\" already exists"
                                   % register.name)
            if isinstance(register, QuantumRegister):
                self.qregs.append(register)
            elif isinstance(register, ClassicalRegister):
                self.cregs.append(register)
            else:
                raise CircuitError("expected a register")

À l'origine, Qiskit devait créer un «registre quantique» et un «registre classique», mais avec la mise à niveau de Qiskit, il est devenu préférable d'utiliser uniquement des nombres. ~~ Est-ce le pakuri de Blueqat? ~~ Un exemple est montré dans lequel le code qui apparaît en premier dans README.md de Qiskit-Terra est également fait numériquement sans créer de registre. ~~ Est-ce le pakuri de Blueqat? ~~ Cependant, la structure interne semble supposer qu'il existe un registre, et s'il n'est pas spécifié, un registre quantique nommé «q» et un registre classique nommé «c» seront créés.

Un petit reproche

En ce qui concerne la pièce jointe après cela. Comme vous pouvez le voir dans les commentaires, c'est une sensation très subtile. Pas avec un trait de soulignement comme _add_register, mais avec ʻadd_register` et pas de trait de soulignement, cela devrait être une fonction qui est censée être appelée non seulement en interne mais aussi en externe.

Cependant, en regardant les commentaires et les messages d'exception dans la partie qui transmet les nombres au lieu des registres, il semble qu'il est peu probable qu'il soit appelé de l'extérieur. J'ai pensé qu'il aurait été préférable de faire la partie "Créer des registres'q'et'c 's'il s'agit d'un entier" dans __init__. ...... Eh bien, le problème réel avec l'implémentation actuelle est que cela ne me dérange pas vraiment, donc c'est très bien.

Bonus: assurez-vous que q et c sont créés si vous les spécifiez avec des nombres

from qiskit import *
q = QuantumRegister(3, 'q')
c = QuantumRegister(3, 'c')
qc = QuantumCircuit(4, 4)
qc.add_register(q)
# => QiskitError: 'register name "q" already exists'
qc.add_register(c)
# => QiskitError: 'register name "c" already exists'

qc = QuantumCircuit(q)
qc.add_register(4)
# => QiskitError: 'register name "q" already exists'

Uhehehehe.

Implémentation de la porte H

Ensuite, regardons-les.

qc.h(0)
qc.cx(0, 1)
qc.measure([0,1], [0,1])

[Qiskit / extensions / standard / h.py] avec QuantumCircuit.h implémenté (https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/extensions/standard/h.py) Quand tu regardes

def h(self, q):  # pylint: disable=invalid-name
    """Apply H to q."""
    return self.append(HGate(), [q], [])


QuantumCircuit.h = h

C'est déroutant, mais ici «soi» devient «circuit quantique». Jetons un coup d'œil à QuantumCircuit.append.

QuantumCircuit.append

    def append(self, instruction, qargs=None, cargs=None):
        """Append one or more instructions to the end of the circuit, modifying
        the circuit in place. Expands qargs and cargs.
        Args:
            instruction (Instruction or Operation): Instruction instance to append
            qargs (list(argument)): qubits to attach instruction to
            cargs (list(argument)): clbits to attach instruction to
        Returns:
            Instruction: a handle to the instruction that was just added
        """
        # Convert input to instruction
        if not isinstance(instruction, Instruction) and hasattr(instruction, 'to_instruction'):
            instruction = instruction.to_instruction()

        expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []]
        expanded_cargs = [self.cbit_argument_conversion(carg) for carg in cargs or []]

        instructions = InstructionSet()
        for (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):
            instructions.add(self._append(instruction, qarg, carg), qarg, carg)
        return instructions

Qu'est-ce que to_instruction ()?

Commençons depuis le début.

        if not isinstance(instruction, Instruction) and hasattr(instruction, 'to_instruction'):
            instruction = instruction.to_instruction()

Les portes et les mesures sont des «instructions», donc elles sont passées. (Plus précisément, classe HGate est [classe Gate] Il hérite de (https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/gate.py#L24) et la classe Gate est la [classe ʻInstruction](https: / Puisqu'il hérite de /github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/instruction.py#L51), HGate est ʻInstruction. Les autres portes et mesures sont également des classes parentes. Si vous suivez, vous atteindrez ʻInstruction`)

Sinon, il semble que si vous avez une méthode to_instruction, elle sera appelée. Il semble que l'idée soit de vous permettre d'ajouter une sorte de "porte étendue".

Quand j'ai cherché la méthode to_instruction avec grep, j'ai trouvé quelque chose lié aux impulsions pour le contrôle matériel, et quelque chose pour faire des non-portes telles que les matrices de Pauli et les représentations Claus en circuits.

Au fait, suis-je le seul à avoir pensé: "Si vous n'avez pas de méthode to_instruction au lieu de ʻInstruction`, je veux que vous lanciez une exception ici." (Il semble y avoir une histoire que vous n'avez pas à la lancer ici car elle sortira plus tard)

argument_conversions

Allons-y ensuite.

        expanded_qargs = [self.qbit_argument_conversion(qarg) for qarg in qargs or []]
        expanded_cargs = [self.cbit_argument_conversion(carg) for carg in cargs or []]

Regardez ces contenus. (Supprimer docstring et quote)


    def qbit_argument_conversion(self, qubit_representation):
        return QuantumCircuit._bit_argument_conversion(qubit_representation, self.qubits)

    def cbit_argument_conversion(self, clbit_representation):
        return QuantumCircuit._bit_argument_conversion(clbit_representation, self.clbits)

Les deux appellent simplement «_bit_argument_conversion», mais avant cela, que sont «self.qubits» et «self.clbits»? Je regarderai.

    @property
    def qubits(self):
        """
        Returns a list of quantum bits in the order that the registers were added.
        """
        return [qbit for qreg in self.qregs for qbit in qreg]

    @property
    def clbits(self):
        """
        Returns a list of classical bits in the order that the registers were added.
        """
        return [cbit for creg in self.cregs for cbit in creg]

Tout le contenu du registre est organisé dans une seule liste. Par exemple, si vous avez deux registres, [QuantumRegister (3, 'q1'), QuantumRegister (2, 'q2')], alors` [q1 [0], q1 [1], q1 [2], q2 [0] ], q2 [1]] ʻest retourné.

Ensuite, lisez _bit_argument_conversion.

    @staticmethod
    def _bit_argument_conversion(bit_representation, in_array):
        ret = None
        try:
            if isinstance(bit_representation, Bit):
                # circuit.h(qr[0]) -> circuit.h([qr[0]])
                ret = [bit_representation]
            elif isinstance(bit_representation, Register):
                # circuit.h(qr) -> circuit.h([qr[0], qr[1]])
                ret = bit_representation[:]
            elif isinstance(QuantumCircuit.cast(bit_representation, int), int):
                # circuit.h(0) -> circuit.h([qr[0]])
                ret = [in_array[bit_representation]]
            elif isinstance(bit_representation, slice):
                # circuit.h(slice(0,2)) -> circuit.h([qr[0], qr[1]])
                ret = in_array[bit_representation]
            elif _is_bit(bit_representation):
                # circuit.h((qr, 0)) -> circuit.h([qr[0]])
                ret = [bit_representation[0][bit_representation[1]]]
            elif isinstance(bit_representation, list) and \
                    all(_is_bit(bit) for bit in bit_representation):
                ret = [bit[0][bit[1]] for bit in bit_representation]
            elif isinstance(bit_representation, list) and \
                    all(isinstance(bit, Bit) for bit in bit_representation):
                # circuit.h([qr[0], qr[1]]) -> circuit.h([qr[0], qr[1]])
                ret = bit_representation
            elif isinstance(QuantumCircuit.cast(bit_representation, list), (range, list)):
                # circuit.h([0, 1])     -> circuit.h([qr[0], qr[1]])
                # circuit.h(range(0,2)) -> circuit.h([qr[0], qr[1]])
                # circuit.h([qr[0],1])  -> circuit.h([qr[0], qr[1]])
                ret = [index if isinstance(index, Bit) else in_array[
                    index] for index in bit_representation]
            else:
                raise CircuitError('Not able to expand a %s (%s)' % (bit_representation,
                                                                     type(bit_representation)))
        except IndexError:
            raise CircuitError('Index out of range.')
        except TypeError:
            raise CircuitError('Type error handling %s (%s)' % (bit_representation,
                                                                type(bit_representation)))
        return ret

C'est long, mais ce que vous faites est comme indiqué dans les commentaires. Il prend en charge diverses méthodes d'appel, donc ce n'est pas grave si vous le comprenez dans une certaine mesure.

Voir ʻEnsemble d'instructions`

La fin de QuantumCircuit.append touche à sa fin.

        instructions = InstructionSet()
        for (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):
            instructions.add(self._append(instruction, qarg, carg), qarg, carg)
        return instructions

Maintenant. Lisez ʻInstructionSet .__ init__` dans qiskit / circuit / instructionset.py.

class InstructionSet:
    """Instruction collection, and their contexts."""

    def __init__(self):
        """New collection of instructions.
        The context (qargs and cargs that each instruction is attached to),
        is also stored separately for each instruction.
        """
        self.instructions = []
        self.qargs = []
        self.cargs = []

J'ai l'impression que vous ne faites pas grand-chose. Si vous regardez également ʻInstructionSet.add`

    def add(self, gate, qargs, cargs):
        """Add an instruction and its context (where it's attached)."""
        if not isinstance(gate, Instruction):
            raise CircuitError("attempt to add non-Instruction" +
                               " to InstructionSet")
        self.instructions.append(gate)
        self.qargs.append(qargs)
        self.cargs.append(cargs)

C'est à peu près comme prévu.

Voir ʻInstruction.broadcast_arguments`

Un peu à gauche!

        for (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):
            instructions.add(self._append(instruction, qarg, carg), qarg, carg)
        return instructions

Continuez à lire broadcast_arguments. Ceci est implémenté dans qiskit / circuit / instruction.py, mais qiskit / circuit / Puisqu'il est remplacé par gate.py, il s'appellera cette fois Gate.broadcast_arguments. Je vais.

    def broadcast_arguments(self, qargs, cargs):
        """Validation and handling of the arguments and its relationship.
        For example:
        `cx([q[0],q[1]], q[2])` means `cx(q[0], q[2]); cx(q[1], q[2])`. This method
        yields the arguments in the right grouping. In the given example::
            in: [[q[0],q[1]], q[2]],[]
            outs: [q[0], q[2]], []
                  [q[1], q[2]], []
        The general broadcasting rules are:
         * If len(qargs) == 1::
                [q[0], q[1]] -> [q[0]],[q[1]]
         * If len(qargs) == 2::
                [[q[0], q[1]], [r[0], r[1]]] -> [q[0], r[0]], [q[1], r[1]]
                [[q[0]], [r[0], r[1]]]       -> [q[0], r[0]], [q[0], r[1]]
                [[q[0], q[1]], [r[0]]]       -> [q[0], r[0]], [q[1], r[0]]
         * If len(qargs) >= 3::
                [q[0], q[1]], [r[0], r[1]],  ...] -> [q[0], r[0], ...], [q[1], r[1], ...]
        Args:
            qargs (List): List of quantum bit arguments.
            cargs (List): List of classical bit arguments.
        Returns:
            Tuple(List, List): A tuple with single arguments.
        Raises:
            CircuitError: If the input is not valid. For example, the number of
                arguments does not match the gate expectation.
        """
        if len(qargs) != self.num_qubits or cargs:
            raise CircuitError(
                'The amount of qubit/clbit arguments does not match the gate expectation.')

        if any([not qarg for qarg in qargs]):
            raise CircuitError('One or more of the arguments are empty')

        if len(qargs) == 1:
            return Gate._broadcast_single_argument(qargs[0])
        elif len(qargs) == 2:
            return Gate._broadcast_2_arguments(qargs[0], qargs[1])
        elif len(qargs) >= 3:
            return Gate._broadcast_3_or_more_args(qargs)
        else:
            raise CircuitError('This gate cannot handle %i arguments' % len(qargs))

Ce que vous faites est comme vous pouvez le voir dans les commentaires. Le traitement change en fonction du nombre de bits quantiques spécifié dans la porte. Dans le cas de la porte H, il n'y en a qu'une, mais regardons-les toutes.

    @staticmethod
    def _broadcast_single_argument(qarg):
        """Expands a single argument.
        For example: [q[0], q[1]] -> [q[0]], [q[1]]
        """
        # [q[0], q[1]] -> [q[0]]
        #              -> [q[1]]
        for arg0 in qarg:
            yield [arg0], []

    @staticmethod
    def _broadcast_2_arguments(qarg0, qarg1):
        if len(qarg0) == len(qarg1):
            # [[q[0], q[1]], [r[0], r[1]]] -> [q[0], r[0]]
            #                              -> [q[1], r[1]]
            for arg0, arg1 in zip(qarg0, qarg1):
                yield [arg0, arg1], []
        elif len(qarg0) == 1:
            # [[q[0]], [r[0], r[1]]] -> [q[0], r[0]]
            #                        -> [q[0], r[1]]
            for arg1 in qarg1:
                yield [qarg0[0], arg1], []
        elif len(qarg1) == 1:
            # [[q[0], q[1]], [r[0]]] -> [q[0], r[0]]
            #                        -> [q[1], r[0]]
            for arg0 in qarg0:
                yield [arg0, qarg1[0]], []
        else:
            raise CircuitError('Not sure how to combine these two qubit arguments:\n %s\n %s' %
                               (qarg0, qarg1))

    @staticmethod
    def _broadcast_3_or_more_args(qargs):
        if all(len(qarg) == len(qargs[0]) for qarg in qargs):
            for arg in zip(*qargs):
                yield list(arg), []
        else:
            raise CircuitError(
                'Not sure how to combine these qubit arguments:\n %s\n' % qargs)

Dans le cas d'un, vous les mettez simplement dans une liste un par un. Dans le cas de trois, «[[q [0], r [0]], [q [1], r [1]], [q [2], r [2]]]» est «[q [ C'est aussi simple que 0], q [1], q [2]] ʻet[r [0], r [1], r [2]]. Dans le cas de deux, il semble que l'abréviation soit autorisée comme indiqué dans le commentaire.

qc = QuantumCircuit(3, 3)
qc.cx([0, 1], 2)
print(qc.draw())
'''résultat(Omis là où vous n'avez pas besoin):
q_0: |0>──■───────
          │       
q_1: |0>──┼────■──
        ┌─┴─┐┌─┴─┐
q_2: |0>┤ X ├┤ X ├
        └───┘└───┘
'''

qc = QuantumCircuit(3, 3)
qc.cx(0, [1, 2])
print(qc.draw())
'''résultat(Omis là où vous n'avez pas besoin):
q_0: |0>──■────■──
        ┌─┴─┐  │  
q_1: |0>┤ X ├──┼──
        └───┘┌─┴─┐
q_2: |0>─────┤ X ├
             └───┘
'''

Comme vous pouvez le voir, for (qarg, carg) in instruction.broadcast_arguments (extended_qargs, extended_cargs): extrait séquentiellement les bits quantiques auxquels la porte est appliquée.

Voir QuantumCircuit._append

Tout d'abord

        for (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):
            instructions.add(self._append(instruction, qarg, carg), qarg, carg)

Comme vous pouvez le voir dans le code

        for (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):
            self._append(instruction, qarg, carg)
            instructions.add(instruction, qarg, carg)

Il vaut mieux le faire. Je comprends que je veux couper une ligne parce que je suis programmeur. Ensuite, quand vous pensez que c'est fini, jetons un coup d'œil à la longueur inattendue _append.

    def _append(self, instruction, qargs, cargs):
        """Append an instruction to the end of the circuit, modifying
        the circuit in place.
        Args:
            instruction (Instruction or Operator): Instruction instance to append
            qargs (list(tuple)): qubits to attach instruction to
            cargs (list(tuple)): clbits to attach instruction to
        Returns:
            Instruction: a handle to the instruction that was just added
        Raises:
            CircuitError: if the gate is of a different shape than the wires
                it is being attached to.
        """
        if not isinstance(instruction, Instruction):
            raise CircuitError('object is not an Instruction.')

        # do some compatibility checks
        self._check_dups(qargs)
        self._check_qargs(qargs)
        self._check_cargs(cargs)

        # add the instruction onto the given wires
        instruction_context = instruction, qargs, cargs
        self._data.append(instruction_context)

        self._update_parameter_table(instruction)

        return instruction

Premier _check_dups

    def _check_dups(self, qubits):
        """Raise exception if list of qubits contains duplicates."""
        squbits = set(qubits)
        if len(squbits) != len(qubits):
            raise CircuitError("duplicate qubit arguments")

Ceci vérifie les doublons dans qarg. Un seul bitgate quantique comme H ne peut pas se dupliquer, mais il joue quelque chose comme qc.cx (0, 0).

Puis _check_qargs et _check_cargs

    def _check_qargs(self, qargs):
        """Raise exception if a qarg is not in this circuit or bad format."""
        if not all(isinstance(i, Qubit) for i in qargs):
            raise CircuitError("qarg is not a Qubit")
        if not all(self.has_register(i.register) for i in qargs):
            raise CircuitError("register not in this circuit")

    def _check_cargs(self, cargs):
        """Raise exception if clbit is not in this circuit or bad format."""
        if not all(isinstance(i, Clbit) for i in cargs):
            raise CircuitError("carg is not a Clbit")
        if not all(self.has_register(i.register) for i in cargs):
            raise CircuitError("register not in this circuit")

Pour Qubit et Clbit, c'est un objet qui est retourné lorsque vous prenez l'index du registre et faites quelque chose commeq [0]. J'ai confirmé qu'il s'agit d'un registre quantique correctement inclus dans le circuit.

C'est la fin de l'ajout de la porte H.

Implémentation de CX Gate

qc.h(0)
qc.cx(0, 1)
qc.measure([0,1], [0,1])

Jetons un coup d'œil à cx.

La méthode cx est implémentée dans qiskit / extensions / standard / cx.py Cependant, c'est presque la même chose que la porte H.

def cx(self, ctl, tgt):  # pylint: disable=invalid-name
    """Apply CX from ctl to tgt."""
    return self.append(CnotGate(), [ctl, tgt], [])


QuantumCircuit.cx = cx
QuantumCircuit.cnot = cx

Le déroulement de l'appel ʻappend` lorsqu'il est appelé est le même que celui de la porte H.

Mise en œuvre de la mesure

Regardons également mesure. Lisez qiskit / circuit / measure.py.

def measure(self, qubit, cbit):
    """Measure quantum bit into classical bit (tuples).
    Args:
        qubit (QuantumRegister|list|tuple): quantum register
        cbit (ClassicalRegister|list|tuple): classical register
    Returns:
        qiskit.Instruction: the attached measure instruction.
    Raises:
        CircuitError: if qubit is not in this circuit or bad format;
            if cbit is not in this circuit or not creg.
    """
    return self.append(Measure(), [qubit], [cbit])


QuantumCircuit.measure = measure

Oui, c'est juste «ajouter». Cependant, notez que broadcast_arguments est de la classe Measure au lieu de la classe Gate.

    def broadcast_arguments(self, qargs, cargs):
        qarg = qargs[0]
        carg = cargs[0]

        if len(carg) == len(qarg):
            for qarg, carg in zip(qarg, carg):
                yield [qarg], [carg]
        elif len(qarg) == 1 and carg:
            for each_carg in carg:
                yield qarg, [each_carg]
        else:
            raise CircuitError('register size error')

Dans le cas de qc.measure ([0,1], [0,1]), celle au-dessus de l'instruction if est appelée. La partie elif correspond au cas où un registre est passé, tel que qc.measure (q, c).

Vous pouvez maintenant lire l'objectif du jour, qc = QuantumCircuit (2, 2) à qc.measure ([0,1], [0,1]).

Résumé

Cette fois, j'ai lu de la création d'un circuit quantique à l'ajout de portes et de mesures. Dans les bibliothèques de calcul quantique, l'addition de méthode dynamique est souvent effectuée afin d'implémenter l'addition de porte, etc. sous la forme de méthodes. Nous avons regardé comment il a été ajouté dans Qiskit. De plus, les registres quantiques sont importants pour implémenter les circuits quantiques de Qiskit. J'ai eu l'impression que le code était compliqué à cause de la manipulation autour de cela.

Je suis toujours curieux de connaître l'implémentation de Qiskit, alors j'aimerais en savoir plus sur la suite.

Recommended Posts

Lecture du code source Qiskit ~ Terra: lecture de la création du circuit à l'ajout de portes et de mesures
Lecture du code source Qiskit ~ Terra: lecture de la création du circuit à l'ajout de portes et de mesures
[Python] Lire le code source de Flask
Flux du code source à la création du format d'exécution
Différences dans la façon d'écrire du code source externe entre Ruby et Python
[Python] Comment lire les données de CIFAR-10 et CIFAR-100
[Python] Lecture du code source Django Vue à partir de zéro ①