# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     name: python3
# ---

# + colab={"base_uri": "https://localhost:8080/"} id="W16NrwageWrG" outputId="2d330d43-11fd-4110-ce56-14ced3e6bb46"
# !pip install tensorflow==1.14
# !pip install keras==2.2.4

from google.colab import drive
drive.mount('/content/gdrive')
# %cd gdrive/My Drive/CS6101/NCR

# + colab={"base_uri": "https://localhost:8080/"} id="bEulQ49keXs1" outputId="2db720bd-bd59-4cca-f58b-3e8437187a28"
from time import time
import numpy as np
import argparse
import tensorflow as tf
from keras import backend as K
from keras.regularizers import l1,l2
from keras.models import Sequential,Model
from keras.layers import Embedding,Input,Dense,merge,Flatten,Lambda,Multiply,concatenate,Add
from keras.optimizers import Adam,Adagrad,SGD,RMSprop,Adadelta
from keras.initializers import RandomNormal,TruncatedNormal

# + id="2nZ3784RekQD"
import math
import multiprocessing
import numpy as np


# Global variables that are shared across processes
_model = None
_testRatings = None
_testNegatives = None
_K = None


def evaluate_model(model, testRatings, testNegatives, K, num_thread):
    """
    Evaluate the performance (Hit_Ratio, NDCG) of top-K recommendation
    Return: score of each test rating.
    """
    global _model
    global _testRatings
    global _testNegatives
    global _K
    _model = model
    _testRatings = testRatings
    _testNegatives = testNegatives
    _K = K

    hits, ndcgs = [], []
    if (num_thread > 1):  # Multi-thread
        pool = multiprocessing.Pool(processes=num_thread)
        res = pool.map(eval_one_rating, range(len(_testRatings)))
        pool.close()
        pool.join()
        hits = [r[0] for r in res]
        ndcgs = [r[1] for r in res]
        return (hits, ndcgs)
    # Single thread

    for idx in range(len(_testRatings)):
        (hr, ndcg) = eval_one_rating(idx)
        hits.append(hr)
        ndcgs.append(ndcg)
    return (hits, ndcgs)


def eval_one_rating(idx):
    rating = _testRatings[idx]
    items = _testNegatives[idx]
    u = rating[0]
    gtItem = rating[1]
    if gtItem==-1:
        return -1,-1
    # items.append(gtItem)
    # Get prediction scores

    users = np.full(len(items), u, dtype='int32')
    item_pos=np.full(len(items),gtItem,dtype='int32')

    predictions1 = _model.predict([users,item_pos,np.array(items)],batch_size=101, verbose=0)
    predictions2 = _model.predict([users,np.array(items),item_pos], batch_size=101, verbose=0)
    prediction=predictions1-predictions2

    num_err=len(prediction[prediction<0])
    if num_err>=_K:
        hr=0
        ndcg=0
    else:
        hr=1
        ndcg=math.log(2) / math.log(num_err + 2)

    return (hr, ndcg)

def evaluate_aux_model(model, testRatings, testNegatives, K, num_thread):
    """
    Evaluate the performance (Hit_Ratio, NDCG) of top-K recommendation
    Return: score of each test rating.
    """
    global _model
    global _testRatings
    global _testNegatives
    global _K
    _model = model
    _testRatings = testRatings
    _testNegatives = testNegatives
    _K = K

    hits, ndcgs = [], []
    if (num_thread > 1):  # Multi-thread
        pool = multiprocessing.Pool(processes=num_thread)
        res = pool.map(eval_one_aux_rating, range(len(_testRatings)))
        pool.close()
        pool.join()
        hits = [r[0] for r in res]
        ndcgs = [r[1] for r in res]
        return (hits, ndcgs)
    # Single thread

    for idx in range(len(_testRatings)):
        (hr, ndcg) = eval_one_aux_rating(idx)
        hits.append(hr)
        ndcgs.append(ndcg)
    return (hits, ndcgs)


def eval_one_aux_rating(idx):
    rating = _testRatings[idx]
    items = _testNegatives[idx]
    u = rating[0]
    aux = rating[2]
    gtItem = rating[1]
    if gtItem==-1:
        return -1,-1
    # items.append(gtItem)
    # Get prediction scores

    users = np.full(len(items), u, dtype='int32')
    item_pos=np.full(len(items),gtItem,dtype='int32')
    aux=np.full(len(items), aux, dtype='int32')

    predictions1 = _model.predict([users,item_pos,np.array(items),aux],batch_size=101, verbose=0)
    predictions2 = _model.predict([users,np.array(items),item_pos,aux], batch_size=101, verbose=0)
    prediction=predictions1-predictions2

    num_err=len(prediction[prediction<0])
    if num_err>=_K:
        hr=0
        ndcg=0
    else:
        hr=1
        ndcg=math.log(2) / math.log(num_err + 2)

    return (hr, ndcg)


# + id="Dbk-eHA7-mW0"
def evaluate_feature_model(model, testRatings, testNegatives, K, num_thread):
    """
    Evaluate the performance (Hit_Ratio, NDCG) of top-K recommendation
    Return: score of each test rating.
    """
    global _model
    global _testRatings
    global _testNegatives
    global _K
    _model = model
    _testRatings = testRatings
    _testNegatives = testNegatives
    _K = K

    hits, ndcgs = [], []
    if (num_thread > 1):  # Multi-thread
        pool = multiprocessing.Pool(processes=num_thread)
        res = pool.map(eval_one_feature_rating, range(len(_testRatings)))
        pool.close()
        pool.join()
        hits = [r[0] for r in res]
        ndcgs = [r[1] for r in res]
        return (hits, ndcgs)
    # Single thread

    for idx in range(len(_testRatings)):
        (hr, ndcg) = eval_one_feature_rating(idx)
        hits.append(hr)
        ndcgs.append(ndcg)
    return (hits, ndcgs)


