[PYTHON] Lesen des Qiskit-Quellcodes ~ Terra: Lesen von der Schaltungserstellung bis zum Hinzufügen von Gates und Messungen

Was machst du?

Ich mache Blueqat, eine Quantencomputer-Bibliothek, und ich plane, den Quellcode von Qiskit, einer Quantencomputer-Bibliothek, zu lesen. Ich las den Teil, über den ich mir normalerweise Sorgen machte, den ich aber nicht lesen konnte.

Wie der Titel schon sagt, werden wir diesmal den Prozess des Erstellens einer Schaltung und des Hinzufügens von Gates und Messungen zur Schaltung lesen. Dieses Mal werde ich also nur den Front-End-Teil lesen, der nichts mit der Methode der Quantenberechnung zu tun hat.

Qiskit Übersicht

Qiskit ist eine von IBM entwickelte Open-Source-Quantencomputerbibliothek.

Qiskit ist wie unten gezeigt in Pakete unterteilt. Bei der Installation ist es jedoch weniger mühsam, sie zusammen mit "pip install qiskit" zu installieren, als sie separat auszuführen.

Paket Rolle
Qiskit Terra Dies ist das Hauptpaket. Es enthält eine Klasse zum Erstellen einer Schaltung, eine Funktion zum Transpilieren der Schaltung für die tatsächliche Maschine, eine Funktion zum Aufrufen der API und zum Werfen auf die tatsächliche Maschine usw.
Qiskit Aer Enthält einen Quantenschaltungssimulator, der normalerweise von Qiskit Terra aufgerufen wird
Qiskit Ignis Dies ist eine Bibliothek für diejenigen, die das Rauschen bekämpfen möchten, wenn sie eine Quantenschaltung auf einer tatsächlichen Maschine betreiben. Ich habe noch nie benutzt
Qiskit Aqua Eine Bibliothek, die die Verwendung von Quantenalgorithmen vereinfacht

Diesmal lese ich einen Teil von Qiskit Terra.

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

Insbesondere der in README.md geschriebene Code

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))

Lesen Sie von diesen den Fluss bis zu "qc.measure ([0,1], [0,1])".

Lesen Sie weiter den Hauptzweig auf GitHub. Die aktuelle Commit-ID lautet e7be587, wird jedoch häufig aktualisiert und kann sich bis zum Ende des Artikels ändern. Bitte beachten Sie.

QuantumCircuit-Klasse und dynamische Methoden hinzugefügt

Werfen Sie einen kurzen Blick auf qiskit / Circuit / Quantumcircuit.py.

Wussten Sie alle, dass es nichts gibt, was da sein sollte? Beachten Sie den obigen Code.

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

Ich sollte "QuantumCircuit.h", "QuantumCircuit.cx", "QuantumCircuit.measure" haben, aber ich kann es nirgendwo finden.

Da es so viele dieser Quantentore gibt, verstehe ich wirklich den Wunsch, sie separat zu definieren. Wo ist es definiert?

Bei import wirdqiskit.extensions. *Wird geladen und das Gate dort hinzugefügt. Die Haupttore befinden sich unter qiskit / extensions / standard. Die Messung wird auch dem "Quantenkreis" unter [qiskit / Circuit / Measure.py] hinzugefügt (https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/measure.py). (Ich wollte, dass du diese Dinge an einem Ort zusammenstellst)

Machen Sie eine Quantenschaltung

Nachdem wir das Rätsel der Tore und Messmethoden gelöst haben, kehren wir zum Hauptthema zurück.

qc = QuantumCircuit(2, 2)

Ich werde lesen.

Lesen Sie "QuantumCircuit .__ init__" unter 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, du brauchst einen Namen für die Schaltung. Wenn Sie es nicht angeben, wird es ohne Erlaubnis angehängt.

Es ist auch schrecklich, aber ich war besorgt als eine Person, die jeden Tag durch Programmieren erschöpft ist.

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

Wenn eine Zeile viele Zeichen enthält, ärgere ich mich über Pylint, aber ich teile es wahrscheinlich in zwei Zeilen, um dies zu vermeiden. Ich frage mich immer: "Es macht mir nichts aus, wenn ich wütend werde. Ist es möglich, die Lesbarkeit zu verbessern, indem das, was ursprünglich in einer Zeile angezeigt wurde, in zwei Zeilen unterteilt wird? Soll es nach Nachrichten mit grep suchen?" .. Es ist für Erwachsene.

Der Rest ist die Initialisierung des Inhalts. Lesen wir auch 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")

Ursprünglich musste Qiskit "Quantenregister" und "Klassisches Register" erstellen, aber als Qiskit aktualisiert wurde, wurde es besser, nur Zahlen zu verwenden. ~~ Ist es Blueqats Pakuri? ~~ Der erste Code in README.md von Qiskit-Terra zeigt auch ein Beispiel für die numerische Ausführung ohne Erstellung eines Registers. ~~ Ist es Blueqats Pakuri? ~~ Die interne Struktur scheint jedoch davon auszugehen, dass es ein Register gibt, und wenn nicht angegeben, werden ein Quantenregister mit dem Namen "q" und ein klassisches Register mit dem Namen "c" erstellt.

