#Imports
import random

def white_elephant(): #Exercise 12 ###############################    FAILED
    #white elephant accepts no arguments
    #it will contain people from many departments
    #and they will all give gifts to other
    #people in other apartments
    #SECRET SANTA

    #Create a try block
    try:

        #Create a list with everyone's name
        people = ['Julia','Oliver','Abigail','Camden','Kayleigh','Cooper','Kerrigan'
                  ,'Avergy','Charlotte','Elle']

        #Create two more lists for those who've given gifts and those who've been given gifts
        given = []
        received = []

        #Create another list to never have two peple give gifts to each other(unless it works)
        past_index = [0,1,2,3,4,5,6,7,8,9]

        #Create three booleans
        cont = False
        done = False
        keep_going = False

        #Add accumulators to know how many people from each department has given gifts
        DD = 0
        HRD = 0
        SD = 0

        #Message before the loops
        print('Here are the results:')

        #Create a loop for everything
        while not done:

            #Create a loop to find gifters
            while not cont:

                #Reset keep_going to True everytime
                keep_going = True

                #Find the person who will give a gift
                giving_index = random.randint(0,9)
                gifter = people[giving_index]

                #Check to see if that person has given yet
                #If so, loop again
                if gifter in given:
                    cont = False
                #If not, add them to the list and stop looping
                else:
                    cont = True
                    given.append(gifter)

            #Reset the boolean
            cont = False
            print(giving_index)
            #Check and see if the department chosen has already had two gifters
            if DD == 2 and giving_index <= 2:
                keep_going = False
            elif SD == 2 and giving_index >= 7:
                keep_going = False
            elif HRD == 2 and giving_index >=3 and giving_index <= 6:
                keep_going = False

            #If all of the departments had had 2 people already, then continue
            if DD >= 2 and SD >= 2 and HRD >= 2:
                keep_going = True

            #Run these if statements IF AND ONLY IF keep_going is True
            if keep_going:

                #Check to see which department that person came from and choose a random number from a different department
                if giving_index <= 2: #If they are in DD

                    #Add 1 to the Development Department
                    DD += 1

                    #Create a while loop to find receivers
                    while not cont:

                        #Find a random number between the other two departments
                        index = random.randint(3,9)
                        receiver = people[index]
                        print(index)
                        #Check to see if that person has recieved yet
                        #If so, loop again
                        if receiver in received:
                            cont = False
                        elif past_index[giving_index] == index:
                            cont = False
                        #If not, add them to the list and stop looping
                        else:
                            cont = True
                            received.append(receiver)

                    #Reset the boolean variable
                    cont = False

                    #put the giver's number into the receiver's spot in the past index list
                    past_index[index] = giving_index

                #Check to see which department that person came from and choose a random number from a different department
                elif giving_index >= 3 and giving_index <= 6: #If they are in HRD

                    #Add to the HR Department
                    HRD += 1

                    #Create a while loop to find receivers
                    while not cont:

                        #Find a random number between the other two departments
                        index = random.randint(0,9)
                        receiver = people[index]
                        print(index)
                        #Check to see if that person has recieved yet
                        #If so, loop again
                        if receiver in received or index >= 3 and index <= 6:
                            cont = False
                        elif past_index[giving_index] == index:
                            cont = False
                        #If not, add them to the list and stop looping
                        else:
                            cont = True
                            received.append(receiver)

                    #Reset the boolean variable
                    cont = False

                    #put the giver's number into the receiver's spot in the past index list
                    past_index[index] = giving_index

                #Check to see which department that person came from and choose a random number from a different department
                elif giving_index >= 7: #If they are in SD

                    #Add 1 to the Sales Department
                    SD += 1

                    #Create a while loop to find receivers
                    while not cont:

                        #Find a random number between the other two departments
                        index = random.randint(0,6)
                        receiver = people[index]
                        print(index)
                        #Check to see if that person has recieved yet
                        #If so, loop again
                        if receiver in received:
                            cont = False
                        elif past_index[giving_index] == index:
                            cont = False
                        #If not, add them to the list and stop looping
                        else:
                            cont = True
                            received.append(receiver)

                    #Reset the boolean variable
                    cont = False

                    #put the giver's number into the receiver's spot in the past index list
                    past_index[index] = giving_index

                #Print out the message at the end of everything
                print(gifter, 'gifts to', receiver)

            #Check to see if everyone has been used
            if len(people) == len(given) == len(received):
                done = True
            else:
                done = False
        print(given, '\n', received)
    except ValueError as err:
        print(err)