def eval_one_feature_rating(idx):
    rating = _testRatings[idx]
    items = _testNegatives[idx]
    u = rating[0]
    gender = rating[3]
    age = rating[4]
    occupation = rating[5]
    gtItem = rating[1]
    if gtItem==-1:
        return -1,-1
    # items.append(gtItem)
    # Get prediction scores

    users = np.full(len(items), u, dtype='int32')
    item_pos=np.full(len(items),gtItem,dtype='int32')
    age=np.full(len(items), age, dtype='int32')
    gender=np.full(len(items), gender, dtype='int32')
    occupation=np.full(len(items), occupation, dtype='int32')

    predictions1 = _model.predict([users,item_pos,np.array(items),gender,age,occupation],batch_size=101, verbose=0)
    predictions2 = _model.predict([users,np.array(items),item_pos,gender,age,occupation], batch_size=101, verbose=0)
    prediction=predictions1-predictions2

    num_err=len(prediction[prediction<0])
    if num_err>=_K:
        hr=0
        ndcg=0
    else:
        hr=1
        ndcg=math.log(2) / math.log(num_err + 2)

    return (hr, ndcg)


# + colab={"background_save": true} id="YacCwY3eeodf"
import scipy.sparse as sp
import numpy as np

class DataSet:

    def __init__(self):
        self.pos_per_user = None
        self.nUsers = 0
        self.nItems = 0
        self.nClicks = 0

        self.userids = {}
        self.itemids = {}

        self.rUserids = {}
        self.rItemids={}

        self.pos_per_user={}

    # def loadData(self, filepath, userMin, itemMin):

    def loadClicks(self, filepath, userMin, itemMin):

        uCounts={}
        iCounts={}
        nRead=0
        print("  Loading clicks from %s, userMin = %d  itemMin = %d ", filepath, userMin, itemMin)

        with open(filepath, 'r') as f:
            for line in f.readlines():
                uName, iName, rating, time= line.strip().split(' ')
                nRead+=1
                if uName not in uCounts:
                    uCounts[uName]=0
                if iName not in iCounts:
                    iCounts[iName]=0

                uCounts[uName]+=1
                iCounts[iName]+=1
        print("\n  First pass: #users = %d, #items = %d, #clicks = %d\n",
        len(uCounts),len(iCounts), nRead)



        with open(filepath, 'r') as f:
            for line in f.readlines():
                uName, iName, rating, time= line.strip().split(' ')
                try:
                    tmp = int(time)
                except:
                    continue

                if uCounts[uName]< userMin:
                    continue

                if iCounts[iName]< itemMin:
                    continue

                self.nClicks+=1

                if  iName not in self.itemids:
                    self.rItemids[self.nItems]=iName
                    self.itemids[iName]=self.nItems
                    self.nItems+=1

                if uName not in self.userids:
                    self.rUserids[self.nUsers]=uName
                    self.userids[uName]=self.nUsers
                    # rUserids maps int userid (start 0) to string userid (start 1)
                    # userids maps string userid(start 1) to int userid (start 0)
                    self.nUsers+=1
                    self.pos_per_user[self.userids[uName]]=[]
                self.pos_per_user[self.userids[uName]].append((self.itemids[iName], int(time)))

            print("  Sorting clicks for each users ")

            for u in range(self.nUsers):
                sorted(self.pos_per_user[u], key=lambda d: d[1])
            print("\n \"nUsers\": %d,\"nItems\":%d, \"nClicks\":%d\n",self.nUsers,self.nItems,self.nClicks)

            self.val_per_user = []
            self.test_per_user = []
            self.train_per_user={}
            self.test_negative_per_user = {}
            mat = sp.dok_matrix((self.nUsers, self.nItems), dtype=np.float32)
            np.random.seed(2017)
            for u in range(self.nUsers):

                if len(self.pos_per_user[u])<3:
                    item_test=-1
                    item_valid=-1
                    continue

                item_test=self.pos_per_user[u][-1][0]
                self.pos_per_user[u].pop()
                item_valid=self.pos_per_user[u][-1][0]
                self.pos_per_user[u].pop()

                # item_val=self.pos_per_user[u][-1][0]
                # self.pos_per_user[u].pop()
                # self.train_per_user[u]=[]
                self.train_per_user[u]=[e[0] for e in self.pos_per_user[u]]
                row = user_df[user_df.id==self.rUserids[u]].iloc[0]
                self.test_per_user.append([u,item_test,row['aux_info'],gender2id[row['gender']],age2id[row['age_group']],occupation2id[row['occupation']]])
                # self.test_per_user[u]=item_test
                self.val_per_user.append([u,item_valid,row['aux_info'],gender2id[row['gender']],age2id[row['age_group']],occupation2id[row['occupation']]])

                for item in self.train_per_user[u]:
                    mat[u,item]=1.0

                self.test_negative_per_user[u]=[]
                for i in range(100):
                    neg_item_id = np.random.randint(0,self.nItems)
                    while neg_item_id in self.train_per_user[u] or neg_item_id==item_test \
                          or neg_item_id==item_valid or neg_item_id in self.test_negative_per_user[u]:
                        neg_item_id = np.random.randint(0, self.nItems)
                    self.test_negative_per_user[u].append(neg_item_id)
            self.trainMatrix = mat
            self.testRatings = self.test_per_user
            self.validRatings = self.val_per_user

            self.testNegatives=[]
            for u in range(self.nUsers):
                if u in self.train_per_user:
                    self.testNegatives.append([e for e in self.test_negative_per_user[u]])


