from qiskit.quantum_info import SparsePauliOp

op = SparsePauliOp.from_list([("ZZII", 1)])
num_qubits = op.num_qubits
ops = [op]

print(op)

target_energy = -1

from qiskit.circuit.library import EfficientSU2
circuit = EfficientSU2(num_qubits, reps=1, entanglement="linear", insert_barriers=True)

circuit.decompose().draw()

from qiskit_ibm_runtime import QiskitRuntimeService, Estimator
from qiskit.algorithms.optimizers import SPSA
import numpy as np

service = QiskitRuntimeService()
options = {"backend" : "ibmq_qasm_simulator"}

# Callback: number of function evals, parameters, function value, stepsize, whether step was accepted
history = {"nfevs": [], "points": [], "fvals": [], "updates": [], "accepted": []}

with Estimator(circuits=circuit, observables=ops, options=options, service=service) as estimator:
    
    def evaluate_expectation(x):
        x = list(x)
        results = estimator(circuits=[0], observables=[0], parameter_values=[x]).values[0]
        return np.real(results)  
    
    def callback(nfev, point, fval, update, accepted):
        print('expectation value: {}'.format(fval))
        history["nfevs"].append(nfev)
        history["points"].append(point)
        history["fvals"].append(fval)
        history["updates"].append(update)
        history["accepted"].append(accepted)

    initial_point = np.random.random(circuit.num_parameters)
    optimizer = SPSA(20, callback=callback)
    result = optimizer.minimize(fun=evaluate_expectation, x0=initial_point)


history["fvals"][-1] - target_energy

print('optimal point:\n{}'.format(result.x))

import qiskit_ibm_runtime
qiskit_ibm_runtime.version.get_version_info()

from qiskit.tools.jupyter import *

%qiskit_version_table
%qiskit_copyright