from sklearn.datasets import load_iris

iris_data = load_iris()

print(iris_data.DESCR)

features = iris_data.data
labels = iris_data.target

from sklearn.preprocessing import MinMaxScaler

features = MinMaxScaler().fit_transform(features)

import pandas as pd
import seaborn as sns

df = pd.DataFrame(iris_data.data, columns=iris_data.feature_names)
df["class"] = pd.Series(iris_data.target)

sns.pairplot(df, hue="class", palette="tab10")

from sklearn.model_selection import train_test_split
from qiskit.utils import algorithm_globals

algorithm_globals.random_seed = 123
train_features, test_features, train_labels, test_labels = train_test_split(
    features, labels, train_size=0.8, random_state=algorithm_globals.random_seed
)

from sklearn.svm import SVC

svc = SVC()
_ = svc.fit(train_features, train_labels)  # suppress printing the return value

train_score_c4 = svc.score(train_features, train_labels)
test_score_c4 = svc.score(test_features, test_labels)

print(f"Classical SVC on the training dataset: {train_score_c4:.2f}")
print(f"Classical SVC on the test dataset:     {test_score_c4:.2f}")

from qiskit.circuit.library import EfficientSU2

circuit = EfficientSU2(num_qubits=3, reps=1, insert_barriers=True)
circuit.decompose().draw(output='mpl')

x = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0,1.1,1.2]

encode = circuit.bind_parameters(x)
encode.decompose().draw(output='mpl')

from qiskit.circuit.library import ZZFeatureMap

circuit = ZZFeatureMap(3, reps=1, insert_barriers=True)
circuit.decompose().draw(output='mpl')

x = [0.1,0.2,0.3]

encode = circuit.bind_parameters(x)
encode.decompose().draw(output='mpl')

from qiskit.circuit.library import ZZFeatureMap

x = [-0.1,0.2]
# YOUR CODE HERE


# circuit = 
# encode = 
circuit.decompose().draw(output='mpl')

from qiskit.circuit.library import ZZFeatureMap

num_features = features.shape[1]

feature_map = ZZFeatureMap(feature_dimension=num_features, reps=1)
feature_map.decompose().draw(output="mpl", fold=20)

from qiskit.circuit.library import RealAmplitudes

ansatz = RealAmplitudes(num_qubits=num_features, reps=3)
ansatz.decompose().draw(output="mpl", fold=20)

from qiskit.algorithms.optimizers import COBYLA

optimizer = COBYLA(maxiter=100)

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

objective_func_vals = []
plt.rcParams["figure.figsize"] = (12, 6)


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()

from qiskit_ibm_runtime import QiskitRuntimeService, Session, Options, Estimator, Sampler
service = QiskitRuntimeService(channel='ibm_quantum')

service.backends()
backend = service.backends(simulator=True)[0]
print(backend)

import time
from qiskit_machine_learning.algorithms.classifiers import VQC
# from qiskit.primitives import Sampler

with Session(service = service, backend = backend):
    vqc = VQC(
        sampler=Sampler(),
        feature_map=feature_map,
        ansatz=ansatz,
        optimizer=optimizer,
        callback=callback_graph,
    )

    # clear objective value history
    objective_func_vals = []

    start = time.time()
    vqc.fit(train_features, train_labels)
    elapsed = time.time() - start


print(f"Training time: {round(elapsed)} seconds")

train_score_q4 = vqc.score(train_features, train_labels)
test_score_q4 = vqc.score(test_features, test_labels)

print(f"Quantum VQC on the training dataset: {train_score_q4:.2f}")
print(f"Quantum VQC on the test dataset:     {test_score_q4:.2f}")

from qiskit.circuit.library import ZZFeatureMap, ZFeatureMap, PauliFeatureMap, StatePreparation

num_features = features.shape[1]

feature_map_ex =  # Use any featuremap of your choice!
feature_map_ex.decompose().draw(output="mpl", fold=20)

from qiskit.circuit.library import RealAmplitudes, EfficientSU2, ExcitationPreserving, PauliTwoDesign, TwoLocal, NLocal

ansatz_ex = # Use any ansatz of your choice!
ansatz_ex.decompose().draw(output="mpl", fold=20)

from qiskit.algorithms.optimizers import COBYLA, SPSA, L_BFGS_B, NELDER_MEAD, UMDA, TNC

optimizer_ex = # Use any optimizer of your choice!

import time
from qiskit_machine_learning.algorithms.classifiers import VQC
from qiskit.primitives import Sampler