#=======#

#=======#

def we2(): #For Exercise 12 ###############################      FAILED
    #white elephant accepts no arguments
    #it will basically create a secret santa list
    #for everyone within three companies

    #Create a try block
    try:

        #Create a list for all the people in their respective companies
        DD = ['Julia','Oliver','Abigail']
        HRD = ['Camden','Kayleigh','Cooper','Kerrigan']
        SD = ['Avery','Charlotte','Elle']

        #Combine the lists
        people = DD + HRD + SD

        #Mix the list
        random.shuffle(people)

        #Create boolean variables
        cont = False
        keep_going2 = True

        #Accumulators
        index1 = 0
        DDc = 0
        HRDc = 0
        SDc = 0

        #Loop eveything
        while keep_going2:

            #Loop to find persons
            while not cont:

                #Pair each person in the list with the person to their right
                gifter = people[index1]
                index1 += 1
                i = people[index1]
                if i in DD and gifter in DD or i in HRD and gifter in HRD or i in SD and gifter in SD:
                    cont = False
                    index1 -= 1
                    random.shuffle(people)
                else:
                    cont = True
                    receiver = i

            #Print that person and their receiver
            print(gifter, 'gifts to', receiver)

            #Check to see if any company is done
            if gifter in DD:
                DDc += 1
            elif gifter in HRD:
                HRDc += 1
            elif gifter in SD:
                SDc += 1

            #Remove people if necessary
            if DDc >= 3:
                people.remove('Julia')
                people.remove('Oliver')
                people.remove('Abigail')
            elif HRDc >= 3:
                people.remove('Camden')
                people.remove('Kayleigh')
                people.remove('Coooper')
                people.remove('Kerrigan')
            elif SDc >= 3:
                people.remove('Avery')
                people.remove('Charlotte')
                people.remove('Elle')

            if people == []:
                keep_going2 = False

    except NameError as err:
        print(err)

#============#

def we(): #For Exercise 12 ################################   FAILED
    #white elephant accepts no arugments
    #it will play secret santer

    #Create lists for all the names in their own companies
    DD = ['Julia','Oliver','Abigail']
    HRD = ['Camden','Kayleigh','Cooper','Kerrigan']
    SD = ['Avery','Charlotte','Elle']

    #Create a list that has everyone
    people = DD + HRD + SD
    #Shuffle all of the lists
    random.shuffle(DD)
    random.shuffle(HRD)
    random.shuffle(SD)

    #Variable
    index = 0

    #Apply names in DD to names in HRD or SD
    gifter = DD[index]
    num = random.randint(3,9)
    receiver = people[index]

    #Create a list that

#=====================#

def white(): #For Exercise 12
    #secret santa

    #Create lists for all the names in their own companies
    DD = ['Julia','Oliver','Abigail']
    HRD = ['Camden','Kayleigh','Cooper','Kerrigan']
    SD = ['Avery','Charlotte','Elle']

    #Create a list that has everyone
    people = DD + HRD + SD

    #Shuffle all of the lists
    random.shuffle(people)

    print('Here are the results:')

    #Apply givers to receivers
    gifter = people[3]
    receiver = people[9]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[9]
    receiver = people[1]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[1]
    receiver = people[6]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[6]
    receiver = people[0]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[0]
    receiver = people[8]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[8]
    receiver = people[3]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[7]
    receiver = people[5]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[5]
    receiver = people[4]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[4]
    receiver = people[2]

    #print
    print(gifter, 'gifts to', receiver)

    #Apply givers to receivers
    gifter = people[2]
    receiver = people[7]

    #print
    print(gifter, 'gifts to', receiver)

