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

# 何をするの?
量子コンピューティングライブラリの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に書いてあるコード

```py
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](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/quantumcircuit.py)をざっと眺めましょう。

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

```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](https://github.com/Qiskit/qiskit-terra/tree/master/qiskit/extensions/standard)にあります。
また、測定は[qiskit/circuit/measure.py](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/measure.py)で`QuantumCircuit`に追加されます。
(こういうのは、1箇所にまとめといて欲しいなぁ、と思いました)

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

```py
qc = QuantumCircuit(2, 2)
```

を読んでみます。

[qiskit/circuit/quantumcircuit.py](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/quantumcircuit.py)にある`QuantumCircuit.__init__`を読みます。

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

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

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

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

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

あとは、中味の初期化とかですね。`add_register`も読んでみましょう。

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

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

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

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

```py
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ゲートの実装

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

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

`QuantumCircuit.h`が実装されている[qiskit/extensions/standard/h.py](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/extensions/standard/h.py)を見ると

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


QuantumCircuit.h = h
```

ややこしいのですが、ここでの`self`は`QuantumCircuit`になります。
`QuantumCircuit.append`を見ていきましょう。

# `QuantumCircuit.append`
```py
    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()`とは
初めから見ていきましょう。

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

ゲートや測定は`Instruction`なのでスルーされるのですが。
(具体的には、[`HGate`クラス](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/extensions/standard/h.py#L27)が[`Gate`クラス](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/gate.py#L24)を継承していて、`Gate`クラスが[`Instruction`クラス](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/instruction.py#L51)を継承しているので、`HGate`は`Instruction`です。他のゲートや測定も、親クラスを追っていくと`Instruction`に辿り着きます)

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

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

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

## argument_conversionたち
次にいきましょう。

```py
        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は削除して引用します)

```py

    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`ってなんだろ。見てみます。

```py
    @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`を読みます。

```py
    @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`も終わりが見えてきました。

```py
        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](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/instructionset.py)の、`InstructionSet.__init__`を読んでいきましょう。

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

大したことをしてない感じですね。`InstructionSet.add`もついでに見ると

```py
    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`を見る
残り少し！

```py
        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](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/instruction.py)で実装されていますが、[qiskit/circuit/gate.py](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/gate.py)でオーバーライドされているため、今回呼び出されるのは`Gate.broadcast_arguments`になります。

```py
    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個ですが、ついでなので全部見ていきましょう。

```py
    @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個の場合、コメントにあるように、省略記法を許しているようです。

```py
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`を見る

最初に言っとくと

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

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

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

とやった方が行儀がいいですね。1行削りたくなる気持ちはプログラマなので分かりますが。
では、終わったと思ったら意外と長かった`_append`を見ていきます。

```py
    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`

```py
    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`

```py
    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ゲートの実装

```py
qc.h(0)
qc.cx(0, 1)
qc.measure([0,1], [0,1])
```
の、cxについて見てみます。

`cx`メソッドは[qiskit/extensions/standard/cx.py](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/extensions/standard/cx.py)で実装されていますが、Hゲートとほぼ同じです。

```py
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](https://github.com/Qiskit/qiskit-terra/blob/master/qiskit/circuit/measure.py)を読みます。

```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_arguments`に`Gate`クラスのものではなく`Measure`クラスのものが使われることに注意してください。

```py
    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の実装はやはり気になるので、続きに関してもまた読んでいきたいと思います。