Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Qiskitソースコードリーディング 〜 Terra: 回路作成からゲートや測定の追加までを読む

More than 1 year has passed since last update.

何をするの?

量子コンピューティングライブラリのBlueqatを作っている私が、量子コンピューティングライブラリのQiskitのソースコードを読んでみる、という企画です。
普段気になっていながらも読めていなかった部分を、せっかくなので読んでみました。

タイトルの通り、今回は回路を作成して、ゲートや測定を回路に付け加えるまでの処理を読んでいきます。
なので、今回は量子計算のやり方のようなものは一切出てこない、ごくごくフロントエンドの部分だけを読みます。

Qiskit概要

QiskitはIBMが開発しているオープンソースの量子コンピューティングライブラリです。

Qiskitは以下のようにパッケージが分かれていますが、インストールする際はバラバラにやるよりもpip install qiskitでまとめてインストールした方がトラブルが少ないです。

パッケージ 役割
Qiskit Terra メインとなるパッケージです。回路を作るクラスや、回路を実機向けにトランスパイルする機能、APIを叩いて実機に投げる機能などが含まれています
Qiskit Aer 量子回路のシミュレータが含まれており、通常はQiskit Terraから呼び出します
Qiskit Ignis 量子回路を実機で動かした際のノイズと戦いたい人のためのライブラリです。私は使ったことがありません
Qiskit Aqua 量子アルゴリズムを簡単に使えるようにしたライブラリです

今回読むのはQiskit Terraの一部です。

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

具体的には、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))

のうち、qc.measure([0,1], [0,1])までの流れを読んでいきます。

GitHub上のmasterブランチを読んでいきます。
現時点のコミットIDはe7be587ですが、結構頻繁に更新されているので、記事が書き終わる頃には変わる可能性もあります。ご了承ください。

QuantumCircuitクラスと動的なメソッド追加

qiskit/circuit/quantumcircuit.pyをざっと眺めましょう。

皆さん、あるはずのものがないことに、お気づきになられましたでしょうか?
先ほどのコードに注目ください。

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

QuantumCircuit.h, QuantumCircuit.cx, QuantumCircuit.measureがあるはずなのに、どこにも見つかりません。

これらの量子ゲートは数が多いので別で定義したい、という気持ちはすごくよく分かります。
では、一体どこで定義されているのでしょうか。

import時に、qiskit.extensions.*が読み込まれて、ゲートはそこで追加されています。
メジャーなゲートはqiskit/extensions/standardにあります。
また、測定はqiskit/circuit/measure.pyQuantumCircuitに追加されます。
(こういうのは、1箇所にまとめといて欲しいなぁ、と思いました)

QuantumCircuitを作る

さて、ゲートや測定のメソッドの謎を解いたところで、本題に戻りましょう。

qc = QuantumCircuit(2, 2)

を読んでみます。

qiskit/circuit/quantumcircuit.pyにあるQuantumCircuit.__init__を読みます。

    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

へー、回路に名前が必要なんですね。指定しなかったら勝手に付けてくれるみたいです。

あと、すげーくだらないんですが、プログラミングで日々消耗する者として気になったのが。

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

1行の文字数が多いとpylintとかに怒られるんですが、恐らくは、それを避けるために2行に分けています。
こういうの、いつも「それ怒られてもなぁ。本来は1行で表示されるものを2行に分けることで可読性上がるの? メッセージをgrepで検索とか想定してる?」って思っちゃうんですが。大人の対応ですね。

あとは、中味の初期化とかですね。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")

元々は、Qiskitでは、QuantumRegisterClassicalRegisterを作らなければならなかったんですが、Qiskitがバージョンアップしていくうちに、単なる数字だけでもよくなりました。Blueqatのパクリかな?
Qiskit-TerraのREADME.mdで一番最初に出るコードも、わざわざレジスタを作らずに数字でやる、という例が示されています。Blueqatのパクリかな?
ですが、内部構造としては、レジスタがあることを前提としているようで、指定しなければ'q'という名前の量子レジスタと'c'という名前の古典レジスタが作られるようです。

ちょっとした文句

その後付け部分についてですが。コメントを見たら分かるように、これ、とても微妙な感じになっています。
_add_registerのようなアンダースコア付きではなく、add_registerと、アンダースコアなしなので、これは内部だけではなく外部からも呼び出せることを想定した関数のはずです。

けれど、レジスタではなく数字を渡す部分のコメントや例外メッセージを見ると、外部からの呼び出しを想定してなさそうな雰囲気になっています。「整数だったらレジスタ'q'と'c'を作る」の部分については、__init__の中でやった方がよかったんじゃないかなぁ、と思いました。
……まぁ、今の実装で実際にすごく困るかというと、大して困らないので、いいんですが。

おまけ: 数字で指定したらq, cが作られる、ということを確認する

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'

うへへへへ。

Hゲートの実装

続いて、これらを見ていきます。

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

QuantumCircuit.hが実装されているqiskit/extensions/standard/h.pyを見ると

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


QuantumCircuit.h = h

ややこしいのですが、ここでのselfQuantumCircuitになります。
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

to_instruction()とは

初めから見ていきましょう。

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

ゲートや測定はInstructionなのでスルーされるのですが。
(具体的には、HGateクラスGateクラスを継承していて、GateクラスがInstructionクラスを継承しているので、HGateInstructionです。他のゲートや測定も、親クラスを追っていくとInstructionに辿り着きます)