def white_elephant(): #THIS IS MISSING THE PART WHERE IT CHECKS TO SEE IF IndexR IS IN THE SAME COMPANY AS IndexG VICE VERSA
    #white elephant accepts no arguments
    #it will pair people from different companies that will give gifts to each other
    #this is basically a program for making a Secret Santa list

    #Create a try block
    try:

        #Create a list for everyone and their respective companies
        DD = ['Julia','Oliver','Abigail']
        HRD = ['Camden','Kayleigh','Cooper','Kerrigan']
        SD = ['Avery','Charlotte','Elle']

        people = DD + HRD + SD #Remove people from this list to signify that they have gone

        persons = [] #Main list to pull names

        Receivers = [] #Remove people from this list to signify that they've received a gift

        #Copy the names from people into persons
        for name in people:
            persons.append(name)

        #Copy the names from people into Receivers
        for name in people:
            Receivers.append(name)

        #Create a while loop for the entire process
        for thing in range(1,len(persons)+1):

            #Create a boolean variable
            cont = False

            #Create a while loop for the gifters
            while not cont:
                #Create a try block
                try:
                    #Get the random index for gifters
                    indexG = random.randint(0,len(persons))

                    #Check to see if the gifter has already given a gift
                    if persons[indexG] in people: #If they haven't
                        p = 6+5
                    else: #If they have
                        e = v + i
                #Loop again if gifter has already given a gift
                except:
                    cont = False
                #Break the loop if the gifter has yet to give a gift and remove them from the people list
                else:
                    cont = True
                    people.remove((persons[indexG]))

            #Assign the gifter to gifter
            gifter = persons[indexG]

            #Reset the boolean variable
            cont = False

            #Create a while loop for the receivers
            while not cont:
                #Create a try block
                try:

                    #Create an index for the receivers
                    indexR = random.randint(0,len(persons))

                    #Check to see if the receiver has already received a gift AND if their name matches the gifter
                    if persons[indexR] in Receivers and persons[indexG] != persons[indexR]: #If both are True
                        o = 1+1
                    else: #If they both are False
                        o = b + u

                    #Check to see which department the gifter is from. Then use a for loop
                    #to check if the gifter and the receiver are in the same department.
                    if indexG <= 2: #Development Department
                        for name in DD:
                            if persons[indexR] == name:
                                o = b + e
                            else:
                                o = 1+1
                    elif indexG >= 3 and indexG <= 6: #HR Department
                        for name in HRD:
                            if persons[indexR] == name:
                                o = b + u
                            else:
                                o = 1+1
                    elif indexG >= 7: #Sales Department
                        for name in SD:
                            if persons[indexR] == name:
                                o = b+9
                            else:
                                o=1+1
                #If there is a crash anywhere, then loop again
                except:
                    cont = False
                #If no crash, break the loop and remove the receiver from the Receivers list
                else:
                    cont = True
                    Receivers.remove((persons[indexR]))

            #Assign the receiver to receiver
            receiver = persons[indexR]

            #Print the results and loop again
            print(gifter, 'gifts to', receiver)

    #Except messages
    except NameError:
        print('There are one or more variables that have a mispelled name')
    except:
        print('There was an issue within the program')

white_elephant()


import matplotlib.pyplot as plt
from matplotlib import style
import numpy as np
import pandas as pd
from sklearn.metrics import confusion_matrix,classification_report
from sklearn.metrics import accuracy_score

style.use('ggplot')

