import numpy as np

from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import BasicAer
from qiskit.compiler import transpile
from qiskit.quantum_info.operators import Operator, Pauli
from qiskit.quantum_info import process_fidelity

from qiskit.extensions import RXGate, XGate, CXGate

#Operator create
XX = Operator([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]])
XX

# Operater propaty

XX.data

input_dim, output_dim = XX.dim
input_dim, output_dim

#InputとOutputの関数でサブシステムの追跡可能

op = Operator(np.random.rand(2 ** 1, 2 ** 2))
print('Input dimensions:', op.input_dims())
print('Output dimesions:', op.output_dims())


#6*6の行列場合
op = Operator(np.random.rand(6, 6))
print('Input dimensions:', op.input_dims())
print('Output dimensions:', op.output_dims())


#初期化時に手動でして可能
op = Operator(np.random.rand(2 ** 1, 2 ** 3), input_dims=[8])
print('Input dimensions:', op.input_dims())
print('Output dimensions:', op.output_dims())

# Specify system is a qubit and qutrit
op = Operator(np.random.rand(6, 6),
              input_dims=[2, 3], output_dims=[2, 3])
print('Input dimensions:', op.input_dims())
print('Output dimensions:', op.output_dims())

print('Dimension of input system 0:', op.input_dims([0]))
print('Dimension of iutput system 1:', op.input_dims([1]))

#Pauliオブジェクトからオペレーターに変換

pauliXX = Pauli('XX')
Operator(pauliXX)


#Gateオブジェクトからオペレータに変換
Operator(CXGate())

#parameterized gateオブジェクトからオペレータに変換
Operator(RXGate(np.pi / 2))

#QuantumCircuitオブジェクトからオペレータに変換
circ = QuantumCircuit(10)
circ.h(0)
for j in range(1, 10):
    circ.cx(j-1, j)

Operator(circ)

#回路にオペレータ利用
XX = Operator(Pauli('XX'))

circ = QuantumCircuit(2,2)
circ.append(XX, [0,1])
circ.measure([0,1],[0,1])
circ.draw('mpl')

backend = BasicAer.get_backend('qasm_simulator')
circ =transpile(circ, backend, basis_gates=['u1', 'u2', 'u3', 'cx'])
job =backend.run(circ)
job.result().get_counts(0)

circ2 = QuantumCircuit(2,2)
circ2.append(Pauli('YY'),[0, 1])
circ2.measure([0,1], [0,1])

circ2.draw()

#単一量子ビットオペレータの場合
A = Operator(Pauli('X'))
B = Operator(Pauli('Z'))
A.tensor(B)

#順序が逆になるテンソル積
A = Operator(Pauli('X'))
B = Operator(Pauli('Z'))

A.expand(B)

A = Operator(Pauli('X'))
B = Operator(Pauli('Z'))
A.compose(B)

#順番を逆にする役割
A = Operator(Pauli('X'))
B = Operator(Pauli('Z'))

A.compose(B, front= True)

#Compose XZと3-qubit indetity
op = Operator(np.eye(2 ** 3))
XZ = Operator(Pauli('XZ'))
op.compose(XZ, qargs=[0,2])

op.is_unitary()

#Compose YXと前のオペレータ
op = Operator(np.eye(2 ** 4))
YX = Operator(Pauli('YX'))
op.compose(XZ, qargs= [0,2], front=True)

op.is_unitary()

#標準な線形オペレータ(非ユニタリー性）
XX = Operator(Pauli('XX'))
YY = Operator(Pauli('YY'))
ZZ = Operator(Pauli('ZZ'))

op = 0.5 * (XX + YY -6 * ZZ)
op

op.is_unitary()

Operator(np.eye(2)).compose([[0,1],[1,0]])

#Operatorの近似的チェック法

Operator(Pauli('X'))!= Operator(XGate())

Operator(XGate()) != np.exp(1j * 0.5) * Operator(XGate())

op_a = Operator(XGate())
op_b = np.exp(1j * 1) * Operator(XGate())

F = process_fidelity(op_a, op_b)
print('Process fidelity =', F)

import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright

