Qiskit: WeightedPauliOperator, MatrixOperator, InitialStateなどなど

# はじめに

Qiskitで量子プログラミングをするのに際して重要なclassの使い方を記します。
特に、Qiskit AquaなどでQAOAだったりを使うときに必要かなと思います。
まだまだ未熟なところもあると思いますが、参考にしてくれると嬉しいです。

# WeightedPailiOperator

QAOAだったり、VQEだったりにハミルトニアンを読み込ませるときに使うclass methodです。
qiskit.aqua.optimization.ising ではmax cutやtspなどのWeigthedPauliOperatorを返してくれるclass methodが提供されていますが、実際に自分で作ろうとすると少し大変です。
WeightedPauliOperatorに入れるclass methodはPauli classと呼ばれるものです。この説明を行いたいと思い餡巣。

## Pauli

まずは、sample code
とりあえず、使う物すべてimport

```python:
import numpy as np

from qiskit.aqua.operators import WeightedPauliOperator, MatrixOperator
from qiskit.aqua.operators.op_converter import to_weighted_pauli_operator
from qiskit.aqua.components.initial_states import Custom, InitialState
from qiskit.quantum_info import Pauli
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
```

次に一番簡単な例

```python:
pauli_list = []
num_qubit = 3
z_p = np.zeros(num_qubit, dtype=bool)
x_p = np.zeros(num_qubit, dtype=bool)
pauli_list.append([1, Pauli(z_p, x_p)])
```

この場合はIIIをハミルトニアンに追加します。
次に、ZIZを追加したいとき

```python:
z_p[0] = True
z_p[2] = True
pauli_list.append([1, Pauli(z_p, x_p)])
```

ちなみに、appendしているlistの一つ目の要素（今は1）のところにはcomplex numberを入れることができます。次に、XXXを追加したいとき。

```python:
for i in range(num_qubit):
    x_p[i] = True
z_p = np.zeros(num_qubit, dtype=bool)
pauli_list.append([1, Pauli(z_p, x_p)])
```

Yを入れたいときはxもzもTrueにすればいいですね。
これを直接QAOAなどには入力できないので

```python:
qubitOp = WeightedPauliOperator(paulis=pauli_list)
```

で、完了です。
ちなみに、中身を見たい場合は

```python:
print(qubitOp.print_details())
```

でok
出力は

```
III	(1+0j)
ZIZ	(1+0j)
XXX	(1+0j)
```

# MatrixOperator

このclassはもっと直感的に記述したい人におすすめです。
例えばZ operatorを入れたいときは

```python:
z = np.array([[1, 0], [0, -1]])
z = MatrixOperator(z)
# 中身を見てみる
print(z.print_details())
```

出力は

```
  (0, 0)	1
  (1, 1)	-1
```

この形はなんだろう？？と思うかもしれませんが、

```math
|0><0|-|1><1| 
```
のことですね。
しかし、このままではQAOAなどに入力することができません。そこでconvertします。

```python:
z = to_weighted_pauli_operator(z)
# 中身を見てみる
print(z.print_details())
```

出力は

```
Z	(1+0j)
```

しっかり変形されましたね。しかしながら、複数のqubitを使っていきたい場合はtensorを計算しないといけないので、少し大変です。


# InitialState

QAOAの関数の四つ目の引数のinitial_state
使い方がいまいちわからずInitialStateを使えばよいことは分かるが、いまいち、、、
調べてみてもなかなか出なかったのでここに記します。

## Custom

circuitをInitialStateに変換するmethodがCustomです。
例えば

```math:
\frac{|00>+|01>+|10>+|11>}{2}
```

をinitial stateとして使いたいとき

```python:
qr = QuantumRegister(2)
qc = QuantumCircuit(qr)
qc.h(qr)

Custom(2, circuit=qc)
```

とすれば、QuantumCircuit classをInitialState classに変換することができます。

## 追記

Customを使うにあたって，qiskitのライブラリーを書き換える箇所があります．
qiskit/aqua/algorithms/minimum_eigen_solvers/qaoa/var_form.pyの中の

```python:
def construct_circuit(self, parameters, q=None):
    """ construct circuit """
    angles = parameters
    if not len(angles) == self.num_parameters:
        raise ValueError('Incorrect number of angles: expecting {}, but {} given.'.format(
            self.num_parameters, len(angles)
        ))
    # initialize circuit, possibly based on given register/initial state
    if q is None:
        q = QuantumRegister(self._num_qubits, name='q')
    if self._initial_state is not None:
        circuit = self._initial_state.construct_circuit('circuit', q)
    else:
        circuit = QuantumCircuit(q)
    circuit.u2(0, np.pi, q)
    for idx in range(self._p):
        beta, gamma = angles[idx], angles[idx + self._p]
        circuit += self._cost_operator.evolve(
            evo_time=gamma, num_time_slices=1, quantum_registers=q
        )
        circuit += self._mixer_operator.evolve(
            evo_time=beta, num_time_slices=1, quantum_registers=q
        )
    return circuit
```

を

```python:
def construct_circuit(self, angles):
    if not len(angles) == self.num_parameters:
        raise ValueError('Incorrect number of angles: expecting {}, but {} given.'.format(
            self.num_parameters, len(angles)
        ))
    circuit = QuantumCircuit()
    if self._initial_state:
        circuit += self._initial_state.construct_circuit('circuit')
    if len(circuit.qregs) == 0:
        q = QuantumRegister(self._cost_operator.num_qubits, name='q')
        circuit.add_register(q)
    elif len(circuit.qregs) == 1:
        q = circuit.qregs[0]
    else:
        raise NotImplementedError
    circuit.u2(0, np.pi, q)
    for idx in range(self._p):
        beta, gamma = angles[idx], angles[idx + self._p]
        circuit += self._cost_operator.evolve(None, evo_time=gamma, num_time_slices=1, quantum_registers=q)
        circuit += self._mixer_operator.evolve(None, evo_time=beta, num_time_slices=1, quantum_registers=q)
    return circuit
```

# まとめ

自分で作ったHamiltonianでQAOAを実行してみたかったので勉強しました。
Qiskitの資料がネットに少ないので少しでも助けになればと思っています。