class SVM:
    def __init__(self, plot_graph=True):
        self.plot_graph = plot_graph
        self.colors = {1: 'r', -1: 'b'}
        if self.plot_graph:
            self.fig = plt.figure()
            self.ax = self.fig.add_subplot(1, 1, 1)

    # train
    def fit(self, training_data):
        self.train_data = training_data
        # { ||w||: [w,b] }
        optimum_values = {}
        # Assuming all the possible transforms for the 2d data (To find which quadrant the data lies)
        transforms = [[1, 1],
                      [-1,1],
                      [-1,-1],
                      [1,-1]]

        feature_set = []
        for yi in self.train_data:

            for featureset in self.train_data[yi]:

                for feature in featureset:
                    feature_set.append(feature)

        self.max_feature_value = max(feature_set)
        self.min_feature_value = min(feature_set)
        feature_set = None

        #Asssuming step sizes based on Max Feature value;
        step_sizes = [self.max_feature_value * 0.1,
                      self.max_feature_value * 0.01,]

        b_range_multiple = 1
        b_multiple = 3
        w_optimum = self.max_feature_value * 10

        for step in step_sizes:
            w = np.array([w_optimum, w_optimum])

            optimized = False
            while not optimized:
                #Iterating for all combinations of b
                for b in np.arange(-1 * (self.max_feature_value * b_range_multiple),
                                   self.max_feature_value * b_range_multiple,
                                   step * b_multiple):

                    for transformation in transforms:
                        # Multiply with assumed value of w with each transform
                        w_t = w * transformation
                        found_option = True

                        for i in self.train_data:
                            for xi in self.train_data[i]:
                                yi = i

                                if yi * (np.dot(w_t, xi) + b) < 1:
                                    # If any feature value's dot product is less than 1, then ignore that weight matrix
                                    found_option = False
                                break

                        if found_option:
                            optimum_values[np.linalg.norm(w_t)] = [w_t, b]
                        break

                if w[0] < 0:
                    optimized = True
                    print('Optimized a step.')
                else:
                    w = w - step

            norms = sorted([n for n in optimum_values])
            # ||w|| : [w,b]
            optimum = optimum_values[norms[0]]

            self.w = optimum[0]
            self.b = optimum[1]

            w_optimum = optimum[0][0] + step * 2

    def predict(self, testing_data):
        # sign( x.w+b )
        results = np.sign(np.dot(np.array(testing_data), self.w) + self.b)
        if results != 0 and self.plot_graph:
            self.ax.scatter(testing_data[0], testing_data[1], s=200, marker='*', c=self.colors[results])
        return results

    def visualize(self):

        def hyperplane(x, w, b, v):
            return (-w[0] * x - b + v) / w[1]

        data_range = (self.min_feature_value * 0.9, self.max_feature_value * 1.1)
        hyp_x_min = data_range[0]
        hyp_x_max = data_range[1]

        # (w.x+b) = 1
        psv1 = hyperplane(hyp_x_min, self.w, self.b, 1)
        psv2 = hyperplane(hyp_x_max, self.w, self.b, 1)
        self.ax.plot([hyp_x_min, hyp_x_max], [psv1, psv2], 'k')

        # (w.x+b) = -1
        nsv1 = hyperplane(hyp_x_min, self.w, self.b, -1)
        nsv2 = hyperplane(hyp_x_max, self.w, self.b, -1)
        self.ax.plot([hyp_x_min, hyp_x_max], [nsv1, nsv2], 'k')

        # (w.x+b) = 0
        db1 = hyperplane(hyp_x_min, self.w, self.b, 0)
        db2 = hyperplane(hyp_x_max, self.w, self.b, 0)
        self.ax.plot([hyp_x_min, hyp_x_max], [db1, db2], 'y--')

        plt.show()

header = ['Labels','Feature_0','Product_Name','Feature_1', 'Feature_2']
data_read = pd.read_csv(r"train_dataset.csv",sep=',', usecols=header)
data_read1=data_read[['Labels','Feature_0','Product_Name','Feature_1', 'Feature_2']]

my_dict={}
list_tp=[]
list_tn=[]

for index,row in data_read1.iterrows():
    if row['Labels']==1:
        list_tp.append(row[3:].tolist())
    else:
        list_tn.append(row[3:].tolist())

