# -*- coding: utf-8 -*-
"""
Created on Sun Mar 16 15:48:36 2014

@author: xiao
"""

# -*- coding: utf-8 -*-
"""
Created on Tue Mar  4 20:55:48 2014

@author: xiao
"""
from numpy import *
from math import *
from numpy.linalg import *
import matplotlib.pyplot as plt
import time
from multiprocessing import Pool

###################################
#### Original data             ####
###################################
#m number of users
m=100
#number of items
n=1238
#matrix of observation
X=empty((m,n))

###################################
#### Learning parameters       ####
###################################
lamda = 0.5
C=1
iterationlimits=5000

distanceV=empty(251)

#########################################
#### Intermediaire data structures   ####
#########################################
Omega={}
#the set of set of non-positive items for each user
Omega_comp={}

nproc = 2
#dim_embedding is the dimension of the embedding
#usually like 50 or 100
dim_embedding = 10
#V is matrix of dimension dim_embedding * n
V = random.normal(0, 1.0/sqrt(dim_embedding), dim_embedding * n)
V = reshape(V, (dim_embedding, n))

print "finish initialize V"

global lAUC

#########################################
####    read ratings from the file   ####
#########################################
def readDataFromFile(fadress):
    '''the rating is binaire'''
    filename = fadress
    f = open(filename, 'r')
    for line in f:
        nums = [int(x) for x in line.split()]
        if len(nums)<3:
            print nums
        client = nums[0]
        film = nums[1]
#        rating = nums[2]
        X[client-1][film-1] = 1

#########################################
####   get posotive items of user u  ####
#########################################

def getDu():
    '''get posotive items of user u'''
    '''return an ensemble of set of positive items for each user'''
    for i in range(m):
        t = X[i,:]
        D_u=nonzero(t>0)[0]
        Omega[i]=D_u

        D = arange(n)
        D_u_bar = setdiff1d(D, D_u)
        Omega_comp[i] = D_u_bar

'''factorized models'''
def f_d(d,u):
    Du=Omega[u]
    global V
    t=V[:,Du]
#    print t
    vec_items_pos_ranked = t.sum(axis=1)
    return dot(vec_items_pos_ranked.T, V[:,d])/len(Du)

def f_bar_d(bar_d, u):
    return f_d(bar_d,u)

def f(u):
    Du=Omega[u]
    global V
    t=V[:,Du]
#    print t
    vec_items_pos_ranked = t.sum(axis=1)
    return dot(vec_items_pos_ranked.T, V)/ len(Du)

###################################################
####  making one gradient step to matrix V     ####
####  To minimize g=max(0, 1-fd(u)+f\bar d(u)) ####
###################################################

#one gradient step to each element in matrix V
def derivativeOneStep(u,d, bar_d):
    '''for element Vpq, p in range(m), q in range( #items)'''
    '''for each p:'''
    '''if q \in Du and q != d: step = -Vpd + Vpbar_d'''
    '''if q \in Du and q = d:  step = -Vpq + Vpbar+d + \sum_{i \in Du} Vpi'''
    '''if q not \in Du and q != bar_d: step = 0'''
    '''if q not \in Du and q == bar_d: step = \sum_{i \in Du} Vpi'''
#    print "start computing derivatives", u, d, bar_d

    D_u = Omega[u]
    step = zeros((dim_embedding, n))
    sum1 = -V[:,d] + V[:, bar_d]
    sum2 = V[:,D_u].sum(axis=1)

    for i in D_u:
        step[:,i] = sum1
#    step[:, D_u] = sum1
    step[:, d] += sum2
    step[:, bar_d] += sum2

    return step

########################################################
####    localAUC                                    ####
########################################################
####  We hardly use it, because it's too costly.    ####
########################################################

def localAUC_u(u):
    '''we hardly use it, because it's too costly. '''
    Du=Omega[u]
