from mpqp import QCircuit
from mpqp.gates import *
circuit = QCircuit([H(0), CNOT(0,1), Ry(2.6, 0), Ry(-0.87, 1)])
print(circuit)

from mpqp.measures import Observable, ExpectationMeasure

import numpy as np
from mpqp.tools.maths import is_hermitian

matrix = np.array([[4,  2,  3, 8],
                   [2, -3,  1, 0],
                   [3,  1, -1, 5],
                   [8,  0,  5, 2]])
is_hermitian(matrix)

obs = Observable(matrix)

circuit.add(ExpectationMeasure([0,1], observable=obs, shots=0))

from mpqp.execution import run, ATOSDevice, IBMDevice
result = run(circuit, ATOSDevice.MYQLM_PYLINALG)
print(result)
print(result.expectation_value)

circuit = circuit.without_measurements()
circuit.add(ExpectationMeasure([0,1], observable=obs, shots=2000))

results = run(circuit, [ATOSDevice.MYQLM_PYLINALG, IBMDevice.AER_SIMULATOR])
print(results)

from mpqp.measures import I, X, Y, Z

ps_1 = I@Z - 3 * X@Y
print(f"{ps_1=}")

ps_2 = I@Z + 2.555555555*Y@I + X@Z - X@Z
print("ps_2 =",repr(ps_2))
print("     =",repr(ps_2.simplify()))
print("    ~=",repr(ps_2.round(1)))
print("    ~=",ps_2)

ps_2 = ps_2.round(1).simplify()
print(f"""Addition:
({ps_1}) + ({ps_2}) = {ps_1 + ps_2}

Subtraction:
({ps_1}) - ({ps_2}) = {ps_1 - ps_2}

Scalar product:
2 * ({ps_1}) = {2 * ps_1}

Scalar division:
({ps_2}) / 3 ~= {ps_2 / 3}

Tensor product:
({ps_1}) @ Z = {ps_1@Z}
({ps_1}) @ ({ps_2}) = {ps_1@ps_2}""")

obs1 = Observable(ps_1)

print("`obs` created with matrix:")
print("matrix:")
print(obs.matrix)
print("Pauli string:")
print(obs.pauli_string)


print("\n\n`obs1` created with Pauli string:")
print("Pauli string:")
print(obs1.pauli_string)
print("matrix:")
print(obs1.matrix)

circuit = circuit.without_measurements()
circuit.add(ExpectationMeasure([0, 1], observable=obs1, shots=1000))

results = run(
    circuit,
    [
        ATOSDevice.MYQLM_PYLINALG,
        IBMDevice.AER_SIMULATOR,
        ATOSDevice.MYQLM_CLINALG,
    ],
)
print(results)