# + colab={"background_save": true} id="4M4S_BHBeq2O"
def get_model(num_users,num_items,mf_dim=10,layers=[10],reg_layers=[0,0,0,0],reg_mf=0):
    num_layer=len(layers)

    user_input=Input(shape=(1,),dtype='int32')
    item_input_pos=Input(shape=(1,),dtype='int32')
    item_input_neg = Input(shape=(1,), dtype='int32')

    MF_embedding_user=Embedding(input_dim=num_users,output_dim=mf_dim,embeddings_initializer='random_normal',
                                name='mf_user_embedding',embeddings_regularizer=l2(reg_mf),input_length=1)
    MF_embedding_item = Embedding(input_dim=num_items, output_dim=mf_dim, embeddings_initializer='random_normal',
                                  name='mf_item_embedding',embeddings_regularizer=l2(reg_mf), input_length=1)
    MLP_embedding_user=Embedding(input_dim=num_users,output_dim=layers[0],embeddings_initializer='random_normal',
                                 name='mlp_user_embedding', embeddings_regularizer=l2(reg_mf),input_length=1)
    MLP_embedding_item = Embedding(input_dim=num_items, output_dim=layers[0], embeddings_initializer='random_normal',
                                   name='mlp_item_embedding',embeddings_regularizer=l2(reg_mf), input_length=1)

    mf_user_latent=Flatten()(MF_embedding_user(user_input))
    mf_item_latent_pos=Flatten()(MF_embedding_item(item_input_pos))
    mf_item_latent_neg = Flatten()(MF_embedding_item(item_input_neg))


    prefer_pos = Multiply()([mf_user_latent, mf_item_latent_pos])
    prefer_neg = Multiply()([mf_user_latent, mf_item_latent_neg])
    prefer_neg = Lambda(lambda x: x)(prefer_neg)
    mf_vector = concatenate([prefer_pos, prefer_neg])


    mlp_user_latent=Flatten()(MLP_embedding_user(user_input))
    mlp_item_latent_pos=Flatten()(MLP_embedding_item(item_input_pos))
    mlp_item_latent_neg=Flatten()(MLP_embedding_item(item_input_neg))
    mlp_item_latent_neg=Lambda(lambda x: x)(mlp_item_latent_neg)
    mlp_vector=concatenate([mlp_user_latent,mlp_item_latent_pos,mlp_item_latent_neg])
    for idx in range(1,num_layer):
        layer=Dense(layers[idx],kernel_regularizer=l2(0.0000),activation='tanh',name="layer%d" %idx)
        mlp_vector=layer(mlp_vector)

    predict_vector=concatenate([mf_vector,mlp_vector])

    prediction=Dense(1,activation='sigmoid',kernel_initializer='lecun_uniform',name='prediction')(predict_vector)
    model=Model(inputs=[user_input,item_input_pos,item_input_neg],outputs=prediction)

    tf.keras.utils.plot_model(
      model,
      to_file="model.png",
      show_layer_names=True,
    )

    return model

def get_train_instances(train, num_negatives):
    user_input,item_pos,item_neg,labels = [],[],[],[]
    num_items= train.shape[1]
    for (u, i) in train.keys():
        # positive instance
        user_input.append(u)
        item_pos.append(i)
        j = np.random.randint(num_items)
        while (u, j) in train.keys():
            j = np.random.randint(num_items)
        item_neg.append(j)
        labels.append(1)

        user_input.append(u)
        item_pos.append(j)
        item_neg.append(i)
        labels.append(0)

        # negative instances

        for cnt in range(num_negatives-1):
            user_input.append(u)
            j=np.random.randint(num_items)
            while (u,j) in train.keys():
                j = np.random.randint(num_items)
            item_pos.append(j)
            item_neg.append(i)
            labels.append(0)

    # np.random.seed(123)
    # np.random.shuffle(user_input)
    # np.random.seed(123)
    # np.random.shuffle(item_pos)
    # np.random.seed(123)
    # np.random.shuffle(item_neg)
    # np.random.seed(123)
    # np.random.shuffle(labels)
    return user_input, item_pos,item_neg,labels