Eine kleine Beschwerde

Bezüglich des danach beigefügten Teils. Wie Sie in den Kommentaren sehen können, ist dies ein sehr subtiles Gefühl. Da es keinen Unterstrich mit "add_register" anstelle eines Unterstrichs wie "_add_register" gibt, sollte dies eine Funktion sein, die nicht nur intern, sondern auch extern aufgerufen werden soll.

Wenn man sich jedoch die Kommentare und Ausnahmemeldungen in dem Teil ansieht, der Nummern anstelle von Registern übergibt, scheint es unwahrscheinlich, dass sie von außen aufgerufen werden. Ich dachte, es wäre besser gewesen, den Teil von "Create registers'q'and'c 'in" init "zu machen, wenn es eine ganze Zahl ist. ...... Nun, das eigentliche Problem mit der aktuellen Implementierung ist, dass es mich nicht wirklich stört, also ist das in Ordnung.

Bonus: Stellen Sie sicher, dass q und c erstellt werden, wenn Sie sie mit Zahlen angeben

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.

Implementierung des H-Gatters

Als nächstes schauen wir uns diese an.

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

[Qiskit / extensions / standard / h.py] mit QuantumCircuit.h implementiert (https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/extensions/standard/h.py) Wenn Sie anschauen

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


QuantumCircuit.h = h

Es ist verwirrend, aber hier wird "Selbst" zu "Quantenschaltung". Werfen wir einen Blick auf "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

Was ist "to_instruction ()"?

Fangen wir von vorne an.

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

Tore und Messungen sind "Anweisungen", daher werden sie durchlaufen. (Insbesondere ist die HGate-Klasse die [ Gate-Klasse] Es erbt von (https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/gate.py#L24) und die Gate-Klasse ist die [ Instruction-Klasse](https: / Da es von /github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/instruction.py#L51) erbt, ist HGate`` Instruction. Andere Gates und Messungen sind ebenfalls übergeordnete Klassen. Wenn Sie folgen, erreichen Sie Instruction)

Wenn nicht, wird es anscheinend aufgerufen, wenn Sie eine Methode to_instruction haben. Es scheint, als ob die Idee darin besteht, Ihnen zu erlauben, eine Art "erweitertes Tor" hinzuzufügen.

Als ich die Methode "to_instruction" mit grep suchte, fand ich etwas im Zusammenhang mit Impulsen für die Hardwaresteuerung und etwas, um Nicht-Gates wie Pauli-Matrizen und Claus-Darstellungen in Schaltkreise umzuwandeln.

Übrigens bin ich der einzige, der dachte: "Wenn Sie keine" to_instruction "-Methode anstelle einer" Instruction "haben, möchte ich, dass Sie hier eine Ausnahme auslösen." (Es scheint eine Geschichte zu geben, die man hier nicht werfen muss, weil sie später herauskommt.)

argument_conversions

Lass uns als nächstes gehen.

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

Schauen Sie sich diese Inhalte an. (Dokumentzeichenfolge und Anführungszeichen löschen)


    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)

Beide rufen nur "_bit_argument_conversion" auf, aber was sind vorher "self.qubits" und "self.clbits"? Ich werde einen Blick darauf werfen.

    @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]

Der gesamte Inhalt des Registers ist in einer Liste zusammengefasst. Wenn Sie beispielsweise zwei Register haben: "[QuantumRegister (3, 'q1'), QuantumRegister (2, 'q2')]", dann "[q1 [0], q1 [1], q1 [2], q2 [0]. ], q2 [1]] `wird zurückgegeben.

Lesen Sie dann "_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

Es ist eine lange Zeit, aber was Sie tun, ist wie in den Kommentaren angegeben. Es unterstützt verschiedene Aufrufmethoden, daher ist es in Ordnung, wenn Sie es bis zu einem gewissen Grad verstehen.

Siehe Befehlssatz

Das Ende von "QuantumCircuit.append" geht zu Ende.

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

Jetzt. Lesen Sie InstructionSet .__ init__ in qiskit / Circuit / Anweisungssatz.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 = []

Es fühlt sich an, als würdest du nicht viel tun. Wenn Sie sich auch InstructionSet.add ansehen

    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)

Es ist so ziemlich wie erwartet.

Siehe Instruction.broadcast_arguments

Ein bisschen übrig!

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

Lesen Sie weiter Broadcast_arguments. Dies ist in qiskit / Circuit / Anweisung.py implementiert, aber in [qiskit / Circuit / Da es von gate.py] überschrieben wird (https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/gate.py), wird es dieses Mal "Gate.broadcast_arguments" genannt. Ich werde.

    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))

Was Sie tun, ist, wie Sie in den Kommentaren sehen können. Die Verarbeitung ändert sich entsprechend der Anzahl der im Gate angegebenen Quantenbits. Im Fall von H-Gate gibt es nur eines, aber schauen wir uns alle an.

    @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)

