import pandas as pd
import numpy as np
from keras.models import Sequential
from keras.layers import Dense,Dropout
from keras import regularizers

#------------------------------
# utility functions
#------------------------------
def whiten(x):
    o = 0
    if x.shape[0]>x.shape[1]:
        o = input('Do you really want me to compute a '+str(x.shape[0])+'x'+str(x.shape[0])+' covariance matrix (0) or should I transpose the data(1)?')
    if o == 1:
        x=x.T
    # covariance matrix
    sigma = np.dot(x,x.T)/x.shape[1]
    # singlular value decomposition
    U,S,V = np.linalg.svd(sigma)
    diagonal = np.diag(np.sqrt(S))
    # get matrix for whitening
    sigma2 = np.dot( np.dot(U,diagonal),np.linalg.inv(U))
    W1 = np.linalg.inv(sigma2)
    # return whitened data
    return W1,np.dot(W1,x)

def onehot(X):
    X_1hot = np.zeros((len(X),np.nanmax(X)+1))
    for k in range(len(X)):
        X_1hot[k,X[k]] = 1
    return X_1hot
    
def scale_01(x):
    x-=np.nanmin(x)
    return x/(.0001+np.nanmax(x))

#------------------------------
# load an preprocess data
#------------------------------
print('loading data...')
# The competition datafiles are in the directory ../input
# Read competition data files:
train = pd.read_csv("../input/train.csv")
test  = pd.read_csv("../input/test.csv")
# Write to the log:
print("Training set has {0[0]} rows and {0[1]} columns".format(train.shape))
print("Test set has {0[0]} rows and {0[1]} columns".format(test.shape))
# Any files you write to the current directory get shown as outputs
# get data into numpy form
labels = train.values[:,0]
labels = onehot(labels)
train_data = train.values[:,1:]
train_data = np.float32(train_data)
print('whitening data...')
# scale data from 0-1
train_data = scale_01(train_data)
#whiten data
#train_data-=np.nanmean(train_data,axis=0)
#W,train_data = whiten(train_data.T)
#train_data = train_data.T
print('check if data is in correct shape:',train_data.shape)
#-------------------------------
# setup and train neural network
#-------------------------------
print('setting up neural network...')
# 1: Params
H1 = 256
H2 = 257
H3 = 64
H4 = 65
H5 = 36
loss_func = 'binary_crossentropy'
epochs = 20
batch_size = 55

model = Sequential()
model.add(Dropout(.1,input_shape=(train_data.shape[1],)))
# layer 1: compress data
model.add(Dense(H1, activation='elu'))
# layer 2: map to higher dimensional space to disentangle links -> "kernel trick layer"
model.add(Dense(H2, activation='elu'))#,activity_regularizer=regularizers.activity_l1(.001)))
# layer 3: compress data
model.add(Dense(H3, activation='elu'))
# layer 4: map to higher dimensional space to disentangle links -> "kernel trick layer"
model.add(Dense(H4, activation='elu'))#,activity_regularizer=regularizers.activity_l1(.001)))
# layer 5: compress data
model.add(Dense(H5, activation='elu'))
model.add(Dense(labels.shape[1], kernel_initializer='zero', activation='sigmoid'))
model.compile(loss=loss_func, optimizer='adam', metrics=['accuracy'])

weight_file = 'weights_H1_'+str(H1)+'_H2_'+str(H2)+'_H3_'+str(H3)+'_H4_'+str(H4)+'_H5_'+str(H5)+'_loss_'+loss_func+'_epochs_'+str(epochs)+'.h5'
try:
    model.load_weights(weight_file)
    print('found previous network model! Starting further training...')
    model.fit(train_data[::2,:], labels[::2,:], nb_epoch=epochs,validation_split = .3, batch_size=batch_size,verbose=False)
    model.save_weights(weight_file,overwrite=True)
except IOError:
    print('could not find previous network model! fitting new one...')
    model.fit(train_data[::2,:], labels[::2,:], nb_epoch=epochs, batch_size=batch_size,verbose=False)
    model.save_weights(weight_file,overwrite=True)

scores = model.evaluate(train_data[1::2,:], labels[1::2,:])
print("%s: %.4f%%" % (model.metrics_names[1], scores[1]))