100マス量子回路計算やってみよう〜中級編〜

大好評だった[初級編](https://qiita.com/gyu-don/items/3e4e9ef799c5bb377960)に引き続き、中級編もやってみましょう。
今回、2量子ビットに増えて、CNOTゲートが加わりました。

それに伴い、状態の数がめちゃくちゃに増えました。
|0>, |1>, |+>, |->, |i>, |-i>の直積で書けるものや、4つのベル状態

```math
|\Phi_+>=(|00>+|11>)/ \sqrt{2}
|\Phi_->=(|00>-|11>)/ \sqrt{2}
|\Psi_+>=(|01>+|10>)/ \sqrt{2}
|\Psi_->=(|01>-|10>)/ \sqrt{2}
```

で書けるものもありますが、位相が違っていてそのような書き方ができないものも多数出来ました。

なので、今回は、直積やベル状態で書くか、1/√2|00> - 1/√2|10>のように書くか、各状態の成分を書くかのいずれかで回答してください。
また、1/2と1/√2は省略してもいいものとします。

暗算でやるのはかなり難しいと思いますが、がんばってみてください。

# 100マス量子計算中級編
![100マス量子計算中級編.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/88256/8201ba44-7e47-9ecb-b56f-104cdf60ea5a.png)

# 答え
今回は、左列から順に縦方向で答えを表示しています。


```py
from cmath import phase
from math import pi
import math
import numpy as np
from blueqat import Circuit

# 中級編
tate = [
    Circuit().h[0].cx[0, 1],
    Circuit().h[0].cx[0, 1].h[1],
    Circuit().x[0].h[0].cx[0, 1],
    Circuit().h[0, 1],
    Circuit().x[0].h[1],
    Circuit().h[:].s[0].sdg[1],
    Circuit().h[0].sdg[0].y[1],
    Circuit().h[0].cx[0, 1].s[1],
    Circuit().h[0].cx[0, 1].y[0],
    Circuit().h[0].cx[0, 1].y[:],
]

yoko = [
    Circuit().h[0].cx[0, 1],
    Circuit().cx[1, 0].h[1],
    Circuit().y[0, 1],
    Circuit().s[1].cx[0, 1].h[1],
    Circuit().h[:].s[0].sdg[1],
    Circuit().z[1].cx[1, 0].h[1],
    Circuit().x[0].z[1].cx[0, 1].h[0],
    Circuit().h[0].cx[0, 1].y[0],
    Circuit().h[0].cx[0, 1].s[1],
    Circuit().cx[0, 1].cx[1, 0],
]

states = [
    (np.array([1, 0], dtype=complex), '|0>'),
    (np.array([0, 1], dtype=complex), '|1>'),
    (np.array([1, 1], dtype=complex) / np.sqrt(2), '|+>'),
    (np.array([1, -1], dtype=complex) / np.sqrt(2), '|->'),
    (np.array([1, 1j], dtype=complex) / np.sqrt(2), '|i>'),
    (np.array([1, -1j], dtype=complex) / np.sqrt(2), '|-i>'),
]
sqrt2_inv = 1/2**0.5

def to_str(val, label, is_firstterm):
    val_abs = abs(val)

    if abs(val_abs) < 1e-5:
        return '' # Zero
    if abs(val_abs - 1) < 1e-5:
        val_s = ''
    elif abs(val_abs - sqrt2_inv) < 1e-5:
        val_s = '/√2'
    elif abs(val_abs - 0.5) < 1e-5:
        val_s = '/2'
    else:
        raise ValueError('Strange value. val = ', val)

    if is_firstterm:
        sign = ''
    else:
        sign = '+'
    imag = '1' # if real '1' else 'i'
    val_phase = phase(val)
    if abs(val_phase) < 1e-5:
        pass
    elif abs(val_phase - 0.5 * pi) < 1e-5:
        imag = 'i'
    elif abs(val_phase + 0.5 * pi) < 1e-5:
        sign = '-'
        imag = 'i'
    elif abs(val_phase - pi) < 1e-5 or abs(val_phase + pi) < 1e-5:
        sign = '-'
    else:
        raise ValueError('Strange phase. val = ', val)

    if imag == '1' and val_s == '':
        imag = ''
    if is_firstterm:
        return sign + imag + val_s + '|' + label + '>'
    return sign + ' ' + imag + val_s + '|' + label + '>'



def to_phasearr(state):
    zeros = np.isclose(state, 0.j)
    factor = math.sqrt(4 - sum(zeros))

    def to_phase(val, is_zero):
        if is_zero:
            return 0
        if np.isclose(val * factor, 1):
            return 1
        if np.isclose(val * factor, -1):
            return -1
        if np.isclose(val * factor, 1j):
            return 1j
        if np.isclose(val * factor, -1j):
            return -1j
        raise ValueError(f'val: {val}')

    return [to_phase(val, zero) for val, zero in zip(state, zeros)]


def check_bell(state):
    if np.allclose(state, np.array([sqrt2_inv, 0, 0, sqrt2_inv])):
        return '|Φ+>'
    if np.allclose(state, np.array([sqrt2_inv, 0, 0, -sqrt2_inv])):
        return '|Φ->'
    if np.allclose(state, np.array([0, sqrt2_inv, sqrt2_inv, 0])):
        return '|ψ+>'
    if np.allclose(state, np.array([0, sqrt2_inv, -sqrt2_inv, 0])):
        return '|ψ->'
    return None

single = {
        '|0>': np.array([1, 0], dtype=complex),
        '|1>': np.array([0, 1], dtype=complex),
        '|+>': np.array([sqrt2_inv, sqrt2_inv], dtype=complex),
        '|->': np.array([sqrt2_inv, -sqrt2_inv], dtype=complex),
        '|i>': np.array([sqrt2_inv, 1j * sqrt2_inv], dtype=complex),
        '|-i>': np.array([sqrt2_inv, -1j * sqrt2_inv], dtype=complex)
}
double = {k2 + k1: np.kron(s1, s2) for k1, s1 in single.items() for k2, s2 in single.items()}

def check_kron(state):
    for k, v in double.items():
        if np.allclose(state, v):
            return k
    return None

def get_answer(c1, c2):
    state = (c1.copy() + c2).run(ignore_global=True)
    bell = check_bell(state)
    kron = check_kron(state)
    ss = []
    for val, label in zip(state, ['00', '10', '01', '11']):
        s = to_str(val, label, not ss)
        if s:
            ss.append(s)
    s = ' '.join(ss)
    if bell:
        s = bell + '\t= ' + s
    elif len(ss) > 1 and kron:
        s = kron + '\t= ' + s
    return s

for c1 in yoko:
    for c2 in tate:
        print(get_answer(c2, c1))
    print('')
```

# 上級編?

量子ビット増やすとか、ゲート増やすとか、いくらでも難しくしようはあるんですが、ぶっちゃけこれ以上いりますかね……