def get_feature_model(num_users,num_items,gender_inp,age_inp,occupation_inp,mf_dim=10,layers=[10],reg_layers=[0,0,0,0],reg_mf=0):
    num_layer=len(layers)

    user_input=Input(shape=(1,),dtype='int32',name="userid_input")
    item_input_pos=Input(shape=(1,),dtype='int32',name="pos_item_input")
    item_input_neg = Input(shape=(1,), dtype='int32',name="neg_item_input")
    gender_input=Input(shape=(1,),dtype='int32',name="gender_input")
    age_input=Input(shape=(1,),dtype='int32',name="age_input")
    occupation_input=Input(shape=(1,),dtype='int32',name="occupation_input")

    MF_embedding_user=Embedding(input_dim=num_users,output_dim=int(mf_dim),embeddings_initializer='random_normal',
                                name='mf_user_embedding',embeddings_regularizer=l2(reg_mf),input_length=1)
    MF_embedding_gender=Embedding(input_dim=gender_inp,output_dim=int(mf_dim),embeddings_initializer='random_normal',
                                    name='gender_embedding',embeddings_regularizer=l2(reg_mf),input_length=1)
    MF_embedding_age=Embedding(input_dim=age_inp,output_dim=int(mf_dim),embeddings_initializer='random_normal',
                                    name='age_embedding',embeddings_regularizer=l2(reg_mf),input_length=1)
    MF_embedding_occupation=Embedding(input_dim=occupation_inp,output_dim=int(mf_dim),embeddings_initializer='random_normal',
                                    name='occupation_embedding',embeddings_regularizer=l2(reg_mf),input_length=1)
    MF_embedding_item = Embedding(input_dim=num_items, output_dim=mf_dim, embeddings_initializer='random_normal',
                                  name='mf_item_embedding',embeddings_regularizer=l2(reg_mf), input_length=1)
    MLP_embedding_user=Embedding(input_dim=num_users,output_dim=layers[0],embeddings_initializer='random_normal',
                                 name='mlp_user_embedding', embeddings_regularizer=l2(reg_mf),input_length=1)
    MLP_embedding_item = Embedding(input_dim=num_items, output_dim=layers[0], embeddings_initializer='random_normal',
                                   name='mlp_item_embedding',embeddings_regularizer=l2(reg_mf), input_length=1)

    mf_user_latent=Add()([Flatten()(MF_embedding_user(user_input)), Flatten()(MF_embedding_gender(gender_input)), Flatten()(MF_embedding_age(age_input)), Flatten()(MF_embedding_occupation(occupation_input))])
    mf_item_latent_pos=Flatten()(MF_embedding_item(item_input_pos))
    mf_item_latent_neg = Flatten()(MF_embedding_item(item_input_neg))


    prefer_pos = Multiply()([mf_user_latent, mf_item_latent_pos])
    prefer_neg = Multiply()([mf_user_latent, mf_item_latent_neg])
    prefer_neg = Lambda(lambda x: x)(prefer_neg)
    mf_vector = concatenate([prefer_pos, prefer_neg])


    mlp_user_latent=Add()([Flatten()(MF_embedding_user(user_input)), Flatten()(MF_embedding_gender(gender_input)), Flatten()(MF_embedding_age(age_input)), Flatten()(MF_embedding_occupation(occupation_input))])
    mlp_item_latent_pos=Flatten()(MLP_embedding_item(item_input_pos))
    mlp_item_latent_neg=Flatten()(MLP_embedding_item(item_input_neg))
    mlp_item_latent_neg=Lambda(lambda x: x)(mlp_item_latent_neg)
    mlp_vector=concatenate([mlp_user_latent,mlp_item_latent_pos,mlp_item_latent_neg])
    for idx in range(1,num_layer):
        layer=Dense(layers[idx],kernel_regularizer=l2(0.0000),activation='tanh',name="layer%d" %idx)
        mlp_vector=layer(mlp_vector)

    predict_vector=concatenate([mf_vector,mlp_vector])

    prediction=Dense(1,activation='sigmoid',kernel_initializer='lecun_uniform',name='prediction')(predict_vector)
    model=Model(inputs=[user_input,item_input_pos,item_input_neg,gender_input,age_input,occupation_input],outputs=prediction)

    tf.keras.utils.plot_model(
      model,
      to_file="model_all.png",
      show_layer_names=True,
    )

    return model

def get_aux_model(num_users,num_items,aux_inp,mf_dim=10,layers=[10],reg_layers=[0,0,0,0],reg_mf=0):
    num_layer=len(layers)

    user_input=Input(shape=(1,),dtype='int32')
    item_input_pos=Input(shape=(1,),dtype='int32')
    item_input_neg = Input(shape=(1,), dtype='int32')
    aux_input=Input(shape=(1,),dtype='int32')

    MF_embedding_user=Embedding(input_dim=num_users,output_dim=int(mf_dim),embeddings_initializer='random_normal',
                                name='mf_user_embedding',embeddings_regularizer=l2(reg_mf),input_length=1)
    MF_embedding_aux_user=Embedding(input_dim=aux_inp,output_dim=int(mf_dim),embeddings_initializer='random_normal',
                                    name='mf_aux_embedding',embeddings_regularizer=l2(reg_mf),input_length=1)
    MF_embedding_item = Embedding(input_dim=num_items, output_dim=mf_dim, embeddings_initializer='random_normal',
                                  name='mf_item_embedding',embeddings_regularizer=l2(reg_mf), input_length=1)
    MLP_embedding_user=Embedding(input_dim=num_users,output_dim=layers[0],embeddings_initializer='random_normal',
                                 name='mlp_user_embedding', embeddings_regularizer=l2(reg_mf),input_length=1)
    MLP_embedding_item = Embedding(input_dim=num_items, output_dim=layers[0], embeddings_initializer='random_normal',
                                   name='mlp_item_embedding',embeddings_regularizer=l2(reg_mf), input_length=1)

    mf_user_latent=Add()([Flatten()(MF_embedding_user(user_input)), Flatten()(MF_embedding_aux_user(aux_input))])
    mf_item_latent_pos=Flatten()(MF_embedding_item(item_input_pos))
    mf_item_latent_neg = Flatten()(MF_embedding_item(item_input_neg))


    prefer_pos = Multiply()([mf_user_latent, mf_item_latent_pos])
    prefer_neg = Multiply()([mf_user_latent, mf_item_latent_neg])
    prefer_neg = Lambda(lambda x: x)(prefer_neg)
    mf_vector = concatenate([prefer_pos, prefer_neg])


    mlp_user_latent=Add()([Flatten()(MF_embedding_user(user_input)), Flatten()(MF_embedding_aux_user(aux_input))])
    mlp_item_latent_pos=Flatten()(MLP_embedding_item(item_input_pos))
    mlp_item_latent_neg=Flatten()(MLP_embedding_item(item_input_neg))
    mlp_item_latent_neg=Lambda(lambda x: x)(mlp_item_latent_neg)
    mlp_vector=concatenate([mlp_user_latent,mlp_item_latent_pos,mlp_item_latent_neg])
    for idx in range(1,num_layer):
        layer=Dense(layers[idx],kernel_regularizer=l2(0.0000),activation='tanh',name="layer%d" %idx)
        mlp_vector=layer(mlp_vector)

    predict_vector=concatenate([mf_vector,mlp_vector])

    prediction=Dense(1,activation='sigmoid',kernel_initializer='lecun_uniform',name='prediction')(predict_vector)
    model=Model(inputs=[user_input,item_input_pos,item_input_neg,aux_input],outputs=prediction)

    tf.keras.utils.plot_model(
      model,
      to_file="model.png",
      show_layer_names=True,
    )

    return model