my_dict[1]=np.asarray(list_tp)
my_dict[-1]=np.asarray(list_tn)

svm = SVM()
svm.fit(training_data=my_dict)

header = ['Labels','Feature_0','Product_Name','Feature_1', 'Feature_2']
data_read2 = pd.read_csv(r"test_dataset.csv",sep=',', usecols=header)
data_read3=data_read2[['Labels','Feature_0','Product_Name','Feature_1', 'Feature_2']]

list_test_features = []
label = data_read3['Labels']
list_test_labels = []

for index,row in data_read3.iterrows():
        list_test_features.append(row[3:].tolist())

for x in label:
        list_test_labels.append(x)

predict_us = list_test_features
pred = []

for p in predict_us:
    pred.append(svm.predict(p))

svm.visualize()

results = confusion_matrix(list_test_labels, pred, labels=[1, -1])
print('Confusion Matrix:')
print(results)

print(classification_report(list_test_labels, pred))
print('Accuracy Score :', accuracy_score(list_test_labels, pred))

products_list = data_read3['Product_Name']
products = []
for x in products_list:
        products.append(x)

complete_list = []
def perf_measure(products,y_actual, y_pred):

    for i in range(len(y_pred)):
        TP = 0
        FP = 0
        TN = 0
        FN = 0
        if y_actual[i]==y_pred[i]==1:
           TP += 1
        if y_pred[i]==1 and y_actual[i]!=y_pred[i]:
           FP += 1
        if y_actual[i]==y_pred[i]==-1:
           TN += 1
        if y_pred[i]==-1 and y_actual[i]!=y_pred[i]:
           FN += 1

        complete_list.append([products[i],y_actual[i],y_pred[i],TP, FP, TN, FN])

perf_measure(products, list_test_labels, pred)
header = ['Products_Name','Actual','Predicted','TP','FP','TN','FN']
dfObj = pd.DataFrame(complete_list,columns=header)
dfObj.to_csv("Output_SVM.csv",index=False)


from unittest.mock import patch
import mymodule

@patch('anothermodule.Spam')
@patch('mymodule.ClassName2')
@patch('mymodule.ClassName1')
def test(MockClass1, MockClass2, MockSpam):
    mymodule.ClassName1()
    mymodule.ClassName2()
    mymodule.create_classes()
    assert MockClass1 is mymodule.ClassName1
    assert MockClass2 is mymodule.ClassName2
    assert MockClass1.called
    assert MockClass2.called

test()


#!/usr/bin/env python3
from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import uuid

from sklearn.svm import SVC
from sklearn.cluster import SpectralClustering
from sklearn.metrics import normalized_mutual_info_score

from qiskit import BasicAer
from qiskit.circuit.library import ZFeatureMap, ZZFeatureMap
from qiskit.utils import QuantumInstance, algorithm_globals
from qiskit_machine_learning.algorithms import QSVC
from qiskit_machine_learning.kernels import QuantumKernel
from qiskit import QuantumCircuit
from qiskit import Aer, transpile
from qiskit.providers.aer.backends import StatevectorSimulator

seed = 42
algorithm_globals.random_seed = seed

print(f'Start run at {datetime.now()}')

datasets_dir = './datasets/'

training_data = '100_stars_dataset_clean_SVM.csv'
testing_data = '100_stars_validation_dataset_SVM.csv'

print(f'Training data file: {training_data}')
print(f'Testing data file: {testing_data}')

### DATA PREP SECTION
usecols = ["bin0","bin1","bin2","bin3","bin4","bin5", "bin6","bin7","bin8","bin9","bin10","bin11","bin12","bin13","bin14","bin15", "bin16","bin17","bin18","bin19","bin20","bin21","bin22","bin23","bin24","label"]
df = pd.read_csv(datasets_dir+training_data, usecols=usecols)
df_val = pd.read_csv(datasets_dir+testing_data, usecols=usecols)