Im Falle eines Falles fügen Sie sie einfach einzeln in eine Liste ein. Im Fall von drei "[[q [0], r [0]], [q [1], r [1]], [q [2], r [2]]" Es ist so einfach wie 0], q [1], q [2]] und [r [0], r [1], r [2]]`. Im Fall von zwei scheint die Abkürzung wie im Kommentar angegeben zulässig zu sein.

qc = QuantumCircuit(3, 3)
qc.cx([0, 1], 2)
print(qc.draw())
'''Ergebnis(Weggelassen, wo Sie nicht brauchen):
q_0: |0>──■───────
          │       
q_1: |0>──┼────■──
        ┌─┴─┐┌─┴─┐
q_2: |0>┤ X ├┤ X ├
        └───┘└───┘
'''

qc = QuantumCircuit(3, 3)
qc.cx(0, [1, 2])
print(qc.draw())
'''Ergebnis(Weggelassen, wo Sie nicht brauchen):
q_0: |0>──■────■──
        ┌─┴─┐  │  
q_1: |0>┤ X ├──┼──
        └───┘┌─┴─┐
q_2: |0>─────┤ X ├
             └───┘
'''

Wie Sie sehen können, "für (qarg, carg) in Anweisung.broadcast_arguments (expandierte_qargs, erweiterte_cargs):" extrahiert nacheinander die Quantenbits, auf die das Gate angewendet wird.

Siehe QuantumCircuit._append

Als allererstes

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

Wie Sie dem Code entnehmen können

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

Es ist besser, es zu tun. Ich verstehe, dass ich eine Zeile schneiden möchte, weil ich Programmierer bin. Wenn Sie dann denken, dass es vorbei ist, werfen wir einen Blick auf das unerwartet lange "_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

Zuerst _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")

Dies prüft auf Duplikate in qarg. Ein einzelnes Quantenbitgate wie H kann nicht duplizieren, spielt jedoch so etwas wie "qc.cx (0, 0)" ab.

Dann _check_qargs und _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")

Für Qubit und Clbit ist es ein Objekt, das zurückgegeben wird, wenn Sie den Index des Registers nehmen und so etwas wieq [0]ausführen. Ich habe bestätigt, dass es sich um ein Quantenregister handelt, das ordnungsgemäß in der Schaltung enthalten ist.

Dies ist das Ende des Hinzufügens des H-Gatters.

Implementierung des CX-Gatters

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

Werfen wir einen Blick auf cx.

Die Methode "cx" ist in [qiskit / extensions / standard / cx.py] implementiert (https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/extensions/standard/cx.py). Es ist jedoch fast dasselbe wie das H-Gate.

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

Der Ablauf des Aufrufs von "Anhängen" beim Aufrufen ist der gleiche wie beim H-Gatter.

Durchführung der Messung

Schauen wir uns auch "Maßnahme" an. Lesen Sie 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

Ja, es ist nur "Anhängen". Beachten Sie jedoch, dass die "Broadcast_Arguments" der Klasse "Measure" anstelle der Klasse "Gate" angehören.

    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')

Im Fall von "qc.measure ([0,1], [0,1])" wird die über der if-Anweisung stehende aufgerufen. Der elif-Teil entspricht dem Fall, in dem ein Register übergeben wird, wie z. B. "qc.measure (q, c)".

Sie können jetzt das heutige Ziel "qc = QuantumCircuit (2, 2)" bis "qc.measure ([0,1], [0,1])" lesen.

Zusammenfassung

Dieses Mal las ich von der Erstellung einer Quantenschaltung bis zum Hinzufügen von Gates und Messungen. In Quantencomputerbibliotheken wird häufig eine dynamische Methodenaddition durchgeführt, um eine Gateaddition usw. in Form von Methoden zu implementieren. Wir haben uns angesehen, wie es in Qiskit hinzugefügt wurde. Darüber hinaus sind Quantenregister wichtig für die Implementierung der Qiskit-Quantenschaltungen. Ich hatte den Eindruck, dass der Code aufgrund der Handhabung kompliziert war.

Ich bin immer noch neugierig auf die Implementierung von Qiskit, daher würde ich gerne mehr über die Fortsetzung lesen.

Recommended Posts

Lesen des Qiskit-Quellcodes ~ Terra: Lesen von der Schaltungserstellung bis zum Hinzufügen von Gates und Messungen
Lesen des Qiskit-Quellcodes ~ Terra: Lesen von der Schaltungserstellung bis zum Hinzufügen von Gates und Messungen
[Python] Lesen Sie den Flask-Quellcode
Fluss vom Quellcode zum Erstellen des Ausführungsformats
Unterschiede beim Schreiben von externem Quellcode zwischen Ruby und Python
[Python] Lesen von Daten aus CIFAR-10 und CIFAR-100
[Python] Lesen des Django-Quellcodes Ansicht ab Null ①