def get_aux_train_instances(train, num_negatives):
    user_input,item_pos,item_neg,labels,aux_info = [],[],[],[],[]
    num_items= train.shape[1]
    for (u, i) in train.keys():
        # positive instance
        user_input.append(u)
        aux_info.append(aux_map[dataset.rUserids[u]])
        item_pos.append(i)
        j = np.random.randint(num_items)
        while (u, j) in train.keys():
            j = np.random.randint(num_items)
        item_neg.append(j)
        labels.append(1)

        user_input.append(u)
        aux_info.append(aux_map[dataset.rUserids[u]])
        item_pos.append(j)
        item_neg.append(i)
        labels.append(0)

        # negative instances

        for cnt in range(num_negatives-1):
            user_input.append(u)
            aux_info.append(aux_map[dataset.rUserids[u]])
            j=np.random.randint(num_items)
            while (u,j) in train.keys():
                j = np.random.randint(num_items)
            item_pos.append(j)
            item_neg.append(i)
            labels.append(0)

    # np.random.seed(123)
    # np.random.shuffle(user_input)
    # np.random.seed(123)
    # np.random.shuffle(item_pos)
    # np.random.seed(123)
    # np.random.shuffle(item_neg)
    # np.random.seed(123)
    # np.random.shuffle(labels)
    return user_input, item_pos,item_neg,labels, aux_info

def get_feature_train_instances(train, num_negatives):
    user_input,item_pos,item_neg,labels,gender_info,age_info,occupation_info = [],[],[],[],[],[],[]
    num_items= train.shape[1]
    for (u, i) in train.keys():
        # positive instance
        user_input.append(u)
        user = feature_map[dataset.rUserids[u]]
        gender_info.append([user[0]])
        age_info.append(user[1])
        occupation_info.append(user[2])
        item_pos.append(i)
        j = np.random.randint(num_items)
        while (u, j) in train.keys():
            j = np.random.randint(num_items)
        item_neg.append(j)
        labels.append(1)

        user_input.append(u)
        gender_info.append([user[0]])
        age_info.append(user[1])
        occupation_info.append(user[2])
        item_pos.append(j)
        item_neg.append(i)
        labels.append(0)

        # negative instances

        for cnt in range(num_negatives-1):
            user_input.append(u)
            gender_info.append([user[0]])
            age_info.append(user[1])
            occupation_info.append(user[2])
            j=np.random.randint(num_items)
            while (u,j) in train.keys():
                j = np.random.randint(num_items)
            item_pos.append(j)
            item_neg.append(i)
            labels.append(0)
    return user_input, item_pos,item_neg,labels, gender_info, age_info, occupation_info


# + colab={"base_uri": "https://localhost:8080/"} id="dRcI3iZ5BYEA" outputId="7ff3b31b-1d12-4a2a-ee9a-e5963c6b0322"
import pandas as pd
count = 0
data = []
with open("./data/users.csv", "r") as f:
  for line in f.readlines():
    if count == 0:
      count += 1
      continue
    else:
      data.append(line.strip().split(";"))
user_df = pd.DataFrame(data, columns=["id", "gender", "age_group", "occupation", "zipcode"])
print(user_df.shape)
user_df.head()

# Encode auxilary information
idx = 0
info2id = {}
age2id = {group:i for i, group in enumerate(sorted(user_df.age_group.unique()))}
gender2id = {group:i for i, group in enumerate(sorted(user_df.gender.unique()))}
occupation2id = {group:i for i, group in enumerate(sorted(user_df.occupation.unique()))}
for i, row in user_df.sort_values(["gender", "age_group", "occupation"]).iterrows():
  info = (row['gender'], row['age_group'], row['occupation'])
  if info not in info2id:
    info2id[info] = idx
    idx += 1
