#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" Evaluates a specific tourney-based and pool-based active learning (AL)
    algorithm.

    WARNNING (March, 2016): This code is obsolete. It does not work properly
    at this time, and must be updated to fit with the last version of the AL
    scripts.

    Author: JCS, Sep. 2014
"""

# External modules
import numpy as np
import sys
import matplotlib
import matplotlib.pyplot as plt
import sklearn.datasets as skd           # Needs version 0.14 or higher
import pickle

# Path to the required folders from the REDES project
sys.path.append('./b2c')
sys.path.append('./webdetection')
sys.path.append('./activelearning')
sys.path.append('./classifier')

# redes modules
import b2c_utils as b2c
# import webdetection_utils.load_data as wbdload_data
import classifier_utils as classifier

# My modules
import utils_jcs as jcs
import activelearner as al
# import classifier_JCS as clold


###############################################################################
# ## MAIN #####################################################################
###############################################################################

###########################
### Configurable parameters

# Type of data: Two options:
#    'syn'  : Synthetic data are used.
#    'b2c'  : Data are taken from task B (B2C detection) of red.es project
#    'jobs' : Data are taken from task A1 (jobs) of red.es project.
#    'sklearn' : Data generated by make_
datatype = 'syn'

if datatype == 'syn':
    # Parameters for synthetic data
    syn_dim = 2   # Data dimension
    syn_n = 500   # Sample size

elif datatype == 'b2c':
    # Parameters for b2c
    b2c_base_fname = 'b2c_2014_09'
    b2c_type_FS = 'wtfidf'
    b2c_datapath = './results/b2c/'
    num_features = 1000

elif datatype == 'jobs':
    # Parameter for jobs
    jobs_base_fname = 'webdetection'
    jobs_type_FS = 'bagged'
    jobs_datapath = './temp/'  # in g2pi/.../results/webdetection/temp/
    num_features = 1000

elif datatype == 'sklearn':
    # Parameters for sklearn synthetic data
    skl_n = 200    # Sample size

# Common parameters for all AL algorithms
threshold = 0
pool_size = 5  # No. labels requested at each pool of Active Learner
n_sim = 100    # No. of simulation runs to average

# Type of classifier: 'logreg', 'svm' or 'mindist'
type_classif = 'logreg'

# Parameters for the 'random' AL algorithm (i.e., non-AL)
# al_params = [threshold]

# Best AL. This are the parameters of the best algorithm I have found up to
# date.
best_AL = 'greedy'    # Best algorithm
best_ts = 40           # Tourney size


####################
### A title to start

print "======================="
print "    Active Learning"
print "======================="

###############################################################################
### PART I: Load data (samples and true labels)                             ###
###############################################################################

if datatype in {'b2c', 'jobs'}:

    ########################
    ### Load data from files

    # Get names of required files and paths
    if datatype == 'b2c':

        base_fname = b2c_base_fname
        type_FS = b2c_type_FS
        datapath = b2c_datapath

        # Read features and labels
        x_all, y_all, doc_row = b2c.load_data(
            datapath, base_fname, type_FS, num_features)

    elif datatype == 'jobs':

        base_fname = jobs_base_fname
        type_FS = jobs_type_FS
        datapath = jobs_datapath

        # Read features and labels
        x_all, y_all, doc_row = b2c.load_data(
            datapath, base_fname, type_FS, num_features)

    # Check if data dimensions are consistent
    ny = len(y_all)
    nx, dim = x_all.shape

    if nx != ny:
        sys.exit(u'Error: el número de datos y el de etiquetas no coinciden')

    # Remove samples with other labels than +1, -1
    which_train = abs(y_all) == 1
    idx = np.arange(y_all.shape[0])
    x = x_all[idx[which_train], :]
    true_labels = y_all[idx[which_train]]
    # true_labels = [z         for z in y_all     if abs(z)==1]
    # x           = [x_all[k,] for k in range(ny) if abs(y_all[k])==1]

elif datatype == 'syn':

    ###############################
    ### Generate artificial dataset

    # Generate true labels that are consistent with the known ones
    Labels = np.zeros(syn_n)
    true_labels = jcs.generate_true_labels(Labels)

    # Generate feature vectors
    x = jcs.generate_samples(true_labels, syn_dim)

else:  # datatype == 'sklearn':

    x, true_labels = skd.make_classification(
        n_samples=skl_n, n_features=20, n_informative=2, n_redundant=0,
        n_repeated=0, n_classes=2, n_clusters_per_class=2, weights=None,
        flip_y=0.0001, class_sep=1.0, hypercube=True, shift=0.0, scale=1.0,
        shuffle=True, random_state=None)

    # Default values, just for reference:
    # x,true_labels = skd.make_classification(n_samples=n, n_features=20,
    #     n_informative=2, n_redundant=2, n_repeated=0, n_classes=2,
    #     n_clusters_per_class=2, weights=None, flip_y=0.01, class_sep=1.0,
    #     hypercube=True, shift=0.0, scale=1.0, shuffle=True,
    #     random_state=None)

    true_labels = 2*true_labels - 1   # Convert labels to +-1

# Problem size
n = len(true_labels)

#####################
### Select classifier

# Create classifier object
#myClassifier = clold.Classifier(type_classif, dim)
myClassifier = classifier.Classifier(type_classif)

### Report data used in the simulation
print '-----------------------'
print 'Datos de la simulación:'
print '    Tamaño muestral: n = ' + str(n) + ' muestras, etiquetadas en ' \
    + str(n/pool_size) + ' lotes de ' + str(pool_size) \
    + ' muestras cada uno.'
print '    Dimensión de los datos = ' + str(x.shape[1])
print '    Tipo de clasificador = ' + str(type_classif)
print '    Nombre del dataset = ' + str(datatype)


###########################################################################
### PART II: AL algorithm analysis                                      ###
###########################################################################

print '----------------------------'
print 'AL analysis'

print 'Evaluando muestreo aleatorio...'
ALrandom = al.ActiveLearner('random')
Pe_random = ALrandom.evaluate(myClassifier, x, true_labels, pool_size, n_sim)

print 'Evaluando Active Learning...'
ALtourney = al.ActiveLearner(best_AL, alth=threshold, p=0.4,
                             tourneysize=best_ts)
#ALtourney.alth = threshold
#ALtourney.p = 0.4
#ALtourney.tourneysize = best_ts
Pe_best_AL = ALtourney.evaluate(myClassifier, x, true_labels, pool_size, n_sim)

################
### Plot results

# Color codes
color1 = [0.0/255.0, 0.0/255.0, 0.0/255.0]
color2 = [0.0/255.0, 152.0/255.0, 195.0/255.0]
color3 = [177.0/255.0, 209.0/255.0, 55.0/255.0]
color4 = [103.0/255.0, 184.0/255.0, 69.0/255.0]
color5 = [8.0/255.0, 128.0/255.0, 127.0/255.0]
color6 = [46.0/255.0, 50.0/255.0, 110.0/255.0]
color7 = [134.0/255.0, 37.0/255.0, 98.0/255.0]
color8 = [200.0/255.0, 16.0/255.0, 58.0/255.0]
color9 = [194.0/255.0, 53.0/255.0, 114.0/255.0]
color10 = [85.0/255.0, 53.0/255.0, 123.0/255.0]
color11 = [15.0/255.0, 100.0/255.0, 170.0/255.0]
color12 = [68.0/255.0, 192.0/255.0, 193.0/255.0]
color13 = [27.0/255.0, 140.0/255.0, 76.0/255.0]
color14 = [224.0/255.0, 208.0/255.0, 63.0/255.0]
color15 = [226.0/255.0, 158.0/255.0, 47.0/255.0]
color16 = [232.0/255.0, 68.0/255.0, 37.0/255.0]

font = {'family': 'Verdana',
        'weight': 'regular',
        'size': 10}

matplotlib.rc('font', **font)

# Plot error rates vs labeled samples
print 'Tamaño muestral: n = ' + str(n) + ' muestras'
print str(n) + ' muestras, etiquetadas en ' + str(n/pool_size) \
    + ' lotes de ' + str(pool_size) + ' muestras cada uno.'

### Vector of succesive sample sizes
nq = range(pool_size, n+pool_size, pool_size)
nq[-1] = min(nq[-1], n)
n_pools = len(nq)

fig = plt.figure()
print 'Dimension de la matriz de features = ' + str(x.shape)
print 'Numero de etiquetas = ' + str(n)
h1, = plt.plot(nq, Pe_random, '--', marker='o', color=color1)
h2, = plt.plot(nq, Pe_best_AL, '-', marker='.', color=color2)

# Text in the figures
plt.legend([h1, h2],
           ["Muestreo aleatorio", "Active Learning"])
fig.suptitle(u'Evolución del nº de errores con el nº de datos etiquetados')
plt.xlabel(u'Tamaño del conjunto de datos etiquetados')
plt.ylabel('Tasa de error')
plt.show(block=False)

#####################
### Sample efficiency
m = np.zeros(n_pools)
for k in range(n_pools):

    pk = Pe_random[k]
    dif = pk - Pe_best_AL
    dup = (dif < 0).choose(dif, np.inf)
    i1 = np.argmin(dup)
    #q[k] = nq[i1]

    m1 = nq[i1]
    ddown = (dif >= 0).choose(dif, -np.inf)
    i2 = np.argmax(ddown)
    m2 = nq[i2]

    q1 = Pe_best_AL[i1]
    q2 = Pe_best_AL[i2]

    # Interpolate m at pk, between q1 and q2
    if q2 != q1:
        m[k] = m1 + (pk-q1)/(q2-q1)*(m2-m1)
    else:
        m[k] = m1

fig, ax = plt.subplots()
nq_ext = np.append(0, nq)
m_ext = np.append(0, m)
h1, = ax.plot(nq_ext, nq_ext, '-', color=color1)
h2, = ax.plot(nq_ext, m_ext, '-', marker='.', color=color2)
fig.suptitle(u'Eficiencia muestral del Active Learning')
plt.xlabel(u'Demanda de etiquetas sin AL')
plt.ylabel(u'Demanda de etiquetas con AL')
ax.fill_between(nq_ext, 0, m_ext, color=color2)
plt.axis([0, max(nq_ext), 0, max(nq_ext)])
plt.show(block=False)

################
### Save results

with open('./results/ALanalysis.pickle', 'w') as f:
    pickle.dump([nq, Pe_random, Pe_best_AL, nq_ext, m_ext], f)

print '================'
print 'Fin de ejecucion'
print 'Resultados en /results/ALanalysis.pkl'
