QiskitのMulti-controlled gateの実装に関するメモ

調べた日: 2021年7月中旬
調べたリポジトリ: https://github.com/Qiskit/qiskit-terra

# 定義されているmulti-controledゲート

## QuantumCircuitクラス内で定義されているmulti-controlledゲートたち
- mcp
    - `library.standard_gates.p.MCPhaseGate` を回路に引っ付ける
- mcu1
    - `library.standard_gates.u1.MCU1Gate` を回路に引っ付ける
- mcx
    - 実装が複数あり、オプション引数の`mode`で実装を選択する
    - "noancilla" (デフォルト)
        - `library.standard_gates.x.MCXGrayCode` を回路にくっつける
    - "recursion"
        - `library.standard_gates.x.MCXRecursive` を回路にくっつける
    - "v-chain"
        - `library.standard_gates.x.MCXVChain` を回路にくっつける。`dirty_ancillas`オプションはFalse
    - "v-chain-dirty"
        - `library.standard_gates.x.MCXVChain` を回路にくっつける。`dirty_ancillas`オプションはTrue
    - 互換性のために残されている古いもの
        - "advanced" → "recursion"
        - "basic"→ "v-chain"
        - "basic-dirty-ancilla" → "v-chain-dirty"
- mct
    - mcxと同じ (multi-controlled Toffoli?)

## QuantumCircuitクラスの外で定義されているmulti-controlledゲートたち
`library.standard_gates.multi_control_rotation_gates`内で、

- mcrx
- mcry
- mcrz

がQuantumCircuitクラスを拡張する形で定義されている。ややメンテされていないっぽく感じる。

## `Gate.control`によるmulti-controlled gate

このような方法も提供している。

https://qiskit.org/documentation/stubs/qiskit.circuit.ControlledGate.html?highlight=controlledgate#qiskit.circuit.ControlledGate

この方法を利用すると`add_control`([source](https://github.com/Qiskit/qiskit-terra/blob/c0e98390a16fbe7525a33307ae9e086e7ffd5f28/qiskit/circuit/add_control.py#L22))が呼び出される。

なお、ゲートによっては、Gate.controlを再実装しているものもあるが、主には、control版のゲートがある場合はGate.controlを呼び出さずにそちらを使うというもので、概ね`add_control`が呼ばれると考えて問題ない。

## `QuantumCircuit.control`によるmulti-controlled gate
`Gate.control`が間接的に呼び出される。

# 実装

基本的には、グレイコードによる実装になっているものが多い。グレイコードによる実装は[この記事](https://qiita.com/gyu-don/items/7c52c0d20f9998022803)にある。
**グレイコードによる実装では、controlビットの数に応じて、指数関数的に回路が長くなることに注意が必要**である。
ただし、mcrx, mcry, mcrz以外は、その場では展開されず、展開するかしないかは(ちゃんと調べていないが、恐らく)バックエンドに委ねられるので、シミュレータであれば、巨大なmulti-controlledな何かを使った方が有利な可能性が高い。

mcxについては、"recursive", "v-chain", "v-chain-dirty"によって、ancillaを使うことで指数関数的な回路増加を避ける方法を提供している。

実機が対象で、controlビットが多いなら、他のものもmcxを使って書いた方が回路が短くなるかもしれない。だが、ネガティブなことを言うと、実機で巨大なmulti-controlledゲートが必要になっている時点で、そもそも動かない可能性を考えたほうがいい。

## mcpの実装

Pゲート(位相ゲート)は

$$
         P(\lambda) = \begin{pmatrix}
                1 & 0 \\\\ 
                0 & e^{i\lambda}
            \end{pmatrix}
$$

で定義されていて、mcpゲートはそのmulti-control版です。

`MCPhaseGate._define`にて、そのグレイコード実装があります。(ちゃんと調べていないですが、この実装を使うかどうかはバックエンド依存で、実機などではこれが使われるのではないかと思われます)


## mcu1の実装

QiskitにおけるU1ゲートは、Pゲートと同じなので、mcpゲートと同様に`MCU1Gate._define`が定義されています。

## mcx, mctの実装

mctはmcxと同じです。また、各オプションごとに実装が分かれます。

### "noancilla"
`MCXGrayCode._define`に実装があります。なお、`MCXGate._define`は`MCXGrayCode._define`を呼び出すようなっています。
実装としては、MCU1ゲート(将来的にMCPゲートに変わると思われます)の標的の前後にHゲートをあてたものになります。

### "recursion"
まず、C3X, C4Xは予め用意されており、これらはグレイコードやそれに近い実装でancillaなしに作られています。
制御ビットが4つ以内なら、既存の実装を使います。

制御ビットが5以上の場合、ancillaを1つ利用し、再帰的に回路を作ります。
`MCX(controls, target, ancilla)`のような表記をし、以下に`MCX(qs, t, a)`の実装を示します。

- qsの長さが3または4なら、C3X, C4Xの回路を返します
    - 以下を注意して読むと、qsの長さは3を下回らないことが分かりますが、特にそれ自体は大切ではないと思います。(1や2だったらCXやcCXを返せばいいだけ)
- 制御ビットを前半分と後ろ半分に分けます(端数が出た場合は、前半分をひとつ大きくします)
    - これをqs0, qs1と表記することにします
- 回路に`MCX(qs0, a, qs1[0])`をつなげます
- 回路に`MCX(qs1 + [a], t, qs0[-1])` をつなげます
- 回路に`MCX(qs0, a, qs1[0])`をつなげます
- 回路に`MCX(qs1 + [a], t, qs0[-1])`をつなげます
- できあがった回路を返します

ここで、ancillaに`qs0[-1]`や`qs1[0]`を雑に指定していますが、これをしても困らない(ancillaが0でなくていい)ことを示すには、やや根性ビット演算を要します。

### "v-chain"
`MCXVChain._define`に実装があります。
基本的には、(制御ビット数 - 2)のancillaを使ってトフォリゲートをつなげたものですが、トフォリゲートと相対位相を除いて等価で、分解時にゲート数が少なくて済む[RCCXゲート](https://qiskit.org/documentation/stubs/qiskit.circuit.library.RCCXGate.html)が代わりに使われます。

### "v-chain-dirty"
`MCXVChain._define`に実装があります。ancillaが|0>になっていなくても動くようにしていて、[この論文](https://arxiv.org/pdf/quant-ph/9503016.pdf)や[この論文](https://arxiv.org/pdf/1508.03273.pdf)を元にしているようです。必要なancilla数はv-chainと変わりませんが、回路はv-chainよりも長くなります。

## mcrx, mcry, mcrz
最近の記法と相性が悪く、メンテされていないっぽく感じます。次のように使います。

```py
qc = QuantumCircuit(4)
qc.mcrx(0.5, qc.qregs[0][:3], qc.qregs[0][3])
qc.draw()
```

mcxなどのように、ゲートを一つ付け加えるのではなく、呼び出し時にゲートが展開される手法になっています。
実装はグレイコードにより行われています。

## `Gate.control`
概ね、ゲートの種類ごとにif文で分けてmulti-controlled版を使っている実装になっています。
xやcxはmcxを、zはh + mcx + hを使うなどの工夫はされています。

# 印象
mcxだけえらいがんばってる印象を受けました。`Gate.control`は機能として面白いですが、実装はやっぱりこのへんが落とし所なんだろうなという感想になりました。