# parameterized execution algorithm example

from qiskit import Aer, QuantumCircuit, transpile
from qiskit.circuit import ParameterVector
from qiskit.visualization import plot_histogram

# Set to true for more text output detailing execution steps
VERBOSE = True

# set target backend and number of shots
BACKEND = Aer.get_backend("qasm_simulator")
NUM_SHOTS = 1000

# Fake function to update parameters based on the measurement results
def update_params_from_measurement_results(thetas_array, result):
    """Update theta parameters with max count value divided by NUM_SHOTS."""
    update = 0.1 * max(result.values()) / NUM_SHOTS
    return list(map(lambda theta: round(theta+update, 3), thetas_array))

# create initial set of 3 'theta' parameters
thetas = [1.0, 2.0, 3.0]
len_thetas = len(thetas)
params = ParameterVector("θ", len_thetas)

# create quantum circuit with these parameters as arguments to the gates
qc = QuantumCircuit(len_thetas)
for qidx, param in enumerate(params):
    qc.rx(param, qidx)

qc.measure_all()

if VERBOSE: print(qc.draw(output='text', fold=-1))

# execute this circuit ten times (and solve all the world's energy problems!)
energy_value = 0
for rep in range(0, 10):
    # first time, do transpile to backend
    if rep == 0:
        if VERBOSE: print("\nTranspiling circuit")
        qc_trans = transpile(qc, BACKEND)

    # bind parameters to the saved and already transpiled circuit
    if VERBOSE: print(f"\nBinding {thetas = } to qc_exec")
    qc_exec = qc_trans.bind_parameters({params: thetas})

    # execute this transpiled and bound circuit on the backend
    job = BACKEND.run(qc_exec, shots=NUM_SHOTS)

    # get results from completed joband
    counts = job.result().get_counts()
    
    # Plot a histogram
    plot_histogram(counts)
    
    if VERBOSE: print(f'{counts = }\nwith {thetas = }')

    # compute new params from the original ones and the measurement results obtained
    # NOTE: this is highly app-specific as in VQE/QAOA cost function calcs
    # Kludgy mimic: Do not update thetas on last iteration as circuit will not be ran again
    if rep != 9:
        thetas = update_params_from_measurement_results(thetas, counts)

    # Example of energy_value update function, actual calculation is problem specific
    energy_value += min(counts.values())

print(f'\n----------------------')
print(f'Final {energy_value = }')
print(f'Final {counts = }\nwith {thetas = }')

# Uncomment below to visualize results
from qiskit.visualization import plot_histogram
import matplotlib.pyplot as plt
plot_histogram(counts)
plt.show()