id2info = {v:k for k, v in info2id.items()}
user_df['aux_info'] = user_df.apply(lambda row: info2id[(row['gender'], row['age_group'], row['occupation'])], axis=1)
aux_map = {row['id']:row['aux_info'] for i, row in user_df.iterrows()}
feature_map = {row['id']:[gender2id[row['gender']], age2id[row['age_group']], occupation2id[row['occupation']]] for i, row in user_df.iterrows()}

# + colab={"background_save": true, "base_uri": "https://localhost:8080/"} id="yDuTNq8795_4" outputId="4c0bebe0-b1cd-4b3a-c40a-439c7c1d72c8"
num_epochs = 20
batch_size = 256
mf_dim = 16
layers = [64,32,16,8]
reg_mf = 0
reg_layers = [0,0,0,0]
num_negatives = 2
# learning_rate = args.lr
learner = 'adam'
verbose = 1
topK = 10
evaluation_threads = 1#mp.cpu_count()
# print("NeuPR arguments: %s " %(args))

num_negatives=2
learning_rate = 0.0005

k=2 # k=[1,2,3,4,8]
layers=[e*k for e in layers]

learner='adam'
t1 = time()
dataset = DataSet()
dataset.loadClicks('./data/ml1m.txt', 10, 10)
train,validRatings, testRatings, testNegatives = dataset.trainMatrix,dataset.validRatings,dataset.testRatings, dataset.testNegatives
num_users, num_items = train.shape
print(train.shape)
print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d"
      % (time() - t1, num_users, num_items, train.nnz, len(testRatings)))

# + colab={"base_uri": "https://localhost:8080/"} id="zsBC06B6_9HR" outputId="d886b7d4-bb97-45a0-f73c-87d1b1cefb3e"
######################################################
model = get_feature_model(num_users, num_items, len(gender2id), len(age2id), len(occupation2id), mf_dim, layers, reg_layers, reg_mf)