#    D = arange(n)
#    D_u_bar = setdiff1d(D, D_u)
    Du_bar = Omega_comp[u]

    fvalue = f(u)

    f_Du = fvalue[Du]
    f_bar_Du = fvalue[Du_bar]

    A,B = meshgrid(f_Du, f_bar_Du)

    tempMatrix = 1-A+B

    '''indice de normalization: #users * |Du| * (#items - |Du|)*(1+2*C*C) '''
    indice = m*len(Du)*(n-len(Du))*(1+2*C*C)
    return sum(tempMatrix[tempMatrix>0])/indice

#compute localAUC for a set of users
def localAUC_uset(uset):
    res = 0
    for i in uset:
        temp = localAUC_u(i)
        res += temp
    return res

#compute localAUC using multi-process
def localAUC_mp():

    pool = Pool(processes = nproc)

    return sum(pool.map(localAUC_u, range(100)))

def  ftest(x):
    return x*x

def localAUC():
    global lAUC
    lAUC = 0
    for i in range(m):
        lAUC += localAUC_u(i)
    return lAUC

######################################################
####  Implementation  of regular AUC algorithm    ###    i=0

    print "iteration:",i
    gda_localAUC_oneStep()

    startTime = time.time()
    distanceV[i]=localAUC()
    print time.time()- startTime, distanceV[i]#
######################################################

def chooseRandoms():
    u = random.randint(0,m-1)
    D_u = Omega[u]
    while(len(D_u)==n):
        u = random.randint(0,m-1)
        D_u = Omega[u]

    D_u_bar = Omega_comp[u]

    i = random.randint(0, len(D_u)-1)
    j = random.randint(0, len(D_u_bar)-1)
    d = D_u[i]
    bar_d = D_u_bar[j]

    return (u, d, bar_d)
#    d = random.choice(D_u)
#    bar_d = random.choice(D_u_bar)

def gda_localAUC_oneStep():
    (u,d,bar_d)=chooseRandoms()
    if(f_d(d,u)<f_bar_d(bar_d,u)+1):
        global V
        V -= lamda * derivativeOneStep(u, d, bar_d)
        for i in range (n):
            if( linalg.norm(V[:,i])>C):
               V[:,i]=C*V[:,i]/linalg.norm(V[:,i])

def gda_localAUC():
    startTime1 = time.time()
    i=0

    print "iteration:",i
    gda_localAUC_oneStep()

    distanceV[i]=localAUC()
    i+=1

    while i<iterationlimits:
        gda_localAUC_oneStep()
        if i % 20 == 0:
            temp = localAUC()
            distanceV[i/20] = temp
            print temp
        i+=1

    startTime = time.time()
    temp = localAUC()
    distanceV[i/20]=temp
    print "iteration:",i/20,
    print time.time()- startTime1, temp
    plot(distanceV)

#computing localAUC with multiple process.
def gda_localAUC_mp():
    startTime1 = time.time()
    i=0

    gda_localAUC_oneStep()

    temp=localAUC_mp()
    distanceV[i] = temp
    print "iteration:",i, temp
    i+=1

    while i<iterationlimits:
        gda_localAUC_oneStep()
        if i % 20 == 0:
            startTime = time.time()
            temp = localAUC_mp()
            distanceV[i/20]=temp
#            print "iteration:",i/20,
            print temp

        i+=1

    startTime = time.time()
    temp = localAUC()
    distanceV[i/20]=temp
#    print "iteration:",i/20,
    print temp
    print time.time()- startTime1
#    plot(distanceV)

def comp_lAUC_lAUCmp():
    i=0

    print "iteration:",i
    gda_localAUC_oneStep()

    startTime = time.time()
    distanceV[i]=localAUC()
    print time.time()- startTime, distanceV[i]

    startTime = time.time()
    temps = localAUC_mp()
    print time.time() - startTime, temps

######################################################
####               test normal AUC                ####
######################################################

#after iterations show localAUC
#fadress = "/home/xiao/ProjetLibre/matrix/matrixInfo"
fadress = "ldaphome/xiao-wang/korder/matrix/matrixInfo"
readDataFromFile(fadress)
getDu()
#gda_localAUC()
gda_localAUC_mp()
#comp_lAUC_lAUCmp()
#plt(distanceV)