#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jun 19 10:27:05 2019

@author: Kai Sandbrink

"""

from sklearn.metrics import r2_score, mean_squared_error
from sklearn.model_selection import train_test_split
from scipy.optimize import minimize
import numpy as np
import pandas as pd
import pickle, os, argparse
import multiprocessing as mp
import h5py
from sklearn.metrics import roc_auc_score
from sklearn.preprocessing import label_binarize
from sklearn.svm import LinearSVC
from sklearn.multiclass import OneVsRestClassifier
from sklearn.linear_model import LinearRegression, Ridge
import time
import resource
import os

# GLOBAL PARS
t_stride = 2
ntime=320
metrics = ['RMSE', 'r2', 'PCC']
nmetrics = len(metrics)
#datafolder = 'data/'

# %% UTILITY FUNCTIONS

def compute_metrics(y, pred):
    """ Computes RMSE, R2, and correlation performance metrics given true and predicted values

    Parameters
    ----------
    y : np.array of floats, unit activations
    pred : np.array of floats, corresponding predicted values

    Returns
    -------
    [rmse, r2, cor] : prediction metrics
    """
    try:
        rmse = np.sqrt(mean_squared_error(y.flatten(), pred.flatten()))
    except:
        print("MSE Error")
        print(pred)
        print(y)
    r2 = r2_score(y.flatten(), pred.flatten())
    cor = np.corrcoef(y.flatten(), pred.flatten())[0,1]

    #print(r2)
    assert r2 <=1 or np.isnan(r2), 'illegal r2 score!!!! %s %s %s' %(r2, y[:10], pred[:10])

    return [rmse, r2, cor]

def compute_dist_metric(y, pred):
    """ Computes RMSE, R2, and correlation performance metrics given true and predicted values

    Parameters
    ----------
    y : np.array of floats [nr_samples, n_features], unit activations
    pred : np.array of floats [nr_samples, n_features], corresponding predicted values

    Returns
    -------
    dist: int, mean distance between y and pred
    """

    dist = np.linalg.norm(y - pred, axis=1)
    mean_dist = dist.mean(axis=0)
    return mean_dist

def lstring(ilayer):
    ''' Get the formatted string depending on layer

    Arguments
    ---------
    ilayer : int, layer idx (-1 is spindles)

    Returns
    -------
    layer : str
    '''
    if ilayer==-1:
        layer='data'
    else:
        layer = 'l%d' %ilayer
    return layer

def get_binidx(theta, nbins=8):
    binidx = int(( theta + np.pi ) / (2*np.pi/nbins))
    return binidx

def get_centers(fmapntime, ilayer, model, ntime = 320):
    """ Computes the temporal centers of the convolutional centers

    Parameters
    ----------
    fmapntime : int, length of temporal axis in given feature map
    ilayer : int, current layer of network, spindles = -1
    model : dict -> Config, information of model
    ntime : int, starting width

    Returns
    -------
    centers : list of ints, indices of temporal centers
    """

    #s_stride = model['s_stride']
    t_stride = model['t_stride']
    mtype = model['type']

    centers = np.arange(ntime)

    #if spindles skip
    #if(ilayer == -1):
    #    ilayer = 0

    for i in np.arange(0, ilayer + 1): #excludes spindles
        if(mtype == 'S' and i >= 4):
            centers = np.array([centers[i*t_stride] for i in range(int(np.ceil(len(centers)/t_stride)))])
        elif(mtype == 'ST'):
            centers = np.array([centers[i*t_stride] for i in range(int(np.ceil(len(centers)/t_stride)))])

    assert len(centers) == fmapntime, "Time dimensions mismatch!!!! centers: %d fmap: %d" %(len(centers), fmapntime)
    return centers

def read_layer_reps(ilayer, runinfo, model):
    '''Retrieve generated representations for the queried layer, adapted from Pranav 2020-12-01.

    Parameters
    ----------
    ilayer : int, index of current layer, spindles = -1
    runinfo : RunInfo, contains run configuration
    model : dict, contains model configuration

    Returns
    -------
    layer : array of floats [n_samples, n_features, n_time] or [n_samples, n_features, n_time, n_channels],
        storing current layer activation
    '''

    layer = lstring(ilayer)

    if ilayer == -1:
        lo = pickle.load(open(os.path.join(runinfo.datafolder(model), layer + '.pkl'), 'rb'))
    else:
        try:
            lo = pickle.load(open(os.path.join(runinfo.datafolder(model), layer + '.pkl'), 'rb'))
        except:
            h5filepath = os.path.join(runinfo.datafolder(model), layer + '.hdf5')

            reps_layer = h5py.File(h5filepath, 'r')
            num_datasets = len(list(reps_layer))
            ds_shape = list(reps_layer.get('0').shape)

            batch_size = ds_shape[0]
            ds_shape[0] = batch_size*num_datasets
            lo = np.zeros((ds_shape))
            for i in range(num_datasets):
                lo[batch_size*i : batch_size*(i+1)] = reps_layer.get(str(i))[()]

    if(model['type'] == 'LSTM' and ilayer == model['nlayers'] - 1):
        lo = lo.swapaxes(-2,-1)
        print("lo axes swapped. layer representations shape %s " %str(lo.shape))

    if(runinfo.verbose):
        print("read layer represenations. shape: ", lo.shape)

    return lo

# %% DIRECTION TUNING UTILITY FCTS

def unit_vector(vector):
    """ Returns the unit vector of the vector.  """
    return vector / np.linalg.norm(vector)

def angle_between(v1, v2):
    """ Returns the angle in radians between vectors 'v1' and 'v2'::

            >>> angle_between((1, 0, 0), (0, 1, 0))
            1.5707963267948966
            >>> angle_between((1, 0, 0), (1, 0, 0))
            0.0
            >>> angle_between((1, 0, 0), (-1, 0, 0))
            3.141592653589793

    https://stackoverflow.com/questions/2827393/angles-between-two-n-dimensional-vectors-in-python

    Arguments
    ---------
    v1 : np.array [2,]
    v2 : np.array [2,]

    Returns
    -------
    float, angle between v1 and v2
    """

    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)
    return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))
        #clip solves rounding errors

def angle_xaxis(v):
    """  Returns the angle between a vector and the x-axis ([1,0] vector) according to the rule
        Positive angle => positive y, negative angle => negative y
        Returns nan if v is 0

    Arguments
    ---------
    v : np.array [2,]

    Returns
    -------
    float, angle between v and the [1,0] vector
    """

    if not np.any(v):
        return np.nan
    elif v[1] >= 0:
        return angle_between(v, [1, 0])
    else:
        return - angle_between(v, [1,0])

def get_polar(X):
    ''' Converts Cartesian coordinates to polar

    Parameters
    ----------
    X : np.array [number of samples or timepoints, 2], Cartesian coordinates

    Returns
    -------
    Xp : np.array [number of samples or timepoints, 2], Polar coordinates
    '''

    Xp = np.zeros(X.shape)
    Xp[:,0] = np.apply_along_axis(np.linalg.norm, 1, X)
    Xp[:,1] = np.apply_along_axis(angle_xaxis, 1, X)
    return Xp

# %% DIRECTION AND VELOCITY TUNING FUNCTIONS

def linreg(X_train, X_test, Y_train, Y_test):
    ''' Performs linear regression with train and test set

    Parameters
    ----------
    X_train : np.array [number of samples,]
    X_test : np.array [number of samples,]
    Y_train : np.array [number of samples,]
    Y_test : np.array [number of samples,]

    Returns
    -------
    trainmets : list of floats [3], metrics for training fit specified in compute_metrics function
    testmets : list of floats [3], test metrics specified in compute_metrics function
    '''

    if(np.any(np.isnan(X_train))):
        print("Warning: Contains nans")
    c = np.linalg.lstsq(X_train, Y_train, rcond=None)[0]
    trainmets = np.concatenate((compute_metrics(Y_train, X_train @ c), c, np.zeros(3 - len(c))))
    testmets = np.concatenate((compute_metrics(Y_test, X_test @ c), c, np.zeros(3 - len(c))))

    #print("LinReg Fit Score: " + str(trainmets[:3]))
    #print("LinReg Test Score: " + str(testmets[:3]))
    #print("Coefficients: " + str(c[:3]))
    return trainmets, testmets

def feature_set(Xcols_train, Xcols_test, Y_train, Y_test):
    ''' Combines different features together and excludes invalid values

    Arguments
    ---------
    Xcols_train : tuple of np.arrays of shape [nr of samples, k features]
    Xcols_test : tuple of np.arrays
    Y_train : np.array
    Y_test : np.array

    Returns
    -------
    X_train : np.array [nr of samples, nr of features]
    X_test : np.array [nr of samples, nr of features]
    Y_train : np.array [nr of samples,]
    Y_test : np.array [nr of samples,]

    '''

    X_train = np.column_stack(Xcols_train).squeeze()
    X_test = np.column_stack(Xcols_test).squeeze()
    assert len(X_train) == len(Y_train), "Dimensions mismatch!!! X: %s , Y: %s" %(str(X_train.shape), str(Y_train.shape))
    if len(X_train.shape) > 1:
        nna = ~np.any(np.isnan(X_train), axis=1)
    else:
        nna = ~np.isnan(X_train)
    X_train = X_train[nna]
    Y_train = Y_train[nna]

    if len(X_test.shape) > 1:
        nna = ~np.any(np.isnan(X_test), axis=1)
    else:
        nna = ~np.isnan(X_test)
    X_test = X_test[nna]
    Y_test = Y_test[nna]

    return X_train, X_test, Y_train, Y_test

# %% DIRECTION AND VELOCITY TUNING
def tune_row_vel(X_train, X_test, Y_train, Y_test, row, isPolar = True):
    ''' Fit tuning curves for a single neuron / unit ("row") for velocity inputs (and similar kinematic features) for four different kinds of models
        1. simple linear regression over input features of X
        2. Directional tuning curves
        3. Velocity / Speed Dependence
        4. Direction x Velocity tuning curve

    Arguments
    ---------
    X : np.array [nr of samples, 2]
    Y : np.array [nr of samples]
    row : int, row index
    isPolar : bool, is result already boolean?

    Returns
    -------
    tuple of
        row : int, row index
        rowtraineval : np.array [4, 6] for four different model types. Cols 0-2: Metrics from compute_metrics, Cols 3-5: Linear Regression coeffs
        rowtesteval : np.array [4, 6] for four different model types. Cols 0-2: Metrics from compute_metrics, Cols 3-5: Linear regression coeffs
    '''

    rowtraineval = np.zeros((4,6))
    rowtesteval = np.zeros((4,6))

    ##BASELINE LINREG MODEL
    Xtform_train, Xtform_test, Ytform_train, Ytform_test = feature_set(
            (X_train, np.ones_like(X_train[:,0])),
            (X_test, np.ones_like(X_test[:,0])),
            Y_train,
            Y_test
            )

    rowtraineval[0], rowtesteval[0] = linreg(Xtform_train, Xtform_test, Ytform_train, Ytform_test)

    #Change to polar coordinates
    if(not isPolar):
        #print("converting to polar...")
        X_train = get_polar(X_train)
        X_test = get_polar(X_test)

    ##DIR DEP
    Xtform_train, Xtform_test, Ytform_train, Ytform_test = feature_set(
        (np.cos(X_train[:,1]), np.sin(X_train[:,1]), np.ones_like(X_train[:,0])),
        (np.cos(X_test[:,1]), np.sin(X_test[:,1]), np.ones_like(X_test[:,0])),
        Y_train,
        Y_test
        )

    rowtraineval[1], rowtesteval[1] = linreg(Xtform_train, Xtform_test, Ytform_train, Ytform_test)

    ##VEL DEP
    Xtform_train, Xtform_test, Ytform_train, Ytform_test = feature_set(
        (X_train[:,0], np.ones_like(X_train[:,0])),
        (X_test[:,0], np.ones_like(X_test[:,0])),
        Y_train,
        Y_test
        )
    rowtraineval[2], rowtesteval[2] = linreg(Xtform_train, Xtform_test, Ytform_train, Ytform_test)

    ##DIR PLUS VEL DEP
    Xtform_train, Xtform_test, Ytform_train, Ytform_test = feature_set(
        (X_train[:,0] * np.cos(X_train[:,1]), X_train[:,0] * np.sin(X_train[:,1]), np.ones_like(X_train[:,0])),
        (X_test[:,0] * np.cos(X_test[:,1]), X_test[:,0] * np.sin(X_test[:,1]), np.ones_like(X_test[:,0])),
        Y_train,
        Y_test
        )

    rowtraineval[3], rowtesteval[3] = linreg(Xtform_train, Xtform_test, Ytform_train, Ytform_test)

    return (row, rowtraineval, rowtesteval)

# %% LABEL SPECIFICITY

def tune_row_label(X_train, X_test, Y_train, Y_test, node):
    """Perform ANOVA-like analysis to determine how much of the variance can be
    explained by tuning to labels for a single unit ("row").

    Arguments
    ----------
    X: labels
    Y: node activitiy for each sample
    row: row index

    Returns
    ----------
    node: node index
    nodeeval: np.array
    """

    ##RESHAPE OPERATIONS
    X_train = label_binarize(X_train, [ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19.,])
    X_test = label_binarize(X_test, [ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19.,])
    Y_train = Y_train.reshape(-1,1)
    Y_test = Y_test.reshape(-1,1)

    try:
        svm = OneVsRestClassifier(LinearSVC(max_iter=10, verbose=0))
        svm.fit(Y_train, X_train)

        nodetraineval = roc_auc_score(X_train, svm.decision_function(Y_train))
        try:
            nodetesteval = roc_auc_score(X_test, svm.decision_function(Y_test))
        except ValueError as err:
            print("test evaluation failed. %s" %err)
            nodetesteval = 0.5

    except ValueError as err:
         print("fitting SVM failed. %s" %err)
         nodetraineval = 0.5
         nodetesteval = 0.5

    #print("Train eval: %s" %str(nodetraineval))
    #print("Test eval: %s" %str(nodetesteval))

    return node, np.array([(nodetraineval-0.5)*2]), np.array([(nodetesteval-0.5)*2])

# %% TUNE

def tune(X, fset, Y, centers, nmods, nmets, ilayer, mmod='std', pool=None):
    ''' Makes calls to tuning curves for individual rows using multiprocessing

       Arguments
       ---------
       X : np.array
       fset : string, name of kinematic feature
       Y : hidden layer activations to be fitted
       centers : np.array of ints, mask for downsampling that contains the integer positions for temporal dimension that correspond to units in hidden layers
       nmods : number of different types of models that will be tested for this kinematic feature
       nmets : number of metrics to be used (usually 3 to correspond to output of compute_metrics)
       mmod : modifier for the kinematic feature type (default 'std')

       Returns
       -------
       trainevals : np.array [nr rows / units, nr of tuning curves, nr of metrics]
       testevals : np.array [nr rows / units, nr of tuning curves, nr of metrics]

    '''

    Yshape = Y.shape
    if len(Yshape) == 3:
        ravelshape = (Y.shape[1], nmods, nmets)
        unravelshape = (Y.shape[1], nmods, nmets)
    else:
        ravelshape = (Y.shape[1] * Y.shape[3], nmods, nmets )
        unravelshape = (Y.shape[1], Y.shape[3], nmods, nmets)

    trainevals =  np.empty(ravelshape)
    testevals = np.empty(ravelshape)
        #Axis 0-k: lo dims except time
        #Axis k+1: (0) LinReg (1) Dir Tuning (2) Vel Tuning (3) Dir + Vel Tuning
        #Axis k+2: (0) RMSE (1) r2 (2) PCC

    if(fset != 'labels'):
        n_cpus = 10
    else:
        n_cpus = 5
    print('Max CPU Count: %d , using %d ' %(mp.cpu_count(), n_cpus))
    pool = mp.Pool(n_cpus)
    print("Pool started")

    results = []

    # Resize Y so that feature maps are appended as new rows in first feature map
    Y = Y.swapaxes(1,2).reshape((Y.shape[0], Y.shape[2], -1)).swapaxes(1,2)
    print("Y reshaped")

    for irow in range(Y.shape[1]):
        print("Layer %d, Node %d / %d" %(ilayer, irow, Y.shape[1]))

        if(len(X.shape) > 1):
            x = X[..., centers]
        else:
            x = X

        y = Y[:,irow]

        ### DO TRAIN-TEST SPLIT
        x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.20, random_state=42)

        ##RESHAPE FOR LINEAR REGRESSION

        if len(x.shape) > 1: ##FOR TIME-BASED DATA ( 2 COMPS PER TIMESPACE IS USE CASE)

            tcoff = sum(np.where(centers <= 32, True, False))
            x_train = x_train[...,tcoff:ntime-tcoff]
            x_test = x_test[...,tcoff:ntime-tcoff]
            y_train = y_train[:,tcoff:ntime-tcoff]
            y_test = y_test[:,tcoff:ntime-tcoff]
            x_train = x_train.swapaxes(1,2).reshape((-1,2))
            x_test = x_test.swapaxes(1,2).reshape((-1,2))
        elif fset == 'labels':
            temp_train = np.ones_like(y_train)
            temp_test = np.ones_like(y_test)
            x_train = (temp_train.swapaxes(0,1) * x_train).swapaxes(0,1)
            x_train = x_train.reshape((-1,))
            x_test = (temp_test.swapaxes(0,1) * x_test).swapaxes(0,1)
            x_test = x_test.reshape((-1,))

        y_train = y_train.reshape((-1,))
        y_test = y_test.reshape((-1,))

        if fset == 'acc':
            results.append(pool.apply_async(tune_row_vel, args=(x_train, x_test, y_train, y_test,irow, True)))
        elif fset == 'vel' or fset == 'eepolar' or fset == 'ang' or fset=='angvel':
            results.append(pool.apply_async(tune_row_vel, args=(x_train, x_test, y_train, y_test ,irow, True)))
        elif fset == 'ee':
            results.append(pool.apply_async(tune_row_vel, args=(x_train, x_test,y_train, y_test,irow, False)))
            #sys.stdout.flush()
        elif fset == 'labels':
            results.append(pool.apply_async(tune_row_label, args=(x_train, x_test, y_train, y_test, irow)))

    results = [r.get() for r in results]

    pool.close()
    print("pool closed")
    pool.join()
    print("pool joined")

    for result in results:
        trainevals[result[0]] = result[1]
        testevals[result[0]] = result[2]

    #shape evals back to original shape
    trainevals = trainevals.reshape(unravelshape)
    testevals = testevals.reshape(unravelshape)

    return trainevals, testevals

def tune_decoding(X, fset, Y, centers, ilayer, mmod, alpha = None):
    """ Computes decoding accuracies for given kinetic variable

    Arguments
    ----------

    Returns
    -------
    trainevals : np.array of floats [nr_features, 3]
    testevals : np.array of floats [nr_features, 3]
    coefs : np.array of floats [nr_features, nr_coefs]
    """

    print("Running decoding with alpha parameter %f ..." %alpha)

    if(len(X.shape) > 1):
        X = X[..., centers]

    # Resize Y so that feature maps are appended as new rows in first feature map
    Y = Y.swapaxes(1,2).reshape((Y.shape[0], Y.shape[2], -1)).swapaxes(1,2)

    print("X has size %s and Y %s" %(str(X.shape), str(Y.shape)))

    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.20, random_state=42)

    if fset != 'labels':
        # reshape so that both X and Y are in format [samples x timepoints, features] (except for labels)
        print(X.shape)
        assert len(X.shape) > 1, "X has shape 1 %s" %str(X.shape)

        X_train = X_train.swapaxes(1,2).reshape((-1, X_train.shape[1]))
        if len(Y_train.shape) > 2:
            Y_train = Y_train.swapaxes(1,2).reshape((-1, Y_train.shape[1]))
        else:
            Y_train = Y_train.reshape((-1, Y_train.shape[1]))

        X_test = X_test.swapaxes(1,2).reshape((-1, X_test.shape[1]))
        if len(Y_test.shape) > 2:
            Y_test = Y_test.swapaxes(1,2).reshape((-1, Y_test.shape[1]))
        else:
            Y_test = Y_test.reshape((-1, Y_test.shape[1]))
    # for labels: reshape so that both are in format [samples, timepoints x features]
    else:
        print("About to binarize shape %s" %str(X_train.shape))
        X_train = label_binarize(X_train, [ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19.,])
        print("New shape of X %s"%str(X.shape))
        Y_test = Y_test.reshape((Y_test.shape[0], -1))

        print("About to binarize shape %s" %str(X_test.shape))
        X_train = label_binarize(X_train, [ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19.,])
        print("New shape of X %s"%str(X_train.shape))
        Y_test = Y_test.reshape((Y_test.shape[0], -1))

    #switch kin vars / labels to Y and neuron firing ratest to X
    X_temp_train = X_train
    X_train = Y_train
    Y_train = X_temp_train

    X_temp_test = X_test
    X_test = Y_test
    Y_test = X_temp_test

    if len(Y_train.shape) > 1:
        nna = ~np.any(np.isnan(Y_train), axis=1)
    else:
        nna = ~np.isnan(Y_train)
    X_train = X_train[nna]
    Y_train = Y_train[nna]

    if len(Y_test.shape) > 1:
        nna = ~np.any(np.isnan(Y_test), axis=1)
    else:
        nna = ~np.isnan(Y_test)
    X_test = X_test[nna]
    Y_test = Y_test[nna]

    trainevals = []
    testevals = []
    coefs = []

    assert Y_train.shape[1] > 1, 'Y is supposed to have multiple targets/columns'

    assert Y_train.size < X_train.size, "somehow size of target is bigger than size of training, are you sure that you are decoding the kinematic variable and not the inverse?"

    if(fset == 'ee'):
        ee_lms = []

    if (len(Y_train) >= 1 and len(Y_test)  >= 1):

        for target in range(Y_train.shape[1]):

            if fset != 'labels':

                if alpha is None:
                    lm = LinearRegression().fit(X_train, Y_train[:,target])

                else:
                    lm = Ridge(alpha = alpha).fit(X_train, Y_train[:,target])
                    print("Running regression with alpha %f ..." %alpha)

                trainevals.append(compute_metrics(Y_train[:, target], lm.predict(X_train)))
                testevals.append(compute_metrics(Y_test[:, target], lm.predict(X_test)))

                coefs.append(lm.coef_.copy())

                if fset == 'ee':
                    ee_lms.append(lm)

            else:
                svm = OneVsRestClassifier(LinearSVC(max_iter=10, verbose=0))
                try:
                    svm.fit(X_train, Y_train)
                except ValueError as err:
                    print("fitting SVM failed. %s" %err)
                    nodetraineval = 0.5

                try:
                    print("About to calculate ROC: X_train Shape: %s, Y_train Shape: %s" %(str(X_train.shape), str(Y_train.shape)))
                    nodetraineval = roc_auc_score(Y_train, svm.decision_function(X_train))
                except ValueError as err:
                    print("train evaluation failed. %s" %err)
                    nodetraineval = 0.5

                try:
                    nodetesteval = roc_auc_score(Y_test, svm.decision_function(X_test))
                except ValueError as err:
                    print("test evaluation failed. %s" %err)
                    nodetesteval = 0.5

                #print("Train eval: %s" %str(nodetraineval))
                #print("Test eval: %s" %str(nodetesteval))

                trainevals.append(np.array([(nodetraineval-0.5)*2]*3))
                testevals.append(np.array([(nodetesteval-0.5)*2]*3))
                coefs.append([np.nan]*3)

    else:
        trainevals.append([np.nan]*3)
        testevals.append([np.nan]*3)
        coefs.append([np.nan]*3)

    trainevals = np.vstack(trainevals)
    testevals = np.vstack(testevals)
    coefs = np.vstack(coefs)

    #for ees: compute "mean distance" metric and store in previous PPC columns
    if fset == 'ee':
        Y_train_pred = np.hstack([ee_lms[i].predict(X_train).reshape(-1,1) for i in range(2)])
        Y_test_pred = np.hstack([ee_lms[i].predict(X_test).reshape(-1,1) for i in range(2)])

        print("Shape of Y_train_pred: ", Y_train_pred.shape)
        assert Y_train_pred.shape[1] == 2 and len(Y_train_pred.shape) == 2, "unexpected shape of predictions, Y_train_pred shape " + str(Y_train_pred.shape)

        trainevals[:,2] = compute_dist_metric(Y_train, Y_train_pred)
        testevals[:,2] = compute_dist_metric(Y_test, Y_test_pred)

        print("Rewrote the PPC column :)")

    return (trainevals, testevals, coefs)

def tune_layer(X, fset, xyplmvt, runinfo, ilayer, mmod, model, t_stride=2, pool=None, alpha=None):
    """Performs several layer-wide calculations for fitting the tuning curves and saves output to file

    Arguments
    ---------
    X : np.array of floats, [nr_samples, nr_features, 320 timepoints]
    fset : string, kinematic tuning curve name
    xyplmvt : np.array of bools [nr_samples,], mask restricting to trials / movements that occur in the desired plane or orientation
    runinfo : RunInfo (dict extension)
    ilayer : int, index of current layer, -1 = spindles
    mmod : string, modifier to model or kinematic tuning curve type
    model : dict, information on model
    t_stride : int, temporal stride used when applying convolutional filters

    """

    savepath = runinfo.resultsfolder(model, fset)

    if mmod == 'decoding':
        savepath = runinfo.resultsfolder(model, '%s_%s' %(mmod, fset))

    try:
        os.makedirs(savepath)
    except:
        print('folder already exists')

    savename = 'l%d_%s_mets_%s_%s' %(ilayer+1, fset, mmod, runinfo.planestring())

    modelname = model['name']
    base = model['base']
    layer = lstring(ilayer)
    expid = runinfo['expid']

    #lo = pickle.load(open(os.path.join(runinfo.datafolder(model), layer + '.pkl'), 'rb'))
    lo = read_layer_reps(ilayer, runinfo, model)
    lo = lo[xyplmvt]

    centers = get_centers(lo.shape[2], ilayer, model)

    if mmod=='decoding':
        trainevals, testevals, coefs = tune_decoding(X, fset, lo, centers, ilayer, mmod, alpha)

        print('saving decoding reg coefs')
        if alpha is None:
            savefile = '%s/%s_coefs.npy' %(savepath, savename)
        else:
            savefile = '%s/%s_a%d_coefs.npy' %(savepath, savename, int(alpha*1000))

        np.save(savefile, coefs)

    else:
        if (fset == 'vel' or fset == 'acc' or fset == 'eepolar' or fset == 'ang' or fset=='angvel' or fset=='ee'):
            nmods = 4
            nmets = 6
        elif (fset == 'labels'):
            nmods = 1
            nmets = 1

        if len(X) > 1:
            trainevals, testevals = tune(X, fset, lo, centers, nmods, nmets, ilayer, mmod, pool=pool)
        else:
            trainevals = np.empty((0, nmods, nmets))
            testevals = np.empty((0, nmods, nmets))

    print("Layer %d Completed!" %ilayer)

    if alpha is None:
        savefile_train = '%s/%s_train.npy' %(savepath, savename)
        savefile_test = '%s/%s_test.npy' %(savepath, savename)
    else:
        savefile_train = '%s/%s_a%d_train.npy' %(savepath, savename,  int(alpha*1000))
        savefile_test = '%s/%s_a%d_test.npy' %(savepath, savename,  int(alpha*1000))

    np.save(savefile_train, trainevals)
    np.save(savefile_test, testevals)
    print("files saved")

# %% DATA READIN

def X_data(fset = 'vel', runinfo = dict({'orientation': 'hor', 'plane': 'all', 'datafraction': 0.5}),
           datafolder = '../data/spatial_temporal_4_8-16-16-32_64-64-64-64_5272/',
           threed= False,
           polar=True):
    """Returns input data for model. Uses global parameters.

    Arguments
    ---------
    fset : string, one of ['acc', 'ang', 'angvel', 'labels', 'ee', 'eepolar', 'muscle_coords', 'mf' (muscle firing rates)]
    runinfo : RunInfo (dict extension)
    datafolder : string, location where data is stored
    threed : bool, are all three coords needed or only the two in which movement occurs
    polar : bool, should data be converted to polar

    Returns
    ---------
    vels: np.array of floats [nsamples, 2, 320]
    xyplmvt: list of bools [nsamples_original], boolean mask to mask samples
        that are not in xy plane
    """

    ### INIT
    print(datafolder)
    try:
        kinvars = pd.read_hdf(os.path.join(datafolder, 'kinvars.hdf5'))
        mc = np.swapaxes(kinvars['muscle_coords'].values, 0, 1)
        ee = np.swapaxes(kinvars['endeffector_coords'].values, 0, 1)
    except:
        print('using alternate method for accessing kinvars files by directly accessing needed arrays (pandas causes error)')
        filename = os.path.join(datafolder, 'kinvars.hdf5')

        with h5py.File(filename, "r") as f:

            key = 'data'
            data_group = 'block0_values'
            data_values = f[key][data_group][:]

            ee = data_values[:,:,:3]
            ee = np.swapaxes(ee, 1, 2)
            ee = np.swapaxes(ee, 0, 2)

            mc = data_values[:,:,7:32]
            mc = np.swapaxes(mc, 1, 2)
            mc = np.swapaxes(mc, 0, 2)

    vels = np.gradient(ee)[2]

    # EXCLUDE SAMPLES THAT AREN'T IN XY PLANE
    if(runinfo['orientation']=='hor'):
        if runinfo['height']=='all':
            xyplmvt = (1 - np.any(vels[:,2,:], axis=1)).astype('bool')
        else:
            xyplmvt = np.all(ee[:,2,:] == runinfo['height'], axis=1)

        if not threed:
            ee = ee[:,:2]
            vels = vels[:,:2]
    else:
        if runinfo['height']=='all':
            xyplmvt = np.invert(np.any(vels[:,0,:], axis=1)) #movements in a yz plane
        else:
            xyplmvt = np.all(ee[:,0,:] == runinfo['height'], axis=1)

        if not threed:
            ee = ee[:, 1:]
            vels = vels[:, 1:]

    ee = ee[xyplmvt]
    vels = vels[xyplmvt]
    mc = mc[xyplmvt]
    X = vels

    if len(X) > 1:
        print(X.size)
        print(X.shape)

        if(fset == 'acc'):
            X = np.gradient(vels)[2]
            if polar:
                X = get_polar(X)
            #X = np.nan_to_num(X)
        elif(fset == 'ang'):
            Xa = np.apply_along_axis(angle_xaxis, 1, vels)
            Xb = np.gradient(Xa)[1]
            Xa = Xa[:,np.newaxis,:]
            Xb = Xb[:,np.newaxis,:]
            X = np.concatenate((Xb, Xa), axis=1)
        elif(fset == 'angvel'):
            X = np.apply_along_axis(angle_xaxis, 1, vels)
            X = np.gradient(X)[1]
            X = np.concatenate((X[:,np.newaxis,:], X[:,np.newaxis,:]), axis=1)
        elif(fset == 'labels'):
            X = pickle.load(open(os.path.join(datafolder, 'labels.pkl'), 'rb'))
            X = X[xyplmvt]
        elif(fset == 'ee'):
            X = ee
        elif(fset == 'eepolar'):
            x0 = ee[:,:,0]
            Xrel = [(ee[i].swapaxes(0,1) - x0[i]).swapaxes(0,1) for i in range(len(ee))]
            X[:,0,:] = np.apply_along_axis(np.linalg.norm, 1, Xrel)
            X[:,1,:] = np.apply_along_axis(angle_xaxis, 1, Xrel)

        elif(fset == 'vel'):

            if polar:
                X = get_polar(X)

        elif(fset == 'muscle_coords'):
            X = mc

        elif (fset == 'mf'):
            data = pickle.load(open(os.path.join(datafolder, 'data.pkl'), 'rb'))
            X = data[xyplmvt]

    return X, xyplmvt

# %% MAIN

def main(fset, runinfo, model, startlayer=-1, endlayer=8, mmod='std', pool=None, alpha = None):

    assert fset == 'vel' or fset == 'acc' or fset == 'eepolar' or fset == 'ang'\
        or fset == 'labels' or fset=='angvel' or fset=='ee', "Invalid fset!!!"

    assert mmod == 'std' or mmod=='decoding', 'Invalid mmod!!!'

    assert mmod == 'decoding' or alpha is None, "alpha values only accepted for decoding"

    modelname = model['name']
    nlayers = model['nlayers']
    base = model['base']

    print('evaluating %s model for %s %s, expid %d, plane %s ...' %(modelname, fset, mmod, runinfo['expid'], runinfo.planestring()))

    X, xyplmvt = X_data(fset, runinfo, datafolder=runinfo.datafolder(model))

    np.random.seed(42)

    for ilayer in np.arange(startlayer, min(endlayer, nlayers)):
        tune_layer(X, fset, xyplmvt, runinfo, ilayer, mmod, model, pool=pool, alpha=alpha)

if __name__=='__main__':

    parser = argparse.ArgumentParser(description='Tuning Properties Analysis for DeepDraw Models.')
    parser.add_argument('--fset', type=str, default = 'vel', help='Type of Features {"vel", "acc", "ang", "labels"}')
    parser.add_argument('--expid', type=int, help='Experiment ID Number')
    parser.add_argument('--startlayer', type=int, default=-1, help='Layer you want to start at')
    parser.add_argument('--endlayer', type=int, default=8, help='Layer you want to end before')
    parser.add_argument('--mmod', type=str, default='std', help='Model modifier')
    parser.add_argument('--mtype', type=str, default='S', \
                        help='Model type to be analyzed {(S)patial temporal, (T)emporal spatial, (S)patio(T)emporal, (F)ully (C)onnected}'
                        )

    args = parser.parse_args()

    main(args.fset, args.expid, args.startlayer, args.endlayer, args.mmod, args.mtype)


from django.conf.urls import url, include
from home import views
from home.views import PlacesListView, PlaceDetailView

urlpatterns = [
    url(r'^index/', views.home, name='home'),
    url(r'^nosotros/', views.nosotros, name='nosotros'),
    url(r'^datos/$', PlacesListView.as_view(), name='datos'),
    url(r'^gracias/', views.gracias, name='gracias'),
    url(r'^contactenos/', views.contactenos, name='contactenos'),
    url(r'^datos/(?P<pk>\d+)$', PlaceDetailView.as_view(), name='detalle'),

    #url(r'^crear/$', PlaceCreate.as_view(), name='place-crear'),
    #url(r'^datos/(?P<pk>\d+)/actualizar/$', PlaceUpdate.as_view(), name='place-actualizar'),
    #url(r'^datos/(?P<pk>\d+)/actualizar/$', PlaceDelete.as_view(), name='author-delete'),

]


lista_entrada = []
pares = []
impares = []
contin = 'S'
while contin in 'Ss':
    num = int(input('Digite um número:'))
    if num not in lista_entrada:
        lista_entrada.append(num)
    contin = input('Continuar?[S/N]').upper()
    while contin not in 'SN':
        contin = input('Continuar?[S/N]').upper()
print(f'A lista é: ', lista_entrada)
for c in lista_entrada:
    if c % 2 == 0:
        pares.append(c)
    else:
        impares.append(c)
print(f'A lista par é: ', pares)
print(f'A lista impar é: ', impares)


secret_number = 9
guess_count = 0
#guess_limit = 3
while guess_count < 3:
    guess = int(input("Guess: "))
    guess_count += 1
    if  guess == secret_number:
        print("You won")
        #use break atatement to terminate a loop
        break
else:
    print("Sorry you failed")


import spacy
from dateutil import parser # for date parsing
import json # for request body
from tkinter import Tk, filedialog # for file dialog

from src.read_mapping_file import read_mapping_file
from src.text_preprocessing import delete_punctuation
from src.booleanFormulas import generate_boolean_formula

def file_selection_dialog():
    root = Tk()
    root.withdraw()
    default_dir = 'training_data'
    file_path = filedialog.askopenfilename(parent=root, initialdir=default_dir, title='Please select a file')
    return file_path

def find_model_code(doc,ModelTyp_file):
    # read the ModelTyp's keywords
    (ModelTyp_keywords, ModelTyp_mapping) = read_mapping_file(ModelTyp_file)

    # find the keywords of model in the user's prompt
    model_keyword = [keyword for chunk in doc.noun_chunks for keyword in ModelTyp_keywords if keyword in chunk.text]
    # find the correspongding code of the model
    model_code = [ModelTyp_mapping[keyword] for keyword in model_keyword]
    return model_code

def find_data(doc):
    '''find the date in the user's prompt'''
    for ent in doc.ents:
        if ent.label_ == 'DATE':
            # parse the date using datetime.strptime
            date_obj = parser.parse(ent.text, fuzzy=True)
            formatted_date = date_obj.strftime("%Y-%m-%d")
            return formatted_date

if __name__ == "__main__":
    # load the english model
    nlp = spacy.load("en_core_web_sm")
    nlp.add_pipe("sentencizer")

    # read the user's prompt
    file_path = file_selection_dialog()
    with open(file_path, "r") as f:
        text = f.read()

    # delete the punctuation from the user's prompt
    text = delete_punctuation(text)

    doc = nlp(text)

    #ModelTypCodes
    ModelTyp_file = 'file\modelTyp_mapping.csv'
    model_code = find_model_code(doc,ModelTyp_file)

    # find the transformed code of config
    config_code = generate_boolean_formula(doc)

    # extract the dates from the user's prompt
    formatted_date = find_data(doc)

    # create the request body
    Request_body = {
        "modelTypeCodes": [model_code],
        "booleanFormulas": [config_code],
        "dates": [formatted_date],
    }
    # convert the request body to json
    json_str = json.dumps(Request_body)

    # print the request body
    print(json_str)


import argparse
import ast
import hashlib
import json
import numpy as np
import os
import time

import torchvision.datasets as datasets
from torchvision.datasets import CIFAR10
import torchvision.transforms as transforms
import torchcontrib

import torch
import torch.backends.cudnn as cudnn
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

from imagenet import Imagenet32
from utils import progress_bar, grab_patches, normalize_patches, grab_patches_2, update_batch_norms, update_classifiers, grab_patches_from_loader, correct_topk, preprocess

print('simple_logistic.py')
parser = argparse.ArgumentParser('simple logistic regression with convolutional random features model')
# parameters for the patches
parser.add_argument('--dataset', help="cifar10/?", default='cifar10')
parser.add_argument('--no_padding', action='store_true', help='no padding used')
parser.add_argument('--patches_file', help=".t7 file containing patches", default='')
parser.add_argument('--correct_padding', action='store_true', help='use image instead of 0 padding')
parser.add_argument('--n_channel_convolution', default=256, type=int)
parser.add_argument('--n_channel_convolution_scale_1', default=0, type=int)
parser.add_argument('--spatialsize_convolution', default=6, type=int)
parser.add_argument('--padding_mode', default='constant', choices=['constant', 'reflect', 'symmetric'], help='type of padding for torch RandomCrop')
parser.add_argument('--learn_patches', action='store_true', help='learn the patches by SGD')
parser.add_argument('--patch_distribution', default='empirical', choices=['empirical', 'random'], help='distribution from which patches are drawn')
parser.add_argument('--channel_pca', type=int, default=0, help='perform PCA along channels')
parser.add_argument('--no_zca', action='store_true', help='no zca whitening performed on the patches')
parser.add_argument('--zca_bias', default=0.001, type=float, help='regularization bias for zca whitening')
parser.add_argument('--whiten_dataset', action='store_true', help='no zca whitening performed on the patches')

# parameters for the extraction
parser.add_argument('--stride_convolution', default=1, type=int)
parser.add_argument('--stride_avg_pooling', default=2, type=int)
parser.add_argument('--spatialsize_avg_pooling', default=5, type=int)
parser.add_argument('--bias', type=float, default=0.1)
parser.add_argument('--shrink', default='softshrink', choices=['softshrink', 'hardshrink', 'heaviside', 'topk', 'topk_heaviside', 'topk_heaviside_and_heaviside'], help='type of shrink function')
parser.add_argument('--topk_fraction', default=0.25, type=float)
parser.add_argument('--positive_shrink', action='store_true', help='use positive and negative part of shrink func')

# parameters of the classifier
parser.add_argument('--batch_norm', action='store_true', help='add batchnorm before classifier')
parser.add_argument('--bottleneck_dim', default=0, type=int, help='bottleneck dimension for the classifier')
parser.add_argument('--batch_norm_bottleneck', action='store_true', help='add batchnorm in bottleneck')
parser.add_argument('--separable_convolution', action='store_true', help='makes the classifier convolution separable in channels and space')
parser.add_argument('--n_bagging_patches', type=int, default=0, help='do model bagging in the patches dimension')
parser.add_argument('--convolutional_classifier', type=int, default=0, help='size of the convolution for convolutional classifier')
parser.add_argument('--convolutional_loss', action='store_true', help='use convolutional loss')
parser.add_argument('--lambda_1', default=0., type=float, help='group lasso penalty on the conv')
parser.add_argument('--block_dropout', default=0, type=int, help='perform block dropout')
parser.add_argument('--multi_classif', action='store_true')

# parameters of the optimizer
parser.add_argument('--batchsize', type=int, default=512)
parser.add_argument('--batchsize_net', type=int, default=0)
parser.add_argument('--lr_schedule', type=str, default='{0:1e-3, 1:1e-4}')
parser.add_argument('--nepochs', type=int, default=90)
parser.add_argument('--optimizer', choices=['Adam', 'SGD'], default='Adam')
parser.add_argument('--sgd_momentum', type=float, default=0.)
parser.add_argument('--weight_decay', type=float, default=0.)

# hardware parameters
parser.add_argument('--path_train', help="path to imagenet", default='/d1/dataset/imagenet32/out_data_train')
parser.add_argument('--path_test', help="path to imagenet", default='/d1/dataset/imagenet32/out_data_val')
parser.add_argument('--num_workers', type=int, default=2)
parser.add_argument('--multigpu', action='store_true')
parser.add_argument('--no_cudnn', action='store_true', help='disable cuDNN to prevent cuDNN error (slower)')
parser.add_argument('--no_jit', action='store_true', help='disable torch.jit optimization to prevent error (slower)')

# reproducibility parameters
parser.add_argument('--force_recompute', action='store_true')
parser.add_argument('--numpy_seed', type=int, default=0)
parser.add_argument('--torch_seed', type=int, default=0)
parser.add_argument('--save_model', action='store_true', help='saves the model')
parser.add_argument('--save_best_model', action='store_true', help='saves the best model')
parser.add_argument('--resume', default='', help='filepath of checkpoint to load the model')

# logs
parser.add_argument('--no_progress_bar', action='store_true', help="Don't plot progress basr (for FI)")

args = parser.parse_args()

if args.multi_classif:
    assert args.batchsize_net > 0, 'batchsize_net must be positive for multiclass classification'

if args.batchsize_net > 0:
    assert args.n_channel_convolution // args.batchsize_net == args.n_channel_convolution / args.batchsize_net, 'batchsize_net must divide n_channel_convolution'
if args.block_dropout > 0:
    assert args.n_channel_convolution // args.block_dropout == args.n_channel_convolution / args.block_dropout, 'block_dropout must divide n_channel_convolution'

print('arguments')
print(args)

learning_rates = ast.literal_eval(args.lr_schedule)

# Extract the parameters
n_channel_convolution = args.n_channel_convolution
n_channel_convolution_scale_1 = args.n_channel_convolution_scale_1
stride_convolution = args.stride_convolution
spatialsize_convolution = args.spatialsize_convolution
stride_avg_pooling = args.stride_avg_pooling
spatialsize_avg_pooling = args.spatialsize_avg_pooling
if torch.cuda.is_available():
    device = 'cuda'
    n_gpus = torch.cuda.device_count()
else:
    device = 'cpu'
print(f'device: {device}')
torch.manual_seed(args.torch_seed)
np.random.seed(args.numpy_seed)

train_sampler = None

# Define the dataset
if args.dataset == 'cifar10':
    spatial_size = 32
    transform_train = transforms.Compose([
        transforms.RandomCrop(32, padding=4),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
    ])
    transform_test = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
    ])
    trainset = CIFAR10(root='./data', train=True, download=True, transform=transform_train)
    trainloader = torch.utils.data.DataLoader(trainset, batch_size=args.batchsize, shuffle=True, num_workers=args.num_workers)
    n_classes=10
    testset = CIFAR10(root='./data', train=False, download=True, transform=transform_test)
    testloader = torch.utils.data.DataLoader(testset, batch_size=args.batchsize, shuffle=False, num_workers=args.num_workers)
elif args.dataset in ['imagenet32', 'imagenet64', 'imagenet128']:

    normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                     std=[0.229, 0.224, 0.225])

    n_arrays_train = 10
    padding = 4
    spatial_size = 32
    if args.dataset=='imagenet64':
        spatial_size = 64
        padding = 8
    if args.dataset=='imagenet128':
        spatial_size = 128
        padding = 16
        n_arrays_train = 99
    n_classes = 1000

    if args.no_padding:
        padding = 0

    if args.correct_padding:
        transforms_train = [
            transforms.Resize(spatial_size+2*padding),
            transforms.RandomCrop(spatial_size),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            normalize,
        ]
        transforms_test = [
            transforms.Resize(spatial_size+2*padding),
            transforms.CenterCrop(spatial_size),
            transforms.ToTensor(),
            normalize,
        ]
    else:
        # print('pad + random resized crop scale 0.8 - 1 ')
        transforms_train = [
            transforms.RandomCrop(spatial_size, padding=padding, padding_mode=args.padding_mode),
            # transforms.Pad(padding=padding, padding_mode=args.padding_mode),
            # transforms.RandomResizedCrop(spatial_size, scale=(0.8, 1), ratio=(0.8,1.2)),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor(),
            normalize,
        ]
        transforms_test = [transforms.ToTensor(), normalize]

    trainset = Imagenet32(args.path_train, transform=transforms.Compose(transforms_train), sz=spatial_size, n_arrays=n_arrays_train)
    testset = Imagenet32(args.path_test, transform=transforms.Compose(transforms_test), sz=spatial_size)

    trainloader = torch.utils.data.DataLoader(
        trainset, batch_size=args.batchsize, shuffle=True,
        num_workers=args.num_workers, pin_memory=True)
    testloader = torch.utils.data.DataLoader(
        testset,
        batch_size=args.batchsize, shuffle=False,
        num_workers=args.num_workers, pin_memory=True)
    n_classes = 1000

zca_str = 'nozca' if args.no_zca else f'zcabias{args.zca_bias}'
default_patches_file = f'patches/{args.dataset}_seed{args.numpy_seed}_n{args.n_channel_convolution}_size{args.spatialsize_convolution}_{zca_str}_filter.t7'
patches_file = args.patches_file if args.patches_file else default_patches_file

if args.whiten_dataset:
    # Whitening of sets
    train_whiten, test_whiten = preprocess(trainset.data, testset.data)
    trainset.data = train_whiten
    testset.data = test_whiten

if args.patch_distribution == 'empirical':
    if not os.path.exists(patches_file) or args.force_recompute:
            t = None
            if args.dataset in ['cifar10', 'imagenet32', 'imagenet64', 'imagenet128']:
                if hasattr(trainset, 'train_data'):
                    t = trainset.train_data
                elif hasattr(trainset, 'data'):
                    t = trainset.data
                else:
                    raise RuntimeError
                print(f'Trainset : {t.shape}')
                patches, idx = grab_patches(t, seed=args.numpy_seed, patch_size=spatialsize_convolution)
                # patches, patch_classes = grab_patches_2(t, trainset.targets, 500000, patch_size=spatialsize_convolution, seed=args.numpy_seed)
                # imgset = Imagenet32(args.path_train, transform=transforms.Compose([transforms.ToTensor()]), sz=spatial_size)
                # imgloader = torch.utils.data.DataLoader(imgset, batch_size=args.batchsize, shuffle=False, num_workers=args.num_workers, pin_memory=True)
                # patches, patch_classes = grab_patches_from_loader(imgloader, n_patches=n_channel_convolution, image_size=spatial_size, patch_size=spatialsize_convolution, n_images=1281159, seed=0)
            elif args.dataset == '':
                imgset = datasets.ImageFolder(
                    args.path_train,
                    transforms.Compose([
                        transforms.Resize(spatial_size),
                        transforms.CenterCrop(spatial_size),
                        transforms.ToTensor(),
                ]))

                imgloader = torch.utils.data.DataLoader(
                    imgset, batch_size=args.batchsize, shuffle=False,
                    num_workers=args.num_workers, pin_memory=True, sampler=None)
                patches, patch_classes = grab_patches_from_loader(imgloader, n_patches=n_channel_convolution, image_size=spatial_size, patch_size=spatialsize_convolution, n_images=1281159, seed=0)
            print(f'patches extracted: {patches.shape}')
            patches = normalize_patches(patches, zca_bias=args.zca_bias, zca_whitening=(not args.no_zca))
            print(f'patches normalized: {patches.shape}')
            idxs = np.random.choice(patches.shape[0], n_channel_convolution, replace=False)
            selected_patches = patches[idxs].astype(np.float32)
            print(f'patches randomly selected: {selected_patches.shape}')
            kernel_convolution = torch.from_numpy(selected_patches)
            print(f'saving patches in file {patches_file}')
            torch.save(kernel_convolution, patches_file)
    else:
        kernel_convolution = torch.load(patches_file)
elif args.patch_distribution == 'random':
    kernel_convolution = nn.Conv2d(3, n_channel_convolution,
                             spatialsize_convolution, stride=stride_convolution, bias=False).weight.data
    kernel_convolution = kernel_convolution.view(kernel_convolution.size(0), -1)
    kernel_convolution /= kernel_convolution.norm(p=2, dim=1, keepdim=True)
    kernel_convolution = kernel_convolution.view(kernel_convolution.size(0), 3, args.spatialsize_convolution, args.spatialsize_convolution).contiguous()

if n_channel_convolution_scale_1 > 0 :
    default_patches_file = f'patches/{args.dataset}_seed{args.numpy_seed}_n{args.n_channel_convolution_scale_1}_size{2*args.spatialsize_convolution}_{zca_str}_filter.t7'
    if not os.path.exists(default_patches_file) or args.force_recompute:
        if hasattr(trainset, 'train_data'):
            t = trainset.train_data
        elif hasattr(trainset, 'data'):
            t = trainset.data
        else:
            raise RuntimeError
        print(f'Trainset : {t.shape}')
        patches_1, idx_1 = grab_patches(t, seed=args.numpy_seed, patch_size=2*spatialsize_convolution)
        patches_1 = normalize_patches(patches_1, zca_bias=args.zca_bias, zca_whitening=(not args.no_zca))
        idxs = np.random.choice(patches_1.shape[0], n_channel_convolution_scale_1, replace=False)
        selected_patches_1 = patches_1[idxs].astype(np.float32)
        kernel_convolution_1 = torch.from_numpy(selected_patches_1)
        print(f'saving patches in file {default_patches_file}')
        torch.save(kernel_convolution_1, default_patches_file)
    else:
        kernel_convolution_1 = torch.load(default_patches_file)

def compute_classifier_outputs(outputs1, outputs2, targets, args, batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck, train=True):
    if args.batch_norm:
        outputs1, outputs2 = batch_norm1(outputs1), batch_norm2(outputs2)

    if args.block_dropout > 0 and train:
        with torch.no_grad():
            n = np.random.randint(args.n_channel_convolution // args.block_dropout)
            dropout_mask.fill_(0)
            dropout_mask[0,n*args.block_dropout:(n+1)*args.block_dropout, 0, 0] = 1.
        outputs1 *= dropout_mask
        outputs2 *= dropout_mask

    if args.convolutional_classifier == 0:
        outputs1, outputs2 = outputs1.view(outputs1.size(0),-1), outputs2.view(outputs2.size(0),-1)

    outputs1, outputs2 = classifier1(outputs1), classifier2(outputs2)
    outputs = outputs1 + outputs2

    if args.batch_norm_bottleneck:
        outputs = batch_norm_bottleneck(outputs)

    if args.convolutional_classifier > 0:
        if args.convolutional_loss and train:
            b_size, nc1, h, w = outputs.size()
            outputs = outputs.view(b_size, nc1, -1).transpose(1,2).reshape(b_size*h*w, nc1)
            targets = targets.view(b_size, 1).expand(b_size, h*w).reshape(b_size*h*w)
        elif args.separable_convolution or args.bottleneck_dim > 0:
            outputs = classifier(outputs)
            outputs = F.adaptive_avg_pool2d(outputs, 1)
        else:
            outputs = F.adaptive_avg_pool2d(outputs, 1)
    elif args.bottleneck_dim > 0:
        outputs = classifier(outputs)

    outputs = outputs.view(outputs.size(0),-1)

    return outputs, targets

def create_classifier_blocks(out1, out2, args, params, n_classes):
    batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck =  None, None, None, None, None, None, None

    if args.batch_norm:
        batch_norm1 = nn.BatchNorm2d(out1.size(1)).to(device).float()
        batch_norm2 = nn.BatchNorm2d(out2.size(1)).to(device).float()
        params += list(batch_norm1.parameters()) + list(batch_norm2.parameters())

    if args.batch_norm_bottleneck:
        batch_norm_bottleneck = nn.BatchNorm2d(args.bottleneck_dim).to(device).float()
        params += list(batch_norm_bottleneck.parameters())

    if args.block_dropout > 0:
        dropout_mask = torch.FloatTensor(1, out1.size(1), 1, 1).fill_(0).to(device)

    if args.convolutional_classifier > 0:
        if args.separable_convolution:
            # convolution separable in space channels
            if args.bottleneck_dim > 0:
                classifier = nn.Sequential(
                    nn.Conv2d(args.bottleneck_dim, n_classes, 1).to(device).float(),
                    nn.Conv2d(n_classes, n_classes, args.convolutional_classifier, groups=n_classes).to(device).float()
                )
                classifier1 = nn.Conv2d(out1.size(1), args.bottleneck_dim, 1).to(device).float()
                classifier2 = nn.Conv2d(out2.size(1), args.bottleneck_dim, 1).to(device).float()
            else:
                classifier = nn.Conv2d(n_classes, n_classes, args.convolutional_classifier, groups=n_classes).to(device).float()
                classifier1 = nn.Conv2d(out1.size(1), n_classes, 1).to(device).float()
                classifier2 = nn.Conv2d(out2.size(1), n_classes, 1).to(device).float()
            params += list(classifier.parameters())
        else:
            if args.bottleneck_dim > 0:
                # usual setting
                # classifier = nn.Linear(args.bottleneck_dim, n_classes).to(device).float()
                # params += list(classifier.parameters())
                # classifier1 = nn.Conv2d(out1.size(1), args.bottleneck_dim, args.convolutional_classifier).to(device).float()
                # classifier2 = nn.Conv2d(out2.size(1), args.bottleneck_dim, args.convolutional_classifier).to(device).float()
                # less params
                classifier = nn.Conv2d(args.bottleneck_dim, n_classes, args.convolutional_classifier).to(device).float()
                params += list(classifier.parameters())
                # classifier1 = nn.Conv2d(out1.size(1), args.bottleneck_dim, 1).to(device).float()
                # classifier2 = nn.Conv2d(out2.size(1), args.bottleneck_dim, 1).to(device).float()
                print('no bias in classifiers 1 and 2')
                classifier1 = nn.Conv2d(out1.size(1), args.bottleneck_dim, 1, bias=False).to(device).float()
                classifier2 = nn.Conv2d(out2.size(1), args.bottleneck_dim, 1, bias=False).to(device).float()
            else:
                classifier1 = nn.Conv2d(out1.size(1), n_classes, args.convolutional_classifier).to(device).float()
                classifier2 = nn.Conv2d(out2.size(1), n_classes, args.convolutional_classifier).to(device).float()
    else:
        out1, out2 = out1.view(out1.size(0), -1), out2.view(out1.size(0), -1)
        if args.bottleneck_dim > 0:
            classifier = nn.Linear(args.bottleneck_dim, n_classes).to(device).float()
            params += list(classifier.parameters())
            classifier1 = nn.Linear(out1.size(1), args.bottleneck_dim).to(device).float()
            classifier2 = nn.Linear(out2.size(1), args.bottleneck_dim).to(device).float()
        else:
            classifier1 = nn.Linear(out1.size(1), n_classes).to(device).float()
            classifier2 = nn.Linear(out2.size(1), n_classes).to(device).float()

    params += list(classifier1.parameters()) + list(classifier2.parameters())

    return batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck

def heaviside_half(x, bias):
    return (x > bias).half() - (x < -bias).half()

def heaviside_float(x, bias):
    return (x > bias).float() - (x < -bias).float()

def topk_half(x, k):
    x_abs = torch.abs(x)
    return (x_abs >= x_abs.topk(dim=1, k=k).values.min(dim=1, keepdim=True).values).half() * x.half()

def topk_float(x, k):
    x_abs = torch.abs(x)
    return (x_abs >= x_abs.topk(dim=1, k=k).values.min(dim=1, keepdim=True).values).float() * x.float()

def topk_heaviside_half(x, k):
    x_abs = torch.abs(x)
    return (x_abs >= x_abs.topk(dim=1, k=k).values.min(dim=1, keepdim=True).values).half() * x.sign().half()

def topk_heaviside_float(x, k):
    x_abs = torch.abs(x)
    return (x_abs >= x_abs.topk(dim=1, k=k).values.min(dim=1, keepdim=True).values).float() * x.sign().float()

def topk_heaviside_and_heaviside_half(x, k, bias=args.bias):
    x_abs = torch.abs(x)
    return (x_abs > bias).half() * (x_abs >= x_abs.topk(dim=1, k=k).values.min(dim=1, keepdim=True).values).half() * x.sign().half()

if args.shrink == 'heaviside':
    if args.learn_patches:
        shrink = heaviside_float
    else:
        shrink = heaviside_half
elif args.shrink == 'topk':
    args.bias = int(args.topk_fraction * n_channel_convolution)
    if args.learn_patches:
        shrink = topk_float
    else:
        shrink = topk_half
elif args.shrink == 'topk_heaviside':
    args.bias = int(args.topk_fraction * n_channel_convolution)
    if args.learn_patches:
        shrink = topk_heaviside_float
    else:
        shrink = topk_heaviside_half
elif args.shrink == 'topk_heaviside_and_heaviside':
    args.bias = int(args.topk_fraction * n_channel_convolution)
    shrink = topk_heaviside_and_heaviside_half
elif args.shrink == 'hardshrink':
    shrink = F.hardshrink
elif args.shrink == 'softshrink':
    shrink = F.softshrink

# Define the model
class Net(nn.Module):
    def __init__(self, spatialsize_avg_pooling, stride_avg_pooling, bias=1.0):
        super(Net, self).__init__()
        self.pool_size = spatialsize_avg_pooling
        self.pool_stride = stride_avg_pooling
        self.bias = bias

    def forward(self, x, conv_weight):
        out = F.conv2d(x, conv_weight)
        out = shrink(out, self.bias)
        out1 = F.avg_pool2d(out, [self.pool_size, self.pool_size], stride=[self.pool_stride, self.pool_stride],
                            ceil_mode=True)
        out = F.relu(out, inplace=True)
        out2 = F.avg_pool2d(out, [self.pool_size, self.pool_size], stride=[self.pool_stride, self.pool_stride],
                            ceil_mode=True)
        return out1, out2

criterion = nn.CrossEntropyLoss()

net = Net(spatialsize_avg_pooling, stride_avg_pooling, bias=args.bias).to(device)

kernel_convolution = kernel_convolution.to(device)

x = torch.rand(1, 3, spatial_size, spatial_size).to(device)

if torch.cuda.is_available() and not args.learn_patches:
    net = net.half()
    kernel_convolution = kernel_convolution.half()
    x = x.half()

params = []

if args.no_cudnn:
    torch.backends.cudnn.enabled = False
else:
    cudnn.benchmark = True

if args.multi_classif:
    kernel_convolution_ = kernel_convolution[:args.batchsize_net]
else:
    kernel_convolution_ = kernel_convolution

out1, out2 = net(x, kernel_convolution_)
out1, out2 = out1.float(), out2.float()
print(f'Net output size: out1 {out1.shape[-3:]} out2 {out2.shape[-3:]}')

if args.multi_classif:
    classifier_blocks = [create_classifier_blocks(out1, out2, args, params, n_classes) for i in range(n_channel_convolution // args.batchsize_net)]
else:
    classifier_blocks = create_classifier_blocks(out1, out2, args, params, n_classes)

if n_channel_convolution_scale_1 > 0:
    net_1 = Net(spatialsize_avg_pooling, stride_avg_pooling-1, bias=args.bias).to(device)
    kernel_convolution_1 = kernel_convolution_1.to(device)

    if torch.cuda.is_available() and not args.learn_patches:
        net_1 = net_1.half()
        kernel_convolution_1 = kernel_convolution_1.half()
        x = x.half()

    out1_1, out2_1 = net_1(x, kernel_convolution_1)
    out1_1, out2_1 = out1_1.float(), out2_1.float()

    print(f'Net scale 1 output size: out1 {out1_1.shape[-3:]} out2 {out2_1.shape[-3:]}')
    batch_norm1_1, batch_norm2_1, classifier1_1, classifier2_1, classifier_1, dropout_mask_1, batch_norm_bottleneck = create_classifier_blocks(out1_1, out2_1, args, params, n_classes)

print(f'Parameters shape {[param.shape for param in params]}')
print(f'N parameters : {sum([np.prod(list(param.shape)) for param in params])/1e6} millions')

del x, out1, out2

# if args.batch_norm:
    # batch_norm1.eval()
    # batch_norm2.eval()
    # if n_channel_convolution_scale_1 > 0:
        # batch_norm1_1.eval()
        # batch_norm2_1.eval()

if args.batchsize_net > 0:
    kernel_convolution = [kernel_convolution[i*args.batchsize_net:(i+1)*args.batchsize_net] for i in range(args.n_channel_convolution // args.batchsize_net)]
else:
    kernel_convolution = [kernel_convolution]

if torch.cuda.is_available() and not args.learn_patches and not args.no_jit:
    print('optimizing net execution with torch.jit')
    with torch.no_grad():
        trial = torch.rand(args.batchsize//n_gpus, 3, spatial_size, spatial_size).to(device).half()

        inputs = {'forward': (trial, kernel_convolution[0])}
        with torch.jit.optimized_execution(True):
            net = torch.jit.trace_module(net, inputs, check_trace=False, check_tolerance=False)
        del inputs
        del trial

        if n_channel_convolution_scale_1 > 0:
            trial_1 = torch.rand(args.batchsize//n_gpus, 3, spatial_size, spatial_size).to(device).half()
            inputs_1 = {'forward': (trial_1, kernel_convolution_1)}
            with torch.jit.optimized_execution(True):
                net_1 = torch.jit.trace_module(net_1, inputs_1, check_trace=False, check_tolerance=False)
            del inputs_1
            del trial_1

if args.multigpu and n_gpus > 1:
    print(f'{n_gpus} available, using Dataparralel for net')
    net = nn.DataParallel(net)

batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck = classifier_blocks
batch_norm1.eval()
batch_norm2.eval()

def train(epoch):
    net.train()
    if n_channel_convolution_scale_1 > 0:
        net_1.train()
    if not args.multi_classif:
        batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck = classifier_blocks
        if args.batch_norm:
            batch_norm1.train()
            batch_norm2.train()
            if n_channel_convolution_scale_1 > 0:
                batch_norm1_1.train()
                batch_norm2_1.train()
        if args.batch_norm_bottleneck:
            batch_norm_bottleneck.train()

    train_loss = 0
    total=0
    correct=0

    for batch_idx, (inputs, targets) in enumerate(trainloader):
        inputs, targets = inputs.to(device), targets.to(device)
        with torch.no_grad():
            if torch.cuda.is_available():
                inputs = inputs.half()
            if len(kernel_convolution) > 1:
                if args.multi_classif:
                    with torch.enable_grad():
                        for i in range(len(kernel_convolution)):
                            batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck = classifier_blocks[i]
                            if args.batch_norm:
                                batch_norm1.train(), batch_norm2.train()
                                outputs1, outputs2 = net(inputs, kernel_convolution[i])
                            outputs1 = outputs1.float()
                            outputs2 = outputs2.float()
                            optimizer.zero_grad()

                            outputs, targets = compute_classifier_outputs(outputs1, outputs2, targets, args, batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck, train=True)

                            loss = criterion(outputs, targets)
                            if args.lambda_1 > 0.:
                                group_sparsity_norm = torch.norm(torch.cat([classifier1.weight, classifier2.weight], dim=0), dim=0, p=2).mean()
                                loss += args.lambda_1 * group_sparsity_norm
                            loss.backward()
                            optimizer.step()
                            train_loss += loss.item() / len(kernel_convolution)
                        total += 1
                        continue
                else:
                    outputs = []
                    for i in range(len(kernel_convolution)):
                        outputs.append(net(inputs, kernel_convolution[i]))
                    outputs1 = torch.cat([out[0] for out in outputs], dim=1)
                    outputs2 = torch.cat([out[1] for out in outputs], dim=1)
                    del outputs
            else:
                outputs1, outputs2 = net(inputs, kernel_convolution[0])

            outputs1 = outputs1.float()
            outputs2 = outputs2.float()

            if n_channel_convolution_scale_1 > 0:
                outputs1_1, outputs2_1 = net_1(inputs, kernel_convolution_1)
                outputs1_1 = outputs1_1.float()
                outputs2_1 = outputs2_1.float()

        optimizer.zero_grad()

        outputs, targets = compute_classifier_outputs(outputs1, outputs2, targets, args, batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck, train=True)

        if n_channel_convolution_scale_1 > 0:
            outputs_1, _ = compute_classifier_outputs(outputs1_1, outputs2_1, targets, args, batch_norm1_1, batch_norm2_1, classifier1_1, classifier2_1, classifier_1, dropout_mask_1, batch_norm_bottleneck, train=True)
            outputs += outputs_1

        loss = criterion(outputs, targets)
        if args.lambda_1 > 0.:
            group_sparsity_norm = torch.norm(torch.cat([classifier1.weight, classifier2.weight], dim=0), dim=0, p=2).mean()
            loss += args.lambda_1 * group_sparsity_norm
        loss.backward()
        optimizer.step()

        if torch.isnan(loss):
            return False
        train_loss += loss.item()
        _, predicted = outputs.max(1)
        total += targets.size(0)
        correct += predicted.eq(targets).sum().item()

        if args.learn_patches:
            with torch.no_grad():
                kernel_convolution[0].data /= kernel_convolution[0].data.view(args.n_channel_convolution, -1).norm(p=2, dim=1).view(args.n_channel_convolution, 1, 1, 1)

        progress_bar(batch_idx, len(trainloader), 'Train, epoch: %i; Loss: %.3f | Acc: %.3f%% (%d/%d) ; threshold %.3f' % (
            epoch, train_loss / (batch_idx + 1), 100. * correct / total, correct, total, args.bias), hide=args.no_progress_bar)
    optimizer.swap_swa_sgd()

    if args.no_progress_bar:
        print('Train, epoch: {}; Loss: {:.2f} | Acc: {:.1f} ; threshold {:.3f}'.format(
            epoch, train_loss / (batch_idx + 1), 100. * correct / total, args.bias))
    return True

def test(epoch, loader=testloader, msg='Test'):
    global best_acc
    net.eval()
    if n_channel_convolution_scale_1 > 0:
        net_1.eval()
    if not args.multi_classif:
        batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck = classifier_blocks
        if args.batch_norm:
            batch_norm1.eval()
            batch_norm2.eval()
            if n_channel_convolution_scale_1 > 0:
                batch_norm1_1.eval()
                batch_norm2_1.eval()
        if args.batch_norm_bottleneck:
            batch_norm_bottleneck.eval()

    test_loss = 0
    correct_top1, correct_top5 = 0, 0
    total = 0
    outputs_list = []
    with torch.no_grad():
        for batch_idx, (inputs, targets) in enumerate(loader):
            inputs, targets = inputs.to(device), targets.to(device)
            if not args.learn_patches and torch.cuda.is_available:
                inputs = inputs.half()
            if len(kernel_convolution) > 1:
                outputs = []
                for i in range(len(kernel_convolution)):
                    outputs.append(net(inputs, kernel_convolution[i]))
                if args.multi_classif:
                    outs = 0
                    for i in range(len(kernel_convolution)):
                        batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck = classifier_blocks[i]
                        batch_norm1.eval(), batch_norm2.eval()
                        outs += compute_classifier_outputs(outputs[i][0].float(), outputs[i][1].float(), targets, args, batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck, train=False)[0]
                    outputs = outs
                else:
                    outputs1 = torch.cat([out[0] for out in outputs], dim=1)
                    outputs2 = torch.cat([out[1] for out in outputs], dim=1)
                    del outputs
            else:
                outputs1, outputs2 = net(inputs, kernel_convolution[0])
            if not args.multi_classif:
                outputs1 = outputs1.float()
                outputs2 = outputs2.float()

                if n_channel_convolution_scale_1 > 0:
                    outputs1_1, outputs2_1 = net_1(inputs, kernel_convolution_1)
                    outputs1_1 = outputs1_1.float()
                    outputs2_1 = outputs2_1.float()

                outputs, targets = compute_classifier_outputs(outputs1, outputs2, targets, args, batch_norm1, batch_norm2, classifier1, classifier2, classifier, dropout_mask, batch_norm_bottleneck, train=False)

                if n_channel_convolution_scale_1 > 0:
                    outputs_1, _ = compute_classifier_outputs(outputs1_1, outputs2_1, targets, args, batch_norm1_1, batch_norm2_1, classifier1_1, classifier2_1, classifier_1, dropout_mask_1, batch_norm_bottleneck, train=False)
                    outputs += outputs_1

            loss = criterion(outputs, targets)

            outputs_list.append(outputs)

            test_loss += loss.item()
            cor_top1, cor_top5 = correct_topk(outputs, targets, topk=(1, 5))
            correct_top1 += cor_top1
            correct_top5 += cor_top5
            _, predicted = outputs.max(1)
            total += targets.size(0)
            progress_bar(batch_idx, len(loader),
                         'Test, epoch: %i; Loss: %.3f | Acc: %.3f%% (%d/%d)' % (epoch, test_loss / (batch_idx + 1),
                                                            100. * correct_top1 / total, correct_top1, total), hide=args.no_progress_bar)
        test_loss /= (batch_idx + 1)
        acc1, acc5 = 100. * correct_top1 / total, 100. * correct_top5 / total
        if args.no_progress_bar:
            print(f'{msg}, epoch: {epoch}; Loss: {test_loss:.2f} | Acc: {acc1:.1f} @1 {acc5:.1f} @5 ; threshold {args.bias:.3f}')
        outputs = torch.cat(outputs_list, dim=0).cpu()
        if args.lambda_1 > 0.:
            group_sparsity_norm = torch.norm(torch.cat([classifier1.weight, classifier2.weight], dim=0), dim=0, p=2)
            print(f'non_zero groups {(group_sparsity_norm != 0).int().sum()}')

        return acc1, outputs

hashname = hashlib.md5(str.encode(json.dumps(vars(args), sort_keys=True))).hexdigest()
if args.save_model:
    checkpoint_dir = f'checkpoints/{args.dataset}_{args.n_channel_convolution}patches_{args.spatialsize_convolution}x{args.spatialsize_convolution}/{args.optimizer}_{args.lr_schedule}'
    if not os.path.exists(checkpoint_dir):
        os.makedirs(checkpoint_dir)
    checkpoint_file = os.path.join(checkpoint_dir, f'{hashname}.pth.tar')
    print(f'Model will be saved at file {checkpoint_file}.')

    state = {'args': args}
    if os.path.exists(checkpoint_file):
        state = torch.load(checkpoint_file)

start_epoch = 0
if args.resume:
    state = torch.load(args.resume)
    start_epoch = state['epoch'] + 1
    print(f'Resuming from file {args.resume}, start epoch {start_epoch}...')
    if start_epoch not in learning_rates:
        closest_i = max([i for i in learning_rates if i <= start_epoch])
        if args.optimizer == 'Adam':
            optimizer = optim.Adam(params, lr=learning_rates[closest_i], weight_decay=args.weight_decay)
        elif args.optimizer == 'SGD':
            optimizer = optim.SGD(params, lr=learning_rates[closest_i], momentum=args.sgd_momentum, weight_decay=args.weight_decay)
        optimizer.load_state_dict(state['optimizer'])

    for block, name in zip(classifier_blocks, ['bn1','bn2','cl1', 'cl2', 'cl', 'bnb']):
        if name in state and block is not None:
            block.load_state_dict(state[name])

    acc, outputs = test(-1)

start_time = time.time()
best_test_acc, best_epoch = 0, -1
for i in range(start_epoch, args.nepochs):
    if i in learning_rates:
        print('new lr:'+str(learning_rates[i]))
        if args.optimizer == 'Adam':
            optimizer = optim.Adam(params, lr=learning_rates[i], weight_decay=args.weight_decay)
            optimizer = torchcontrib.optim.SWA(optimizer, swa_start=10, swa_freq=5)
        elif args.optimizer == 'SGD':
            optimizer = optim.SGD(params, lr=learning_rates[i], momentum=args.sgd_momentum, weight_decay=args.weight_decay)
            optimizer = torchcontrib.optim.SWA(optimizer, swa_start=10, swa_freq=5)
        else:
            raise NotImplementedError('optimizer {} not implemented'.format(args.optimizer))
    no_nan_in_train_loss = train(i)
    if not no_nan_in_train_loss:
        print(f'Epoch {i}, nan in loss, stopping training')
        break
    test_acc, outputs = test(i)

    if test_acc > best_test_acc:
        print(f'Best acc ({test_acc}).')
        best_test_acc = test_acc
        best_epoch = i

    if args.save_model :
        print(f'saving...')
        state.update({
            'optimizer': optimizer.state_dict(),
            'epoch': i,
            'acc': test_acc,
            'outputs': outputs,
        })

        for block, name in zip(classifier_blocks, ['bn1','bn2','cl1', 'cl2', 'cl', 'bnb']):
            if block is not None:
                state.update({
                    name: block.state_dict()
                })

        if best_epoch == i:
            state.update({
                'best_epoch': i,
                'best_acc': test_acc,
                'best_outputs': outputs,
            })
            for block, name in zip(classifier_blocks, ['best_bn1','best_bn2','best_cl1', 'best_cl2', 'best_cl', 'best_bnb']):
                if block is not None:
                    state.update({
                        name: block.state_dict()
                    })
        torch.save(state, checkpoint_file)

print(f'Best test acc. {best_test_acc}  at epoch {best_epoch}/{i}')
hours = (time.time() - start_time) / 3600
print(f'Done in {hours:.1f} hours with {n_gpus} GPU')


from datetime import datetime, timezone, timedelta

from allauth.account.models import EmailAddress
from celery import shared_task
from django.contrib.auth.models import User
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string

from NewsPaper.settings import MY_MAIL

from .models import Post

@shared_task
def mail_notify_new_post(msg_data):
    html_content = render_to_string(
        'notify_new_post.html',
        {
            'msg_data': msg_data,
        }
    )
    msg = EmailMultiAlternatives(
        subject=f'Уведомление о новой публикации',
        body=f'Новая публикация в вашем любимом разделе',
        from_email=MY_MAIL,
        to=[msg_data['subscriber_email'], ],
    )
    msg.attach_alternative(html_content, "text/html")
    print(f'Отправка уведомления для: {msg_data["subscriber_email"]}')
    msg.send()

@shared_task
def weekly_notify_posts():
    print('Старт создания еженедельника')
    last_week = datetime.now(timezone.utc) - timedelta(days=7)
    users = User.objects.all()
    t = 17
    for user in users:
        msg_data = {}
        posts = Post.objects.filter(category__subscribers=user, time__gt=last_week).values('id').exists()
        if posts:
            subscriber_email = user.email
            if subscriber_email:
                if EmailAddress.objects.filter(email=subscriber_email).exists():
                    if EmailAddress.objects.get(email=subscriber_email).verified:
                        msg_data['last_week'] = last_week
                        msg_data['subscriber_name'] = user.username
                        msg_data['subscriber_email'] = subscriber_email
                        print(f'{t} Создание еженедельника для: {msg_data["subscriber_email"]}')
                        mail_weekly_notify_posts.apply_async([msg_data], countdown=t)
                        t += 17

@shared_task
def mail_weekly_notify_posts(msg_data):
    user = msg_data['subscriber_name']
    last_week = msg_data['last_week']
    posts = Post.objects.filter(
        category__subscribers__username=user,
        time__gt=last_week
    ).values('id', 'title', 'time').distinct()
    msg_data['posts'] = posts
    html_content = render_to_string(
        'weekly_notify_posts.html',
        {
            'msg_data': msg_data,
        }
    )
    msg = EmailMultiAlternatives(
        subject=f'Публикации за неделю',
        body=f'Новые публикация в вашем любимом разделе',
        from_email=MY_MAIL,
        to=[msg_data['subscriber_email'], ],
    )
    msg.attach_alternative(html_content, "text/html")
    print(f" Отправка еженедельника для: {msg_data['subscriber_email']}")
    msg.send()


#Here are my exercises for the List Comprehension study group.

'''
Bonus Question: Here is a baby's first version of calculating Factorial with a FOR loop.
The function "factorialize" takes an integer n and returns n!.

Rewrite the factorialize function so that it uses a list comprehension.
You might need to use a reduce.

'''

def factorialize(n):
	n_factorial = 1
	for i in range(1,n+1):
		n_factorial=n_factorial*i
	return n_factorial

'''
Solution:
def factorialize(n):
	return reduce(lambda x,y: x*y, [i for i in range (1, n+1)])
'''


num = int(input())
my = list(map(int, input().split()))

# min = my[0]
# max = my[0]
#
# for i in my[1:]:
#     if i > max:
#         max = i
#     elif i < min:
#         min = i
# print(min, max)

print(min(my), max(my))


# -*- coding:UTF-8 -*-
import cv2
import dlib
import math
from imutils import face_utils
import time
import pandas as pd
from pandas import DataFrame
from PIL import Image
from PIL.ExifTags import TAGS

"""setDLib"""
p = 'shape_predictor_68_face_landmarks.dat'
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(p)

def get_pitures(i):
    # 构造文件路径
    filepath = './face/netlab_530-' + str(i) + '.jpg'
    print(filepath)
    image = cv2.imread(filepath)
    img = Image.open(filepath)
    ret = {}
    if hasattr(img,'_getexif'):
        exifinfo = img._getexif()
        if exifinfo != None:

            for tag, value in exifinfo.items():
                decoded = TAGS.get(tag, tag)

                ret[decoded] = value
    return (image,exifinfo)

# 获取脸部特征点（68个）
def get_shape(gray, rect):
    # determine the facial landmarks for the face region, then
    # convert the facial landmark (x, y)-coordinates to a NumPy
    # array
    shape = predictor(gray, rect)
    shape = face_utils.shape_to_np(shape)
    return shape

# 判断睁眼
def testEye(landmark):
    # 左眼眼角
    point36 = landmark[36]
    # 有眼眼角
    point45 = landmark[45]
    # 计算眼间距
    eyeDistance = math.sqrt(math.pow((point36[0] - point45[0]), 2) + math.pow((point36[1] - point45[1]), 2))

    # 计算眼面积
    pointsRight = [landmark[36], landmark[37], landmark[38], landmark[39], landmark[40], landmark[41]]
    pointsLeft = [landmark[42], landmark[43], landmark[44], landmark[45], landmark[46], landmark[47]]
    areaRight = eyeArea(pointsRight)
    areaLeft = eyeArea(pointsLeft)
    temp = (areaRight + areaLeft) / (math.pow(eyeDistance, 2))
    return temp

# 求眼睛面积
def eyeArea(points):
    point0 = points[0]
    point1 = points[1]
    point2 = points[2]
    point3 = points[3]
    point4 = points[4]
    point5 = points[5]

    tri0 = triangleArea(point0, point1, point5)
    tri1 = triangleArea(point1, point5, point2)
    tri2 = triangleArea(point5, point2, point4)
    tri3 = triangleArea(point2, point4, point3)

    triArea = tri0 + tri1 + tri2 + tri3
    return triArea

# 求三角形面积
def triangleArea(point0, point1, point2):
    # a = 1.0/2.0(x1*(y2-y3)+x2*(y3-y1)+x3*(y1-y2))
    a1 = point0[0] * (point1[1] - point2[1])
    a2 = point1[0] * (point2[1] - point0[1])
    a3 = point2[0] * (point0[1] - point1[1])

    a = abs(0.5 * (a1 + a2 + a3))
    # print(a)
    return a

# 获取专注度
def get_focus(shape):
    result = testEye(shape)
    return result

# 测试每一个人
def test_pre(emotions, focus):
    attention = 0
    get = 0
    if (focus > 0.018):
        attention = 1
    if (len(emotions) > 0):
        anger = emotions['anger']
        contempt = emotions['contempt']
        disgust = emotions['disgust']
        fear = emotions['fear']
        happiness = emotions['happiness']
        neutral = emotions['neutral']
        sadness = emotions['sadness']
        surprise = emotions['surprise']
        e = [anger, contempt, disgust, fear, happiness, neutral, sadness, surprise]
        emotion = e.index(max(e))
        if (attention == 1 and (emotion == 4 or emotion == 5)):
            get = 1
    return (attention, get)

if __name__ == '__main__':
    print('开始检测')

    # 打开Excel文件
    data = pd.read_excel('result.xlsx', sheetname=0)
    temp = 0

    for temp in range(0, 50):
        # 开始获取图片时间戳
        time_get_begin = time.time()
        print('begin:', time_get_begin)

        total = 0
        listen = 0
        understand = 0

        # 获取图片，图片里有时间、教室信息
        image, exif = get_pitures(temp)
        gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        print('exif:', exif)
        print(exif[256])
        print(exif[257])
        # resolution = (exif[])

        rects = detector(gray, 1)
        print('识别人脸数:' + str(len(rects)))

        # 总人数total
        total = len(rects)
        # loop over the face detections每张人脸
        for (i, rect) in enumerate(rects):
            # print('检测到人脸:')
            l = rect.left()
            r = rect.right()
            t = rect.top()
            b = rect.bottom()
            # print(l, r, t, b)
            roiImage = image[t:b, l:r]
            # cv2.imwrite('sss.jpg', roiImage)

            # 获取脸部特征点
            shape = get_shape(image, rect)
            # 获取专注度
            focus = get_focus(shape)
            print('focus:' + str(focus))

        # 结束的时候获取时间戳
        time_get_end = time.time()
        print('end:', time_get_end)
        print(time_get_end - time_get_begin)

        # 把记录下来是数据写入Excel文件
        data['编号'][temp] = temp
        data['帧开始时间'][temp] = time_get_begin
        data['帧结束时间'][temp] = time_get_end
        data['帧处理时间'][temp] = time_get_end - time_get_begin
        temp = temp + 1
        # 将更新写到新的Excel中
        DataFrame(data).to_excel('ji.xlsx')


#!/usr/bin/env python3
# Project Euler problem __
# Explanation ...

import sys

from functions import cast_number
from my_random import MyRNG

def static_test():
    if "x" not in static_test.__dict__:
        static_test.x = 0
    static_test.x += 1
    print(static_test.x)

def main(args):
    # arg_f(argument_list, index, validity_function, default_value)
    arg_f = lambda a, i, f, d: f(a[i]) if len(a)>i and f(a[i]) != None else d
    ARGUMENT1 = arg_f(args,1,cast_number,"default 1")
    ARGUMENT2 = arg_f(args,2,cast_number,"default 2")
    ARGUMENT3 = arg_f(args,3,cast_number,"default 3")
    r = MyRNG()
    for x in range(500):
        i = r.rand_range(365)
        print(f"{i} {'Winning ****' if i == 0 or i == 364 else ''}")

main(sys.argv)


t = int(input())
for i in range(t):
    n = int(input())
    li = [int(x) for x in input().split()]
    even = []
    odd = []
    for i in li:
        if i%2==0:
            even.append(i)
        else:
            odd.append(i)
    even.extend(odd)
    print(*even,end=" \n")


#!/usr/bin/env python3

# Basic
from random import shuffle

class Phrases(object):
    SEPARATOR = '-  -  -  -  -  -  -  -  -  -'

class Question(object):
    ICONS_CHOICES = (
        'choice_1',
        'choice_2',
        'choice_3',
        'choice_4'
    )
    ICON_FINISH = 'end_question'
    ICON_CORRECT = 'choice_correct'
    ICON_INCORRECT = 'choice_incorrect'
    POINTS_CORRECT = 3
    POINTS_INCORRECT = -1

    def __init__(self, question, answers, user_request, emojis):
        self.question = question
        self.answers = answers
        self.player_request = user_request.mention
        self.emojis = emojis
        self.player_scores = dict()
        self.answers_shuffled = None
        self.index_correct = None
        self.shuffle_answers()
        self.content = None
        self.make_content()
        self.message = None

    def get_guild_id(self):
        if self.message:
            return self.message.guild.id
        return None

    def shuffle_answers(self):
        indices = list(range(len(self.answers)))
        shuffle(indices)
        self.index_correct = indices.index(0)
        self.answers_shuffled = [self.answers[i] for i in indices]

    async def process_reaction(self, reaction, user):
        player = user.mention
        icon = reaction.emoji
        if icon.name == self.ICON_FINISH:
            return await self.finish(player)
        elif icon.name in self.ICONS_CHOICES:
            self.set_player_score(player, icon)

    def set_player_score(self, player, icon):
        # Only first answer counts
        if player in self.player_scores:
            return
        # Check if legal choice
        index_choice = self.ICONS_CHOICES.index(icon.name)
        if index_choice >= len(self.answers):
            return
        # Check answer
        if index_choice == self.index_correct:
            self.player_scores[player] = self.POINTS_CORRECT
        else:
            self.player_scores[player] = self.POINTS_INCORRECT

    async def finish(self, player=None):
        # Only requesting player can finish
        if player and player != self.player_request:
            return
        # Requesting player gets incorrect if not chosen answer
        if self.player_request not in self.player_scores:
            self.player_scores[self.player_request] = self.POINTS_INCORRECT
        # Show results
        self.update_content()
        await self.send()
        return self.player_scores

    def make_content(self):
        # Format question and answers
        line_quiz = f'{self.emojis["buzz"]} **Quiz time!** {self.question}'
        lines_answers = []
        # Indicate choices with colour icons
        for i, answer in enumerate(self.answers_shuffled):
            icon = self.emojis[self.ICONS_CHOICES[i]]
            lines_answers.append(f'{icon}  {answer}')
        # Collect content lines
        lines = [line_quiz] + lines_answers
        self.content = '\n'.join(lines)

    def update_content(self):
        # Format question and answers
        line_quiz = f'{self.emojis["buzz"]} **Quiz time!** {self.question}'
        lines_answers = []
        # Indicate choices correctness with icons
        for i, answer in enumerate(self.answers_shuffled):
            if i == self.index_correct:
                icon = self.emojis[self.ICON_CORRECT]
            else:
                icon = self.emojis[self.ICON_INCORRECT]
            lines_answers.append(f'{icon}  {answer}')
        # Summarise quiz results
        winners = [p for p, s in self.player_scores.items() if s > 0]
        losers = [p for p, s in self.player_scores.items() if s <= 0]
        line_losers = f'` {self.POINTS_INCORRECT:+1d} pts ` {", ".join(losers)}'
        line_winners = f'` {self.POINTS_CORRECT:+1d} pts ` {", ".join(winners)}'
        # Collect appropriate content lines
        lines = [line_quiz] + lines_answers + [Phrases.SEPARATOR]
        if winners:
            lines.append(line_winners)
        if losers:
            lines.append(line_losers)
        self.content = '\n'.join(lines)

    async def send(self, ctx=None):
        if self.message:
            # Message exists, update with new content
            await self.message.edit(content=self.content)
            # Remove all reactions
            await self.message.clear_reactions()
        else:
            # No message exists yet
            assert ctx, "No context was provided."
            self.message = await ctx.send(self.content)
            # Add reactions
            for i in range(len(self.answers)):
                icon = self.emojis[self.ICONS_CHOICES[i]]
                await self.message.add_reaction(icon)
            await self.message.add_reaction(self.emojis[self.ICON_FINISH])
        return self.message.id


import pyttsx3
import speech_recognition as sr

engine = pyttsx3.init('sapi5')
voices = engine.getProperty('voices')
print(voices)
engine.setProperty('voice', voices[0].id)

def speak(audio):
    engine.say(audio)
    engine.runAndWait()

def takeCommand():
    r = sr.Recognizer()
    with sr.Microphone() as source:
        print("Listening...")
        r.pause_threshold = 1
        print("speak_now...")
        audio = r.listen(source)
        print("Done: voice detected.")

    try:
        print("Recognizing: Converting your voice into text.")
        query = r.recognize_google(audio, language='en-in')
        print(f"You said: , {query}\n")

    except Exception as e:
        speak("Say that again please...")
        print(e)
        return "None"
    return query

if __name__ == "__main__":
    speak("Vision is here.")
    while True:
        query = takeCommand().lower()


time=int(input())
while(time>0):
    time-=1
    strr=input()
    listt=strr.split()
    num=int(listt[0])
    step=int(listt[1])

    alist=[x+1 for x in range(num)]
    index=0
    while (len(alist)>1):
        index=(index+step-1)%len(alist)  # 列表的索引
        alist.pop(index)
    print(alist[0])


import requests as req
import json

link = 'https://base-login-112c0-default-rtdb.firebaseio.com/clientes'

# faz a leitura dos dados do banco de dados
def read_data():
    global link
    requisicao = req.get(f'{link}/.json')
    dic = requisicao.json()
    return dic

# deleta um usuário
def del_user(user: str):
    global link
    requisicao = req.delete(f'{link}/{user}/.json', )
    return requisicao

# adiciona um usuario
def add_user(cads: list):
    global link
    if len(cads) == 2:
        dic = {'email':cads[0], 'senha':cads[1]}
        requisicao = req.post(f'{link}/.json', json.dumps(dic))
        return requisicao
    else:
        raise ValueError('Quantidade de dados inválidos')

# altera a senha de um usuario
def alter(user: str, mud: dict):
    global link
    requisicao = req.patch(f'{link}/{user}/.json', json.dumps(mud))
    return requisicao

def users():
    global link
    dic = read_data()
    user_list = [user for user in dic]
    return user_list

def busq(user: str, item: str = 'all'):
    dic = read_data()
    if item == 'email' or item == 'senha':
        return dic[user][item]
    else:
        if item == 'all':
            return dic[user]
        else:
            raise ValueError('Esse valor não existe no dicionário')

if __name__ == '__main__':
    alter('-N07_mfkXG2CoQBVI5mt', {'senha':321})
    dic = busq('-N07_mfkXG2CoQBVI5mt', item='senha')
    print(dic)


# url: http://rosalind.info/problems/revc/

# Problem

# In DNA strings, symbols 'A' and 'T' are complements of each other, as
# are 'C' and 'G'.

# The reverse complement of a DNA string s is the string sc formed by
# reversing the symbols of s, then taking the complement of each symbol
# (e.g., the reverse complement of "GTCA" is "TGAC").

# Given: A DNA string s of length at most 1000 bp.

# Return: The reverse complement sc of s.

from common import rev_complement

if __name__ == "__main__":
    file1, file2 = "inputs/REVC_input.txt", "outputs/REVC_output.txt"
    with open(file1, "r") as f1, open(file2, "w") as f2:
        dna_seq = f1.read().strip()
        f2.write(rev_complement(dna_seq))


import cv2, os
import numpy as np
from PIL import Image

cascadePath = "haarcascade_frontalface_default.xml";
faceCascade = cv2.CascadeClassifier(cascadePath);

path = 'photo' #absolute path

#def get_images_and_labels(path):
image_paths = [os.path.join(path, f) for f in os.listdir(path)]
images = []
labels = []
for image_path in image_paths:
                image_pil = Image.open(image_path).convert('L')
                image = np.array(image_pil, 'uint8')
                print (int(os.path.split(image_path)[1].split(".")[0].replace("Subject","")))
                nbr = int(os.path.split(image_path)[1].split(".")[0].replace("Subject", ""))
#                faces = faceCascade.detectMultiScale(image)
#                for (x, y, w, h) in faces:
#                        images.append(image[y: y + h, x: x + w])
#                        labels.append(nbr)
#                        cv2.imshow("Adding faces to traning set...", image[y: y + h, x: x + w])
#                        cv2.waitKey(50)
#        return images, labels


from pynput.keyboard import Key, Controller
from time import sleep
keyboard = Controller()
sleep(5)
for i in range(10):
    keyboard.type("Romain, je suis ton père")
    keyboard.press(Key.enter)
    sleep(0.1)
    keyboard.release(Key.enter)
    sleep(1)


import datetime
import uuid

import jwt
from django.core.management.base import BaseCommand
from django.utils.crypto import get_random_string

from venueless.core.models import World

class Command(BaseCommand):
    help = "Create a new world"

    def handle(self, *args, **options):
        w = World()
        while True:
            v = input("Enter the internal ID for the new world (alphanumeric): ")
            if v.strip() and v.strip().isalnum():
                if World.objects.filter(id=v.strip()).exists():
                    print("This world already exists.")
                w.id = v
                break

        w.title = input("Enter the title for the new world: ")
        w.domain = input(
            "Enter the domain of the new world (e.g. myevent.example.org): "
        )

        secret = get_random_string(length=64)
        w.config = {
            "JWT_secrets": [
                {
                    "issuer": "any",
                    "audience": "venueless",
                    "secret": secret,
                }
            ]
        }
        w.save()

        print("World created.")
        print("Default API key secrets:", w.config["JWT_secrets"])

        print("Admin url:")
        iat = datetime.datetime.utcnow()
        exp = iat + datetime.timedelta(days=365)
        payload = {
            "iss": "any",
            "aud": "venueless",
            "exp": exp,
            "iat": iat,
            "uid": str(uuid.uuid4()),
            "traits": ["admin"],
        }
        token = jwt.encode(payload, secret, algorithm="HS256")
        print(f"https://{w.domain}/#token={token}")