from pycobra.cobra import Cobra
from pycobra.kernelcobra import KernelCobra
from pycobra.mixcobra import MixCobra
from pycobra.generalcobra import GeneralCobra


from pycobra.ewa import Ewa
from pycobra.diagnostics import Diagnostics
from pycobra.visualisation import Visualisation
import numpy as np


%matplotlib inline

# setting up our random data-set
rng = np.random.RandomState(1)

# D1 = train machines; D2 = create COBRA; D3 = calibrate epsilon, alpha; D4 = testing
n_features = 20
D1, D2, D3, D4 = 200, 200, 200, 200
D = D1 + D2 + D3 + D4
X = rng.uniform(-1, 1, D * n_features).reshape(D, n_features)
Y = np.power(X[:,1], 2) + np.power(X[:,3], 3) + np.exp(X[:,10]) 
# Y = np.power(X[:,0], 2) + np.power(X[:,1], 3)

# training data-set
X_train = X[:D1 + D2]
X_test = X[D1 + D2 + D3:D1 + D2 + D3 + D4]
X_eps = X[D1 + D2:D1 + D2 + D3]
# for testing
Y_train = Y[:D1 + D2]
Y_test = Y[D1 + D2 + D3:D1 + D2 + D3 + D4]
Y_eps = Y[D1 + D2:D1 + D2 + D3]

"""
Explanation: Kernel Cobra
We will be using this Jupyter notebook to test our Kernel-Cobra methods, as well as conduct experiments on Kernel-Cobra and MixCobra.
We will start, like with other examples, by setting up a standard data-set to train our machines.
End of explanation
"""


cobra = Cobra()

cobra.set_epsilon(X_eps, Y_eps, grid_points=10)

cobra.fit(X_train, Y_train)

cobra.predict(X_test)

"""
Explanation: COBRA
End of explanation
"""


kernel = KernelCobra()

kernel.fit(X_train, Y_train)

kernel.predict(X_test)

"""
Explanation: Kernel COBRA
End of explanation
"""


mixcobra = MixCobra()

mixcobra.fit(X_train, Y_train)

mixcobra.predict(X_test)

"""
Explanation: Mix COBRA
End of explanation
"""


general= GeneralCobra()

general.fit(X_train, Y_train)

general.fit(X_train, Y_train)

general.predict(X_test)

"""
Explanation: General COBRA
End of explanation
"""


from sklearn import datasets
from sklearn.metrics import accuracy_score
from sklearn.datasets.samples_generator import make_regression

diabetes = datasets.load_diabetes()

diabetes_X_train = diabetes.data[:-40]
diabetes_X_test = diabetes.data[-40:]

diabetes_y_train = diabetes.target[:-40]
diabetes_y_test = diabetes.target[-40:]

diabetes_cobra = Cobra()
diabetes_cobra.set_epsilon(diabetes_X_train, diabetes_y_train)
diabetes_cobra.fit(diabetes_X_train, diabetes_y_train)

diabetes_kernelcobra = KernelCobra()
diabetes_kernelcobra.fit(diabetes_X_train, diabetes_y_train)

diabetes_generalcobra = GeneralCobra()
diabetes_generalcobra.fit(diabetes_X_train, diabetes_y_train)

diabetes_mixcobra = MixCobra()
diabetes_mixcobra.fit(diabetes_X_train, diabetes_y_train)

boston = datasets.load_boston()

boston_X_train = boston.data[:-40]
boston_X_test = boston.data[-40:]

boston_y_train = boston.target[:-40]
boston_y_test = boston.target[-40:]

boston_cobra = Cobra()
boston_cobra.set_epsilon(boston_X_train, boston_y_train)
boston_cobra.fit(boston_X_train, boston_y_train)

boston_kernelcobra = KernelCobra()
boston_kernelcobra.fit(boston_X_train, boston_y_train)

boston_generalcobra = GeneralCobra()
boston_generalcobra.fit(boston_X_train, boston_y_train)

boston_mixcobra = MixCobra()
boston_mixcobra.fit(boston_X_train, boston_y_train)

"""
Explanation: Experiments
Points: the key difference between KernelCobra and MixCobra is that we are not including input distances while constructing the aggregate. We only use output differences. This means that in very high dimensions, the input space indformation might not be very useful, and it also means it will becoime computationally expensive. Our experiments should reflect this well.
Also: When we apply our method, since it is only on output space, a classifier version of this would decompose into the original CobraClassifier.
Datasets
End of explanation
"""


diagnostics_kernel = Diagnostics(kernel, X_test, Y_test)
diagnostics_mix = Diagnostics(mixcobra, X_test, Y_test)

boston_diagnostics_kernel = Diagnostics(boston_kernelcobra, boston_X_test, boston_y_test, load_MSE=False)
boston_diagnostics_mix = Diagnostics(boston_mixcobra, boston_X_test, boston_y_test, load_MSE=False)

diabetes_diagnostics_kernel = Diagnostics(diabetes_kernelcobra, diabetes_X_test, diabetes_y_test, load_MSE=False)
diabetes_diagnostics_mix = Diagnostics(diabetes_mixcobra, diabetes_X_test, diabetes_y_test, load_MSE=False)