with Session(service = service, backend = backend):
    vqc_ex = VQC(
        feature_map=feature_map_ex,
        ansatz=ansatz_ex,
        optimizer=optimizer_ex,
        callback=callback_graph,
    )

# clear objective value history
objective_func_vals = []

start = time.time()
vqc_ex.fit(train_features, train_labels)
elapsed = time.time() - start

print(f"Training time: {round(elapsed)} seconds")

train_score_q4_ex = vqc_ex.score(train_features, train_labels)
test_score_q4_ex = vqc_ex.score(test_features, test_labels)

print(f"Quantum VQC on the training dataset: {train_score_q4_ex:.2f}")
print(f"Quantum VQC on the test dataset:     {test_score_q4_ex:.2f}")

from sklearn.decomposition import PCA

features = PCA(n_components=2).fit_transform(features)

plt.rcParams["figure.figsize"] = (6, 6)
sns.scatterplot(x=features[:, 0], y=features[:, 1], hue=labels, palette="tab10")

train_features, test_features, train_labels, test_labels = train_test_split(
    features, labels, train_size=0.8, random_state=algorithm_globals.random_seed
)

svc.fit(train_features, train_labels)

train_score_c2 = svc.score(train_features, train_labels)
test_score_c2 = svc.score(test_features, test_labels)

print(f"Classical SVC on the training dataset: {train_score_c2:.2f}")
print(f"Classical SVC on the test dataset:     {test_score_c2:.2f}")

num_features = features.shape[1]

feature_map = ZZFeatureMap(feature_dimension=num_features, reps=1)
ansatz = RealAmplitudes(num_qubits=num_features, reps=3)
ansatz.decompose().draw("mpl")

from qiskit.algorithms.optimizers import COBYLA, SPSA, L_BFGS_B, NELDER_MEAD, UMDA, TNC

optimizer = COBYLA(maxiter=50)

from qiskit.primitives import Sampler

with Session(service = service, backend = backend):
    vqc = VQC(
        feature_map=feature_map,
        ansatz=ansatz,
        optimizer=optimizer,
        callback=callback_graph,
    )

# clear objective value history
objective_func_vals = []

# make the objective function plot look nicer.
plt.rcParams["figure.figsize"] = (12, 6)


start = time.time()
vqc.fit(train_features, train_labels)
elapsed = time.time() - start

print(f"Training time: {round(elapsed)} seconds")

train_score_q2_ra = vqc.score(train_features, train_labels)
test_score_q2_ra = vqc.score(test_features, test_labels)

print(f"Quantum VQC on the training dataset using RealAmplitudes: {train_score_q2_ra:.2f}")
print(f"Quantum VQC on the test dataset using RealAmplitudes:     {test_score_q2_ra:.2f}")

from qiskit.circuit.library import EfficientSU2

ansatz = EfficientSU2(num_qubits=num_features, reps=3)
optimizer = COBYLA(maxiter=100)

with Session(service = service, backend = backend):
    
    vqc = VQC(
        feature_map=feature_map,
        ansatz=ansatz,
        optimizer=optimizer,
        callback=callback_graph,
    )

# clear objective value history
objective_func_vals = []

start = time.time()
vqc.fit(train_features, train_labels)
elapsed = time.time() - start

print(f"Training time: {round(elapsed)} seconds")

train_score_q2_eff = vqc.score(train_features, train_labels)
test_score_q2_eff = vqc.score(test_features, test_labels)

print(f"Quantum VQC on the training dataset using EfficientSU2: {train_score_q2_eff:.2f}")
print(f"Quantum VQC on the test dataset using EfficientSU2:     {test_score_q2_eff:.2f}")

print(f"Model                           | Test Score | Train Score")
print(f"SVC, 4 features                 | {train_score_c4:10.2f} | {test_score_c4:10.2f}")
print(f"VQC, 4 features, RealAmplitudes | {train_score_q4:10.2f} | {test_score_q4:10.2f}")
print(f"----------------------------------------------------------")
print(f"SVC, 2 features                 | {train_score_c2:10.2f} | {test_score_c2:10.2f}")
print(f"VQC, 2 features, EfficientSU2   | {train_score_q2_eff:10.2f} | {test_score_q2_eff:10.2f}")
print(f"VQC, 2 features, RealAmplitudes | {train_score_q2_ra:10.2f} | {test_score_q2_ra:10.2f}")

import qiskit.tools.jupyter

%qiskit_version_table
%qiskit_copyright