from qiskit import BasicAer
from qiskit.utils import QuantumInstance, algorithm_globals
from qiskit.algorithms.optimizers import COBYLA, SLSQP
from qiskit.circuit.library import TwoLocal, ZZFeatureMap,RealAmplitudes
from qiskit_machine_learning.algorithms import VQC
from qiskit_machine_learning.datasets import ad_hoc_data

seed = 1376
algorithm_globals.random_seed = seed

import pandas as pd
import numpy as np
Iris_data=pd.read_csv('Iris.csv')
Iris_data.drop(['Id'], axis=1)
# print(Iris_data)
# Iris_data.head()
# import numpy as np
# d = np.loadtxt("Iris.csv", delimiter=",")

feature_cols = [
    'SepalLengthCm',
    'SepalWidthCm',
    'PetalLengthCm',
    'PetalWidthCm'
]

target_col = ['Species']

from sklearn.model_selection import train_test_split

xtrain = Iris_data[feature_cols]
ytrain = Iris_data[target_col]#.astype(int)
# ytrain = pd.get_dummies(Iris_data, columns = ['Species'])
# print(ytrain)
ytrain_new = pd.get_dummies(ytrain, columns=["Species"])
# print("The transform data using get_dummies")
# print(ytrain_new)

x_train, x_test, y_train, y_test = train_test_split(xtrain, ytrain_new, test_size=0.25, random_state=42)

x_train.shape, x_test.shape, y_train.shape, y_test.shape

# sv = Statevector.from_label('0' * n)
# feature_map = ZZFeatureMap(feature_dimension=len(feature_cols), reps=2, entanglement="linear")
feature_map = ZZFeatureMap(feature_dimension=4, reps=3, entanglement='linear', insert_barriers=True)
# ansatz = RealAmplitudes(4, reps=1)
ansatz = TwoLocal(num_qubits=4, reps=3, rotation_blocks=['ry','rz'], 
               entanglement_blocks='cx', entanglement='circular', insert_barriers=True)
# ansatz = TwoLocal(feature_map.num_qubits, ['ry', 'rz'], 'cz', reps=3)
circuit = feature_map.combine(ansatz)
circuit.decompose().draw(output='mpl')

import numpy as np
import matplotlib.pyplot as plt
from IPython.display import clear_output

# callback function that draws a live plot when the .fit() method is called
def callback_graph(weights, obj_func_eval):
    clear_output(wait=True)
    objective_func_vals.append(obj_func_eval)
    plt.title("Objective function value against iteration")
    plt.xlabel("Iteration")
    plt.ylabel("Objective function value")
    plt.plot(range(len(objective_func_vals)), objective_func_vals)
    plt.show()

vqc = VQC(feature_map=feature_map,
          ansatz=ansatz,
          optimizer=COBYLA(maxiter=250),
          quantum_instance=QuantumInstance(BasicAer.get_backend('statevector_simulator'),
                                           shots=1024,
                                           seed_simulator=seed,
                                           seed_transpiler=seed),callback=callback_graph
          )


# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)

# fit classifier to data
vqc.fit(x_train, y_train.to_numpy())

# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)

# score classifier
vqc.score(x_test, y_test.to_numpy())
print(f"Testing accuracy: {score:0.2f}")

import os
from qiskit import QuantumCircuit, transpile, Aer
from qiskit.providers.aer import QasmSimulator
from qiskit.providers.aer.noise import NoiseModel
import numpy as np
from qiskit import QuantumCircuit, transpile
from qiskit.quantum_info import Kraus, SuperOp
from qiskit.providers.aer import AerSimulator
from qiskit.tools.visualization import plot_histogram
from qiskit.test.mock import FakeVigo
device_backend = FakeVigo()

backend = Aer.get_backend('aer_simulator')
counts1 = []
values1 = []
noise_model = None
device = QasmSimulator.from_backend(device_backend)
coupling_map = device.configuration().coupling_map
noise_model = NoiseModel.from_backend(device)
basis_gates = noise_model.basis_gates

print(noise_model)
print()

algorithm_globals.random_seed = seed
qi = QuantumInstance(backend=backend, seed_simulator=seed, seed_transpiler=seed,
                     coupling_map=coupling_map, noise_model=noise_model,)
vqc = VQC(feature_map=feature_map,
          ansatz=ansatz,
          optimizer=COBYLA(maxiter=250),
          quantum_instance=qi,callback=callback_graph
          )


# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)

# fit classifier to data
vqc.fit(x_train, y_train.to_numpy())

# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)

# score classifier
vqc.score(x_test, y_test.to_numpy())
print(f"Testing accuracy: {score:0.2f}")

from qiskit.utils.mitigation import CompleteMeasFitter

counts2 = []
values2 = []
if noise_model is not None:
    algorithm_globals.random_seed = seed
    qi2 = QuantumInstance(backend=backend, seed_simulator=seed, seed_transpiler=seed,
                         coupling_map=coupling_map, noise_model=noise_model,
                         measurement_error_mitigation_cls=CompleteMeasFitter,
                         cals_matrix_refresh_period=30)
vqc = VQC(feature_map=feature_map,
          ansatz=ansatz,
          optimizer=COBYLA(maxiter=250),
          quantum_instance=qi2,callback=callback_graph
          )


# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)

# fit classifier to data
vqc.fit(x_train, y_train.to_numpy())

# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)

# score classifier
vqc.score(x_test, y_test.to_numpy())
print(f"Testing accuracy: {score:0.2f}")

from qiskit.algorithms.optimizers import SPSA


vqc = VQC(feature_map=feature_map,
          ansatz=ansatz,
          optimizer=SPSA(maxiter=100),
          quantum_instance=QuantumInstance(BasicAer.get_backend('statevector_simulator'),
                                           shots=1024,
                                           seed_simulator=seed,
                                           seed_transpiler=seed),callback=callback_graph
          )

# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)

# fit classifier to data
vqc.fit(x_train, y_train.to_numpy())

# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)

# score classifier
vqc.score(x_test, y_test.to_numpy())
print(f"Testing accuracy: {score:0.2f}")


vqc = VQC(feature_map=feature_map,
          ansatz=ansatz,
          optimizer=SPSA(maxiter=250),
          quantum_instance=qi,callback=callback_graph
          )

# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)

# fit classifier to data
vqc.fit(x_train, y_train.to_numpy())

# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)

# score classifier
vqc.score(x_test, y_test.to_numpy())
print(f"Testing accuracy: {score:0.2f}")


vqc = VQC(feature_map=feature_map,
          ansatz=ansatz,
          optimizer=SPSA(maxiter=250),
          quantum_instance=qi2,callback=callback_graph
          )
# create empty array for callback to store evaluations of the objective function
objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)

# fit classifier to data
vqc.fit(x_train, y_train.to_numpy())

# return to default figsize
plt.rcParams["figure.figsize"] = (6, 4)

# score classifier
vqc.score(x_test, y_test.to_numpy())
print(f"Testing accuracy: {score:0.2f}")