(alpha_, beta_, bandwidth_mix), mix_mse = diagnostics_mix.optimal_mix(X_test, Y_test)

(alpha_boston, beta_boston, bandwidth_boston_mix), boston_mix_mse = boston_diagnostics_mix.optimal_mix(boston_X_test, boston_y_test)
(alpha_diabetes, beta_diabetes, bandwidth_diabetes_mix), diabetes_mix_mse = diabetes_diagnostics_mix.optimal_mix(diabetes_X_test, diabetes_y_test)

bandwidth_, bandwidth_mse = diagnostics_kernel.optimal_kernelbandwidth(X_test, Y_test, bandwidths=np.arange(0.05, 2.55, step=0.10))
bandwidth_boston, boston_kernel_mse = boston_diagnostics_kernel.optimal_kernelbandwidth(boston_X_test, boston_y_test,bandwidths=np.arange(0.05, 2.55, step=0.10))
bandwidth_diabetes, diabetes_kernel_mse = diabetes_diagnostics_kernel.optimal_kernelbandwidth(diabetes_X_test, diabetes_y_test,bandwidths=np.arange(0.05, 2.55, step=0.10))

"""
Explanation: Diagnostics
End of explanation
"""


diabetes_visualisation = Visualisation(diabetes_cobra, diabetes_X_test, diabetes_y_test, estimators={"Cobra":diabetes_cobra,"GeneralCoba" : diabetes_generalcobra, "KernelCobra" : diabetes_kernelcobra, "MixCobra" : diabetes_mixcobra}, alpha=alpha_diabetes, beta=beta_diabetes, bandwidth_mix = bandwidth_diabetes_mix, bandwidth_kernel=bandwidth_diabetes)

boston_visualisation = Visualisation(boston_cobra, boston_X_test, boston_y_test, estimators={"Cobra":boston_cobra, "GeneralCoba" : boston_generalcobra, "KernelCobra" : boston_kernelcobra, "MixCobra" : boston_mixcobra}, alpha=alpha_boston, beta=beta_boston, bandwidth_mix = bandwidth_boston_mix, bandwidth_kernel=bandwidth_boston)

visualisation = Visualisation(cobra, X_test, Y_test, estimators={"Cobra":cobra, "GeneralCobra" : general, "KernelCobra" : kernel, "MixCobra" : mixcobra}, alpha=alpha_, beta=beta_, bandwidth_mix = bandwidth_mix, bandwidth_kernel=bandwidth_)

visualisation_machines = Visualisation(cobra, X_test, Y_test)

visualisation.QQ("KernelCobra")

visualisation.QQ("MixCobra")

visualisation.QQ("Cobra")

visualisation.QQ("GeneralCobra")

diabetes_visualisation.machine_MSE

boston_visualisation.machine_MSE

visualisation.machine_MSE

visualisation_machines.machine_MSE

visualisation.boxplot()

visualisation_machines.boxplot()

diabetes_visualisation.boxplot()

boston_visualisation.boxplot()

"""
Explanation: Visualisation
End of explanation
"""


import time

# setting up our random data-set
rng = np.random.RandomState(1)

# D1 = train machines; D2 = create COBRA; D3 = calibrate epsilon, alpha; D4 = testing
n_features = [5, 10, 20, 50, 100, 1000, 5000, 10000]
cobra_times = []
mix_times = []
kernel_times = []


for features in n_features:
    D1, D2, D3, D4 = 200, 200, 200, 200
    D = D1 + D2 + D3 + D4
    X = rng.uniform(-1, 1, D * features).reshape(D, features)
    Y = np.power(X[:,1], 2) + np.power(X[:,3], 3) + np.exp(X[:,4]) 
    # Y = np.power(X[:,0], 2) + np.power(X[:,1], 3)

    # training data-set
    X_train = X[:D1 + D2]
    X_test = X[D1 + D2 + D3:D1 + D2 + D3 + D4]
    X_eps = X[D1 + D2:D1 + D2 + D3]
    # for testing
    Y_train = Y[:D1 + D2]
    Y_test = Y[D1 + D2 + D3:D1 + D2 + D3 + D4]
    Y_eps = Y[D1 + D2:D1 + D2 + D3]
    
    cobra = Cobra().fit(X_train, Y_train)
    start = time.time()
    cobra.predict(X_test)
    print("Cobra", features, time.time() - start)
    cobra_times.append(time.time() - start)
    
    kernel = KernelCobra().fit(X_train, Y_train)
    start = time.time()
    kernel.predict(X_test)
    print("Kernel", features, time.time() - start)
    kernel_times.append(time.time() - start)

    mix = MixCobra().fit(X_train, Y_train)
    start = time.time()
    mix.predict(X_test)
    print("Mix", features, time.time() - start)
    mix_times.append(time.time() - start)


import matplotlib.pyplot as plt


plt.plot(n_features, cobra_times, label="Cobra")
plt.plot(n_features, mix_times, label="Mix")
plt.plot(n_features, kernel_times, label="Kernel")
plt.xlabel("No of features")
plt.ylabel("Time in seconds")
plt.legend()

"""
Explanation: Timing
End of explanation
"""


