from qiskit import QuantumCircuit

#define quantum circuit
qc = QuantumCircuit(4)

#add gates
qc.h(0)
qc.cx(0,1)
qc.h(2)
qc.x(3)
qc.cx(2,3)

qc.measure_all()

#draw
qc.draw('mpl', style='clifford') #draw

result = sampler.run(qc)
print(result)

from qiskit.primitives import Sampler
from qiskit.visualization import plot_distribution

sampler = Sampler()
samp_dist = sampler.run(qc).result().quasi_dists[0]
plot_distribution(samp_dist.binary_probabilities(), figsize=(15, 5))

from qiskit.circuit.library import NLocal, CZGate, RZGate, RXGate
from qiskit.circuit import Parameter
from qiskit import QuantumCircuit

qc = QuantumCircuit(5)
qc.x(0)
qc.barrier()

theta = Parameter("θ")
ansatz = NLocal(
    initial_state = qc,
    num_qubits=5,
    rotation_blocks=[RXGate(theta), RZGate(theta)],
    entanglement_blocks = [CZGate()],
    entanglement=[[0, 1], [0, 2], [2, 1], [2, 4]],
    reps=2,
    insert_barriers=True,
)
ansatz.decompose().draw("mpl")

from qiskit.primitives import Estimator
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp

O = SparsePauliOp(["X", "Z"], [2, -1])
estimator = Estimator()

qc1 = QuantumCircuit(1)
job = estimator.run(qc1, O)
expvals1 = job.result().values
print("Exp. val qs1: ", expvals1)

qc2 = QuantumCircuit(1)
qc2.h(0)
job = estimator.run(qc2, O)
expvals2 = job.result().values
print("Exp. val qs2: ", expvals2)

from qiskit.circuit.library import NLocal, CXGate, RZGate, RXGate
from qiskit.circuit import Parameter
from qiskit import QuantumCircuit

# Define an initial state with two qubits
qc = QuantumCircuit(2)
qc.x(0)
qc.barrier()

# Define an general ansatz
theta = Parameter("θ")
ansatz = NLocal(
    initial_state = qc,
    num_qubits=2,
    rotation_blocks=[RXGate(theta), RZGate(theta)],
    entanglement_blocks = [CXGate()],
    entanglement=[[0, 1]],
    reps=1,
    insert_barriers=True,
)
ansatz.decompose().draw("mpl", style='Clifford')

from scipy.optimize import minimize
from qiskit.quantum_info import SparsePauliOp
from qiskit.primitives import Estimator
import numpy as np

# Define observable and estimator 
observable = SparsePauliOp(["IZ", "ZI"], [-1,-1])
estimator = Estimator()

# Define cost function
def cost_func(params, ansatz, observable, estimator):
    cost = estimator.run(ansatz, observable, parameter_values=params).result().values[0]
    return cost

# Define initial state of parameters randomly
x0 = 2 * np.pi * np.random.rand(ansatz.num_parameters)

# Minimize using an optimization method like COBYLA, SLSQP...
res = minimize(cost_func, x0, args=(ansatz, observable, estimator), method="COBYLA")
print(res)

qc = ansatz.assign_parameters(res.x)
qc.measure_all()
qc.draw("mpl", style="clifford")

from qiskit.visualization import plot_distribution

samp_dist = sampler.run(qc).result().quasi_dists[0]
plot_distribution(samp_dist.binary_probabilities(), figsize=(15, 5))