x = df[["bin0","bin1","bin2","bin3","bin4","bin5", "bin6","bin7","bin8","bin9","bin10","bin11","bin12","bin13","bin14","bin15", "bin16","bin17","bin18","bin19","bin20","bin21","bin22","bin23","bin24"]]
x = x.to_numpy().astype(np.float32)
print(f'Training data shape: {x.shape}')

y = df[["label"]]
y = y.to_numpy().astype(np.float32)
print(f'Training data label shape: {y.shape}')

x_val = df_val[["bin0","bin1","bin2","bin3","bin4","bin5", "bin6","bin7","bin8","bin9","bin10","bin11","bin12","bin13","bin14","bin15", "bin16","bin17","bin18","bin19","bin20","bin21","bin22","bin23","bin24"]]
x_val = x_val.to_numpy().astype(np.float32)
print(f'Testing data shape: {x_val.shape}')

y_val = df_val[["label"]]
y_val = y_val.to_numpy().astype(np.float32)
print(f'Testing data label shape: {y_val.shape}')

# FUNCTIONS

def precompute_quantum_kernel(adhoc_dimension, feature_map='ZFeatureMap', reps=1, entanglement=None):

    # CREATE FEAUTURE MAP
    if feature_map == 'ZFeatureMap':
        adhoc_feature_map = ZFeatureMap(feature_dimension=adhoc_dimension, reps=reps)
    elif feature_map == 'ZZFeatureMap':
        adhoc_feature_map = ZZFeatureMap(feature_dimension=adhoc_dimension, reps=reps, entanglement=entanglement)
    else:
        print(f'Unknown feature map {feature_map}')
        return None, None
    quantum_simulator = StatevectorSimulator(device='GPU', precision='single')

    adhoc_backend = QuantumInstance(
        quantum_simulator, seed_simulator=seed, seed_transpiler=seed
    )

    adhoc_kernel = QuantumKernel(feature_map=adhoc_feature_map, quantum_instance=adhoc_backend)

    ### PRECOMPUTE QUANTUM KERNEL
    start_prec_k = datetime.now()
    print(f'Start precomputing kernel at {start_prec_k}')

    adhoc_matrix_train = adhoc_kernel.evaluate(x_vec=x)

    end_prec_k = datetime.now()
    print(f'End precomputing kernel at {end_prec_k}')
    print(f'Duration: {end_prec_k - start_prec_k}')

    return adhoc_matrix_train, adhoc_kernel

def fit_quantum_kernel(adhoc_matrix_train, y):
    ### QSVM FIT
    start_qsvm_fit = datetime.now()
    print(f'Start QSVM fit at {start_qsvm_fit}')

    adhoc_svc = SVC(kernel="precomputed")
    adhoc_svc.fit(adhoc_matrix_train, y.ravel())

    end_qsvm_fit = datetime.now()
    print(f'End QSVM fit at {end_qsvm_fit}')
    print(f'Duration: {end_qsvm_fit - start_qsvm_fit}')

    return adhoc_svc

def train_score_calc(adhoc_svc, adhoc_matrix_train, y):
### TRAIN SCORE
    start_qsvm_train_score = datetime.now()
    print(f'Start train score at {start_qsvm_train_score}')

    adhoc_score_train = adhoc_svc.score(adhoc_matrix_train, y.ravel())
    print(f"Precomputed kernel classification train score: {adhoc_score_train}")

    end_qsvm_train_score = datetime.now()
    print(f'End train score at {end_qsvm_train_score}')
    print(f'Duration: {end_qsvm_train_score - start_qsvm_train_score}')

    return adhoc_score_train