そうじゃない場合、もしto_instructionメソッドを持っていれば、それが呼び出されるようです。
ある種の「ゲートを拡張したようなもの」を追加できるようにする、という考えに見えます。

grepでto_instructionメソッドを漁ってみたところ、ハードウェア制御用のパルスに関するもの、パウリ行列やクラウス表現などのゲートではないものを回路にするためのもの、が見つかりました。

ところで、「もしInstructionじゃなく、to_instructionメソッドも持ってなければ、ここで例外投げてほしいなー」と思ったのは、私だけでしょうか。(ここで投げなくても、後段で出るからいい、という話もありそうですが)

argument_conversionたち

次にいきましょう。

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

これらの中味を見ます。(docstringは削除して引用します)

    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)

どっちも_bit_argument_conversionを呼び出しているだけですが、その前にself.qubits, self.clbitsってなんだろ。見てみます。

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

レジスタの中味を全部、ひとつのリストに並べたものですね。
例えば[QuantumRegister(3, 'q1'), QuantumRegister(2, 'q2')]の2つのレジスタがあれば、[q1[0], q1[1], q1[2], q2[0], q2[1]]が返ってきます。

続いて_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

長いですが、何をやってるかはコメントに書いてあるとおりです。
いろんな呼び出し方をサポートしてるんだな、程度に捉えておけば大丈夫です。

InstructionSetを見る

QuantumCircuit.appendも終わりが見えてきました。

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

さて。qiskit/circuit/instructionset.pyの、InstructionSet.__init__を読んでいきましょう。

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 = []

大したことをしてない感じですね。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)

だいぶ想定通りですね。

Instruction.broadcast_argumentsを見る

残り少し!

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

broadcast_argumentsを読んでいきます。
これはqiskit/circuit/instruction.pyで実装されていますが、qiskit/circuit/gate.pyでオーバーライドされているため、今回呼び出されるのはGate.broadcast_argumentsになります。

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

やってること自体は、コメントにあるとおりです。
ゲートに指定する量子ビット数に応じて処理が変わっています。Hゲートの場合1個ですが、ついでなので全部見ていきましょう。

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

1個の場合は、単にひとつずつリストに詰めて出すだけですね。
3個の場合も、[[q[0], r[0]], [q[1], r[1]], [q[2], r[2]]][q[0], q[1], q[2]][r[0], r[1], r[2]]に分けるだけ、とシンプルです。
2個の場合、コメントにあるように、省略記法を許しているようです。

qc = QuantumCircuit(3, 3)
qc.cx([0, 1], 2)
print(qc.draw())
''' 結果(いらないところは略):
q_0: |0>──■───────
          │       
q_1: |0>──┼────■──
        ┌─┴─┐┌─┴─┐
q_2: |0>┤ X ├┤ X ├
        └───┘└───┘
'''

qc = QuantumCircuit(3, 3)
qc.cx(0, [1, 2])
print(qc.draw())
'''結果(いらないところは略):
q_0: |0>──■────■──
        ┌─┴─┐  │  
q_1: |0>┤ X ├──┼──
        └───┘┌─┴─┐
q_2: |0>─────┤ X ├
             └───┘
'''

このようにfor (qarg, carg) in instruction.broadcast_arguments(expanded_qargs, expanded_cargs):で、ゲートを適用する量子ビットを順次取り出していることが分かります。

QuantumCircuit._appendを見る

最初に言っとくと

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

の処理ですが、コードをこれから見ると分かるように

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

とやった方が行儀がいいですね。1行削りたくなる気持ちはプログラマなので分かりますが。
では、終わったと思ったら意外と長かった_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

まず_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")

これは、qargに重複がないか確認しています。
Hのような単一量子ビットゲートでは、重複は起こりえませんが、qc.cx(0, 0)のようなものを弾いてくれます。

続いて、_check_qargs_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")

Qubit, Clbitについては、レジスタのインデックスを取ってq[0]のようにすると返ってくるオブジェクトです。
ちゃんと回路に含まれている量子レジスタであることを確認しています。

以上でHゲートの追加は終わりです。

CXゲートの実装

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

の、cxについて見てみます。

cxメソッドはqiskit/extensions/standard/cx.pyで実装されていますが、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

呼び出されるとappendが呼ばれる流れもHゲートと同じですね。

測定の実装

measureについても見ていきましょう。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

はい、appendしてるだけですね。ですが、broadcast_argumentsGateクラスのものではなくMeasureクラスのものが使われることに注意してください。

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

qc.measure([0,1], [0,1])の場合だと、if文の上のやつが呼び出されます。
elifの部分はqc.measure(q, c)のように、レジスタ渡しした場合に相当します。

これで、本日の目標の、qc = QuantumCircuit(2, 2)からqc.measure([0,1], [0,1])を読むことができました。

まとめ

今回、量子回路の作成からゲート、測定の追加までを読みました。
量子コンピューティングライブラリでは、ゲート追加などをメソッドの形で実装するために、動的なメソッド追加を行うことが多いです。Qiskitではどのように追加されているのか見ていきました。
また、Qiskitの量子回路の実装には、量子レジスタが重要になってきます。そのあたりのハンドリングでコードが煩雑になっている印象を受けました。

Qiskitの実装はやはり気になるので、続きに関してもまた読んでいきたいと思います。

gyu-don
来世はパンダになりたい。
https://github.com/gyu-don/
mdrft
量子コンピュータのアプリケーション、ミドルウェア、ハードウェアをフルスタックで開発
https://blueqat.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away