if learner.lower() == "adagrad":
    model.compile(optimizer=Adagrad(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "rmsprop":
    model.compile(optimizer=RMSprop(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "adam":
    model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy')
else:
    model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy')
    print('sgd')

(hits, ndcgs) = evaluate_feature_model(model, validRatings, testNegatives, topK, evaluation_threads)
hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean()
print('Init: HR = %.4f, NDCG = %.4f' % (hr, ndcg))
best_hr, best_ndcg, best_iter = hr, ndcg, -1
best_test_hr,best_test_ndcg=-1,-1
    # Training model
all_loss = []
for epoch in range(num_epochs):
    t1 = time()
    # Generate training instances
    user_input, item_input_pos,item_input_neg, labels, gender_info, age_info, occupation_info = get_feature_train_instances(train, num_negatives)

    # Training
    hist = model.fit([np.array(user_input), np.array(item_input_pos),np.array(item_input_neg),np.array(gender_info),np.array(age_info),np.array(occupation_info)],  # input
                      np.array(labels),  # labels
                      batch_size=batch_size, epochs=1, verbose=0, shuffle=True)
    t2 = time()

    # model_out_file = 'Pretrain/%s_NeuPR_%d_%s_%d.h5' % (args.dataset, mf_dim, args.layers, epoch)
    # model_out_file = 'Pretrain/%s_NeuPR_%d.h5' % (args.dataset, mf_dim)

    if epoch % verbose == 0:
        (hits, ndcgs) = evaluate_feature_model(model, validRatings, testNegatives, topK, evaluation_threads)
        hits,ndcgs=np.array(hits),np.array(ndcgs)
        vhr, vndcg=hits[hits>=0].mean(), ndcgs[ndcgs>=0].mean()
        (hits, ndcgs) = evaluate_feature_model(model, testRatings, testNegatives, topK, evaluation_threads)
        hits, ndcgs = np.array(hits), np.array(ndcgs)
        hr, ndcg, loss = hits[hits>=0].mean(), ndcgs[ndcgs>=0].mean(), hist.history['loss'][0]
        all_loss.append(loss)
        print('Iteration %d [%.1f s]: [Valid HR = %.4f, NDCG = %.4f, loss=%.6f]\t[Test HR = %.4f, NDCG = %.4f], [%.1f s]'
              % (epoch, t2 - t1, vhr,vndcg,loss,hr,ndcg ,time() - t2))
        # print('Iteration %d [%.1f s]: Test HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]'
        #       % (epoch, t2 - t1, hr, ndcg, loss, time() - t2))
        if vhr > best_hr:
            best_hr, best_ndcg, best_iter = vhr, vndcg, epoch
            best_test_hr,best_test_ndcg=hr,ndcg

print("End. Best Iteration %d: Test HR = %.4f, NDCG = %.4f. " % (best_iter, best_test_hr, best_test_ndcg))
print('learning_rate: %.5f , num_factor: %d' % (learning_rate, mf_dim))

# + colab={"base_uri": "https://localhost:8080/", "height": 1000} id="tvlMumntE1vC" outputId="a70ebb77-4bde-4722-a006-bb18ecaafd43"
import plotly
import numpy as np
import plotly.graph_objs as go
from sklearn.decomposition import PCA

def display_pca_scatterplot_3D(inp2id, id2inp, weights, user_input=None, words=None, label=None, color_map=None, topn=5, sample=10):
    
    
    three_dim = PCA(random_state=0).fit_transform(weights)[:,:3]
    # For 2D, change the three_dim variable into something like two_dim like the following:
    # two_dim = PCA(random_state=0).fit_transform(word_vectors)[:,:2]

    data = []
    count = 0
  
    trace = go.Scatter(
        x = three_dim[:,0], 
        y = three_dim[:,1],  
        #z = three_dim[:,2],
        text = list(inp2id.keys()),
        name = "Age group",
        textposition = "top center",
        textfont_size = 20,
        mode = 'markers+text',
        marker = {
            'size': 10,
            'opacity': 0.8,
            'color': 2
        }

    )
            
    data.append(trace)
    
# Configure the layout

    layout = go.Layout(
        margin = {'l': 0, 'r': 0, 'b': 0, 't': 0},
        showlegend=True,
        legend=dict(
        x=1,
        y=0.5,
        font=dict(
            family="Courier New",
            size=25,
            color="black"
        )),
        font = dict(
            family = " Courier New ",
            size = 15),
        autosize = False,
        width = 1000,
        height = 1000
        )


    plot_figure = go.Figure(data = data, layout = layout)
    plot_figure.show()
    
display_pca_scatterplot_3D(age2id, {}, model.layers[8].get_weights()[0])

# + colab={"base_uri": "https://localhost:8080/", "height": 1000} id="nrmBVhHqOQ06" outputId="fd365a70-e295-4b88-9d96-3e3c9fec5453"
import plotly
import numpy as np
import plotly.graph_objs as go
from sklearn.decomposition import PCA

def display_pca_scatterplot_3D(inp2id, id2inp, weights, user_input=None, words=None, label=None, color_map=None, topn=5, sample=10):
    
    
    three_dim = PCA(random_state=0).fit_transform(weights)[:,:3]
    # For 2D, change the three_dim variable into something like two_dim like the following:
    # two_dim = PCA(random_state=0).fit_transform(word_vectors)[:,:2]

    data = []
    count = 0
  
    trace = go.Scatter(
        x = three_dim[:,0], 
        y = three_dim[:,1],  
        #z = three_dim[:,2],
        text = list(inp2id.keys()),
        name = "Occupation",
        textposition = "top center",
        textfont_size = 20,
        mode = 'markers+text',
        marker = {
            'size': 10,
            'opacity': 0.8,
            'color': 2
        }

    )
            
    data.append(trace)
    
# Configure the layout

    layout = go.Layout(
        margin = {'l': 0, 'r': 0, 'b': 0, 't': 0},
        showlegend=True,
        legend=dict(
        x=1,
        y=0.5,
        font=dict(
            family="Courier New",
            size=25,
            color="black"
        )),
        font = dict(
            family = " Courier New ",
            size = 15),
        autosize = False,
        width = 1000,
        height = 1000
        )


    plot_figure = go.Figure(data = data, layout = layout)
    plot_figure.show()
    
display_pca_scatterplot_3D(occupation2id, {}, model.get_layer("occupation_embedding").get_weights()[0])

# + colab={"base_uri": "https://localhost:8080/"} id="frMxnhJfe2NT" outputId="900fa8c6-4580-44e0-ca3b-a290b1639e30"
######################################################
model = get_aux_model(num_users, num_items, len(info2id), mf_dim, layers, reg_layers, reg_mf)

if learner.lower() == "adagrad":
    model.compile(optimizer=Adagrad(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "rmsprop":
    model.compile(optimizer=RMSprop(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "adam":
    model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy')
else:
    model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy')
    print('sgd')

(hits, ndcgs) = evaluate_aux_model(model, validRatings, testNegatives, topK, evaluation_threads)
hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean()
print('Init: HR = %.4f, NDCG = %.4f' % (hr, ndcg))
best_hr, best_ndcg, best_iter = hr, ndcg, -1
best_test_hr,best_test_ndcg=-1,-1
    # Training model
all_loss = []
for epoch in range(num_epochs):
    t1 = time()
    # Generate training instances
    user_input, item_input_pos,item_input_neg, labels, aux_info = get_aux_train_instances(train, num_negatives)

    # Training
    hist = model.fit([np.array(user_input), np.array(item_input_pos),np.array(item_input_neg),np.array(aux_info)],  # input
                      np.array(labels),  # labels
                      batch_size=batch_size, epochs=1, verbose=0, shuffle=True)
    t2 = time()

    # model_out_file = 'Pretrain/%s_NeuPR_%d_%s_%d.h5' % (args.dataset, mf_dim, args.layers, epoch)
    # model_out_file = 'Pretrain/%s_NeuPR_%d.h5' % (args.dataset, mf_dim)

    if epoch % verbose == 0:
        (hits, ndcgs) = evaluate_aux_model(model, validRatings, testNegatives, topK, evaluation_threads)
        hits,ndcgs=np.array(hits),np.array(ndcgs)
        vhr, vndcg=hits[hits>=0].mean(), ndcgs[ndcgs>=0].mean()
        (hits, ndcgs) = evaluate_aux_model(model, testRatings, testNegatives, topK, evaluation_threads)
        hits, ndcgs = np.array(hits), np.array(ndcgs)
        hr, ndcg, loss = hits[hits>=0].mean(), ndcgs[ndcgs>=0].mean(), hist.history['loss'][0]
        all_loss.append(loss)
        print('Iteration %d [%.1f s]: [Valid HR = %.4f, NDCG = %.4f, loss=%.6f]\t[Test HR = %.4f, NDCG = %.4f], [%.1f s]'
              % (epoch, t2 - t1, vhr,vndcg,loss,hr,ndcg ,time() - t2))
        # print('Iteration %d [%.1f s]: Test HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]'
        #       % (epoch, t2 - t1, hr, ndcg, loss, time() - t2))
        if vhr > best_hr:
            best_hr, best_ndcg, best_iter = vhr, vndcg, epoch
            best_test_hr,best_test_ndcg=hr,ndcg

print("End. Best Iteration %d: Test HR = %.4f, NDCG = %.4f. " % (best_iter, best_test_hr, best_test_ndcg))
print('learning_rate: %.5f , num_factor: %d' % (learning_rate, mf_dim))

# + colab={"base_uri": "https://localhost:8080/", "height": 1000} id="Zb68rBWoesy4" outputId="9f537999-eabd-4f91-d48e-16c46f2b7bb1"
num_epochs = 20
batch_size = 256
mf_dim = 16
layers = [32,32,16,8]
reg_mf = 0
reg_layers = [0,0,0,0]
num_negatives = 2
# learning_rate = args.lr
learner = 'adam'
verbose = 1
topK = 10
evaluation_threads = 1#mp.cpu_count()
# print("NeuPR arguments: %s " %(args))

num_negatives=2
learning_rate = 0.0005

k=2 # k=[1,2,3,4,8]
layers=[e*k for e in layers]

learner='adam'
t1 = time()
dataset = DataSet()
dataset.loadClicks('./data/ml1m.txt', 10, 10)
train,validRatings, testRatings, testNegatives = dataset.trainMatrix,dataset.validRatings,dataset.testRatings, dataset.testNegatives
num_users, num_items = train.shape
print(train.shape)
print("Load data done [%.1f s]. #user=%d, #item=%d, #train=%d, #test=%d"
      % (time() - t1, num_users, num_items, train.nnz, len(testRatings)))

######################################################
model = get_model(num_users, num_items, mf_dim, layers, reg_layers, reg_mf)

if learner.lower() == "adagrad":
    model.compile(optimizer=Adagrad(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "rmsprop":
    model.compile(optimizer=RMSprop(lr=learning_rate), loss='binary_crossentropy')
elif learner.lower() == "adam":
    model.compile(optimizer=Adam(lr=learning_rate), loss='binary_crossentropy')
else:
    model.compile(optimizer=SGD(lr=learning_rate), loss='binary_crossentropy')
    print('sgd')

(hits, ndcgs) = evaluate_model(model, validRatings, testNegatives, topK, evaluation_threads)
hr, ndcg = np.array(hits).mean(), np.array(ndcgs).mean()
print('Init: HR = %.4f, NDCG = %.4f' % (hr, ndcg))
best_hr, best_ndcg, best_iter = hr, ndcg, -1
best_test_hr,best_test_ndcg=-1,-1
    # Training model
all_loss = []
for epoch in range(num_epochs):
    t1 = time()
    # Generate training instances
    user_input, item_input_pos,item_input_neg, labels = get_train_instances(train, num_negatives)

    # Training
    hist = model.fit([np.array(user_input), np.array(item_input_pos),np.array(item_input_neg)],  # input
                      np.array(labels),  # labels
                      batch_size=batch_size, epochs=1, verbose=0, shuffle=True)
    t2 = time()

    # model_out_file = 'Pretrain/%s_NeuPR_%d_%s_%d.h5' % (args.dataset, mf_dim, args.layers, epoch)
    # model_out_file = 'Pretrain/%s_NeuPR_%d.h5' % (args.dataset, mf_dim)

    if epoch % verbose == 0:
        (hits, ndcgs) = evaluate_model(model, validRatings, testNegatives, topK, evaluation_threads)
        hits,ndcgs=np.array(hits),np.array(ndcgs)
        vhr, vndcg=hits[hits>=0].mean(), ndcgs[ndcgs>=0].mean()
        (hits, ndcgs) = evaluate_model(model, testRatings, testNegatives, topK, evaluation_threads)
        hits, ndcgs = np.array(hits), np.array(ndcgs)
        hr, ndcg, loss = hits[hits>=0].mean(), ndcgs[ndcgs>=0].mean(), hist.history['loss'][0]
        all_loss.append(loss)
        print('Iteration %d [%.1f s]: [Valid HR = %.4f, NDCG = %.4f, loss=%.6f]\t[Test HR = %.4f, NDCG = %.4f], [%.1f s]'
              % (epoch, t2 - t1, vhr,vndcg,loss,hr,ndcg ,time() - t2))
        # print('Iteration %d [%.1f s]: Test HR = %.4f, NDCG = %.4f, loss = %.4f [%.1f s]'
        #       % (epoch, t2 - t1, hr, ndcg, loss, time() - t2))
        if vhr > best_hr:
            best_hr, best_ndcg, best_iter = vhr, vndcg, epoch
            best_test_hr,best_test_ndcg=hr,ndcg

print("End. Best Iteration %d: Test HR = %.4f, NDCG = %.4f. " % (best_iter, best_test_hr, best_test_ndcg))
print('learning_rate: %.5f , num_factor: %d' % (learning_rate, mf_dim))

# + colab={"base_uri": "https://localhost:8080/", "height": 500} id="2lEKe3iCgWOD" outputId="879e34cc-e0e9-4499-f4db-b90a218ff0e4"
import keras
keras.__version__

# + id="KyP-QdxbgkL1"


StarcoderdataJupyterScriptsDedupFiltered