def test_score_calc(adhoc_svc, adhoc_kernel, x_val, y_val):
    ### TEST SCORE
    start_qsvm_test_score = datetime.now()
    print(f'Start test score at {start_qsvm_test_score}')

    test_scores = []
    test_iterator = range(0, x_val.shape[0], x.shape[0])
    test_iterations = len(test_iterator)
    iteration = 0
    for idx in range(0, x_val.shape[0], x.shape[0]):
        #print(x_val[idx:idx+x.shape[0],:].shape)
        iteration += 1
        start_time = datetime.now()
        print(f'Iteration {iteration}/{test_iterations} starts at {start_time}')
        adhoc_matrix_test = adhoc_kernel.evaluate(x_vec=x_val[idx:idx+x.shape[0],:], y_vec=x)
        adhoc_score = adhoc_svc.score(adhoc_matrix_test, y_val[idx:idx+x.shape[0],:].ravel())
        end_time = datetime.now()
        print(f'Iteration {iteration}/{test_iterations} ends at {end_time} - Duration {end_time - start_time}')
        test_scores.append(adhoc_score)

    test_score = sum(test_scores) / len(test_scores)

    print(f"Precomputed kernel classification test score: {test_score}")

    end_qsvm_test_score = datetime.now()
    print(f'End test score at {end_qsvm_test_score}')
    print(f'Duration: {end_qsvm_test_score - start_qsvm_test_score}')

    return test_score

### TESTING WITH DIFFERENT FEATUREMAP OPTIONS
adhoc_dimension = x.shape[1] # corresponds to number of qubits needed
feature_maps_list = ['ZFeatureMap', 'ZZFeatureMap']
reps_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
entanglements_list = ['full', 'linear', 'circular', 'sca']

for feature_map in feature_maps_list:
    for reps in reps_list:
        if feature_map == 'ZFeatureMap':
            print(60*'-')
            print(f'START ({datetime.now()}): {feature_map}, {reps}, NONE')
            print(60*'-')

            adhoc_matrix_train, adhoc_kernel = precompute_quantum_kernel(adhoc_dimension=adhoc_dimension, feature_map=feature_map, reps=reps)
            adhoc_svc = fit_quantum_kernel(adhoc_matrix_train=adhoc_matrix_train, y=y)
            adhoc_score_train = train_score_calc(adhoc_svc=adhoc_svc, adhoc_matrix_train=adhoc_matrix_train, y=y)
            test_score = test_score_calc(adhoc_svc=adhoc_svc, adhoc_kernel=adhoc_kernel, x_val=x_val, y_val=y_val)

            print(60*'-')
            print(f'RESULT: {feature_map}, {reps}, NONE, {adhoc_score_train}, {test_score}')
            print(60*'-')
            kernel_path = 'saved_kernels/' + f'{feature_map}-{reps}-NONE-{str(uuid.uuid4())}'
            print(f'Saving precomputed kernel at: {kernel_path}')
            with open(kernel_path, 'wb') as f:
                np.save(f, adhoc_matrix_train)

        elif feature_map == 'ZZFeatureMap':
            for entanglement in entanglements_list:
                print(60*'-')
                print(f'START ({datetime.now()}): {feature_map}, {reps}, {entanglement}')
                print(60*'-')

                adhoc_matrix_train, adhoc_kernel = precompute_quantum_kernel(adhoc_dimension=adhoc_dimension, feature_map=feature_map, reps=reps, entanglement=entanglement)
                adhoc_svc = fit_quantum_kernel(adhoc_matrix_train=adhoc_matrix_train, y=y)
                adhoc_score_train = train_score_calc(adhoc_svc=adhoc_svc, adhoc_matrix_train=adhoc_matrix_train, y=y)
                test_score = test_score_calc(adhoc_svc=adhoc_svc, adhoc_kernel=adhoc_kernel, x_val=x_val, y_val=y_val)

                print(60*'-')
                print(f'RESULT: {feature_map}, {reps}, {entanglement}, {adhoc_score_train}, {test_score}')
                print(60*'-')
                kernel_path = 'saved_kernels/' + f'{feature_map}-{reps}-{entanglement}-{str(uuid.uuid4())}'
                print(f'Saving precomputed kernel at: {kernel_path}')
                with open(kernel_path, 'wb') as f:
                    np.save(f, adhoc_matrix_train)
        else:
            print(f'Unknown feature map {feature_map}')

print(f'End run at {datetime.now()}')