class EntityScalarGroup(Group):
  """ Class representing a group of variables which are scalar and
      related to an entity.
  """ 

  def __init__(self, name, e_type, weight_param, var_param, var_H):
    """ Initializes the EntityScalarGroup object.

        Args:
          name: a string with the name of the group.
          e_type: a string with the entity type related to this group.
          weight_param: parameter object of regression weights.
          var_param: variance parameter of this variable's distribution.
          var_H: variance parameter of the response variable (helpfulness vote).

        Returns:
          None.
    """
    super(EntityScalarGroup, self).__init__(name, 1, e_type,
        weight_param, var_param, var_H)

  def add_instance(self, entity_id, features, votes):
    """ Adds an instance variable of this group with the appropriate Variable
        subclass.
    
        Args:
          entity_id: the id of the entity associated to the instance.
          features: an array of features regarding the entity,
          votes: list of votes (training set).
    
        Returns:
          None. The instance is included in the dictionary of instances of this
        object.
    """
    if entity_id in self.variables:
      return 
    self.variables[entity_id] = EntityScalarVariable(self.name, entity_id,
        self.e_type, features, votes)
    self.size += 1 class EntityArrayGroup(Group):
  """ Class representing a group of variables which are vectors and
      entity-related.
  """ 

  def __init__(self, name, shape, e_type, weight_param, var_param, var_H):
    """ Initializes the EntityArrayGroup object. 

        Args:
          name: a string with the name of the group.
          shape: an integer or tuple with the shape of each variable instance.
          e_type: a string with the entity name associated to the variable.
          weight_param: parameter object of regression weights.
          var_param: variance parameter of this variable's distribution.
          var_H: variance parameter of the response variable (helpfulness vote).
        
        Returns:
          None.
    """
    super(EntityArrayGroup, self).__init__(name, shape, e_type, 
        weight_param, var_param, var_H)

  def add_instance(self, entity_id, features, votes): 
    """ Adds an instance variable of this group with the appropriate Variable
        subclass.

        Args:
          entity_id: the id of the entity associated to the instance.
          features: an array of features regarding the entity,
          votes: list of votes (training set).
    
        Returns:
          None. The instance is included in the dictionary of instances of this
        object.
    """ 
    if entity_id in self.variables:
      return 
    self.variables[entity_id] = EntityArrayVariable(self.name, self.shape,
        entity_id, self.e_type, features, votes)
    self.size += 1 class InteractionScalarGroup(Group):
  """ Class representing a set of variables which are scalar and entity-related.
  """ 

  def __init__(self, name, e_type, weight_param, var_param, var_H):
    """ Initializes the InteractionScalarGroup object.

        Args:
          name: a string with the name of the group.
          e_type: a string with the entity type name related to this group.
          weight_param: parameter object of regression weights.
          var_param: variance parameter of this variable's distribution.
          var_H: variance parameter of the response variable (helpfulness vote).
      
        Returns:
          None.
    """
    super(InteractionScalarGroup, self).__init__(name, 1, e_type, weight_param,
        var_param, var_H)

  def add_instance(self, entity_id, features, votes): 
    """ Adds an instance variable of this group with the appropriate Variable
        subclass.

        Args:
          entity_id: the id of the entity associated to the instance.
          features: an array of features regarding the entity,
          votes: list of votes (training set).
    
        Returns:
          None. The instance is included in the dictionary of instances of this
        object.
    """ 
    if entity_id in self.variables:
      return 
    self.variables[entity_id] = InteractionScalarVariable(self.name,
        entity_id, self.e_type, features, votes)
    self.size += 1 class EntityScalarParameter(Parameter):
  """ Class specifying a  Parameter that is a collection of regression
      weights associated to an Entity Scalar Variable.
  """

  def __init__(self, name, shape):
    """ Constructor of EntityScalarParameter. 

        Args:
          name: string with the name of the parameter.
          shape: pair with vector or matrix dimensions.

        Returns:
          None.
    """
    super(EntityScalarParameter, self).__init__(name, shape)
  
  def optimize(self, variable_group):
    """ Optimizes the value of the weight parameter using the vector latent
        variables associated with it.
    
        Observations:
        - This optimization is the OLS for linear regression solution.
         
        Args:
          variable_group: variable group whose weight of the regression used for
            calculating the mean of the distribution is represented by this
            parameter.

        Returns:
          None, but the value field of the parameter is updated.
    """
    X = array([reshape(v.features, v.features.shape[0]) for v in
        variable_group.iter_variables()])
    y = array([v.empiric_mean for v in variable_group.iter_variables()])
    y = reshape(y, (y.shape[0], 1))
    new_value = pinv(const.ETA * identity(X.shape[1]) + 
        X.T.dot(X)).dot(X.T.dot(y))
    self.update(new_value) class EntityArrayParameter(Parameter):
  """ Class specifying a  Parameter, that is, collection of regression
      weights, and associated to an Entitiy Array Variable.
  """

  def __init__(self, name, shape):
    """ Constructor of EntityArrayParameter.
     
        Args:
          name: string with the name of the parameter.
          shape pair with matrix dimensions.

        Returns:
          None.
    """
    super(EntityArrayParameter, self).__init__(name, shape)
  
  def optimize(self, variable_group):
    """ Optimizes the value of the weight parameter using the vector latent
        variables associated with it.
    
        Observations:
        - This optimization is the OLS for linear regression solution.
        - Since the regression output is a vector, it is in fact a collection of
        regression problems.
        - We solve the transpose version of the OLS because we want the matrix
        of weights to be (K, f), were K is the number of latent dimensions and
        f the number of features.
        
        Args:
          variable_group: variable group whose weight of the regression used for
            calculating the mean of the distribution is represented by this
            parameter.

        Returns:
          None, but the value field of the parameter is updated.
    """
    X = array([v.features.reshape(-1) for v in variable_group.iter_variables()])
    y = array([v.empiric_mean.reshape(-1) for v in 
        variable_group.iter_variables()])
    new_value = y.T.dot(X).dot(pinv(const.ETA * identity(X.shape[1]) + 
        X.T.dot(X)))
    self.update(new_value) class InteractionScalarParameter(Parameter):
  """ Class specifying a  Parameter that is a collection of regression
      weights associated to an Interaction Scalar Variable modified by an
      indicator variable.
  """

  def __init__(self, name, shape):
    """ Constructor of InteractionParameter.

        Args:
          name: string with the name of the parameter.
          size: pair with vector or matrix dimensions.

        Returns:
          None.
    """
    super(InteractionScalarParameter, self).__init__(name, shape)
  
  def optimize(self, variable_group):
    """ Optimizes the value of the weight parameter using the indicated scalar
        latent variables associated with it.
    
        Observations:
        - This optimization is OLS, for linear regression solution. Since this
        problem is not a linear regression due to the sigmoid function, 
        the solution used is minimizing the error by finding the value of the
        derivative which is equal to zero. Since the parameter is a vector,
        finding the values of the system of linear equations is not easy and
        it is, thus, approximated by using Newton-Raphson method. 
        
        Args:
          variable_group: variable group whose weight of the regression used for
            calculating the mean of the distribution is represented by this
            parameter.

        Returns:
          None, but the value field of the parameter is updated.
    """
    if variable_group.get_size() > 0:
      new_value = newton_raphson(self.get_derivative_1, self.get_derivative_2, 
          variable_group, self.value)
      self.update(new_value) 

  def get_derivative_1(self, value, variable_group):
    """ Gets the first derivative of the expectation with respect to the
        parameter.
    
        Observations:
        - The expectation is the expectation of the log-likelihood with
        respect to the latent variables posterior distribution, found in the
        E-step of the EM method.

        Args:
          value: value of the parameter to calculate the derivative at this
            point.
          variable_group: variable group whose weight of the regression used for
            calculating the mean of the distribution is represented by this
            parameter.
    
        Returns:
          The derivative at point value.
    """
    der = zeros(value.shape)
    for variable in variable_group.iter_variables():
      f = variable.features
      dot = value.T.dot(f)[0,0]
      sig = sigmoid(dot)
      sig1 = sigmoid_der1(dot)
      der += (min(variable.value, 1.0) - sig) * sig1 * f
    der *= 1.0 / (variable_group.var_param.value)# * num_samples)
    return der

  def get_derivative_2(self, value, variable_group):
    """ Gets the second derivative of the expectation with respect to the
        parameter.

        Observations:
        - The expectation is the expectation of the log-likelihood with
        respect to the latent variables posterior distribution, found in the
        E-step of the EM method.

        Args:
          value: value of the parameter to calculate the derivative at this
            point.
          variable_group: variable group whose weight of the regression used for
            calculating the mean of the distribution is represented by this
            parameter.
    
        Returns:
          The derivative at point value.
    """
    der = zeros((value.shape[0], value.shape[0]))
    for variable in variable_group.iter_variables():
      f = variable.features
      dot = value.T.dot(f)[0,0]
      sig = sigmoid(dot)
      sig1 = sigmoid_der1(dot)
      sig2 = sigmoid_der2(dot)
      if variable.feat_matrix is None:
        variable.feat_matrix = f.dot(f.T)
      der += ((min(variable.value, 1.0) - sig) * sig2 - sig1 * sig1) * variable.feat_matrix 
    der *= 1.0 / (variable_group.var_param.value)
    return der  class ScalarVarianceParameter(Parameter):
  """ Class specifying a Scalar Parameter representing a distribution's
      variance of a scalar variable.  
  """

  def __init__(self, name):
    """ Constructor of ScalarVarianceParameter. 

        Args:
          name: string with the name of the parameter.

        Returns:
          None.
    """
    super(ScalarVarianceParameter, self).__init__(name, 1)
  
  def optimize(self, variable_group):
    """ Optimizes the value of the parameter using the scalar latent variables
        associated with it and the weight parameter.
    
        Args:
          variable_group: variable group whose variance of distribution is
            represented by this parameter.

        Returns:
          None, but the value field of the parameter is updated.
    """
    size = variable_group.get_size()
    if size > 0:
      w_value = variable_group.weight_param.value
      sse = 0
      var_sum = 0
      for v in variable_group.iter_variables():
        reg = w_value.T.dot(v.features)[0,0]
        sse += (v.empiric_mean - reg)**2
        var_sum += v.empiric_var
      self.update((float) (sse + var_sum) / size) class ArrayVarianceParameter(Parameter):
  """ Class specifying a Parameter representing a distribution's variance of an
      Array variable. A single value is used and the covariance is obtained by
      multiplying by the identity.
  """

  def __init__(self, name):
    """ Constructor of ArrayVarianceParameter.
    
        Args:
          name: string with the name of the parameter.

        Returns:
          None.
    """
    super(ArrayVarianceParameter, self).__init__(name, 1)
  
  def optimize(self, variable_group):
    """ Optimizes the value of the parameter using the vector latent variables
        associated with it and the weight parameter.
    
        Observations:
        - As the regression objective is a vector, there are in fact multiple
          regressions. However, there is only one variance parameter. 

        Args:
          variable_group: variable group whose variance of distribution is
            represented by this parameter.

        Returns:
          None, but the value field of the parameter is updated.
    """
    size = variable_group.get_size() * variable_group.get_shape()[0]
    w_value = variable_group.weight_param.value
    sse = 0
    var_sum = 0
    for v in variable_group.iter_variables():
      reg = w_value.dot(v.features)
      sse += sum(((v.empiric_mean - reg)**2).reshape(-1).tolist())
      var_sum += sum(v.empiric_var.reshape(-1).tolist())
    self.update(float(sse + var_sum) / size) class PredictionVarianceParameter(ScalarVarianceParameter):
  """ Class specifying the Prediction Parameter, the variance of the true values
      of votes considering the predicted values as means.
  """

  def __init__(self, name):
    """ Constructor of PredictionVarianceParameter. 

        Args:
          name: string with the name of the parameter.

        Returns:
          None.
    """
    super(PredictionVarianceParameter, self).__init__(name)
  
  def optimize(self, groups, votes):
    """ Optimizes the value of the parameter using the prediction and truth
        values.
   
        Observations: 
        - The variance is calculated using RSS (residual sum of squares) of
          predicted and true values of helpfulness votes and empirical variance
          of predicted values.

        Args:
          groups: list of variable group which determines the predicted value.
          votes: list of votes in training set.
           
        Returns:
          None, but the value field of the parameter is updated.
    """
    size = len(votes) 
    sse = 0 # same as rss
    var_sum = 0
    for vote in votes:
      truth = vote['vote']
      pred = 0
      num_samples = groups.values()[0].get_num_samples()
      pred_samples = [0.0] * num_samples
      for g in groups.itervalues():
        inst = g.get_instance(vote)
        if g.pair_name:
          if g.name > g.pair_name:
            continue
          pair_inst = groups[g.pair_name].get_instance(vote)
          pred += inst.empiric_mean.T.dot(pair_inst.empiric_mean)[0,0]
          for i in xrange(num_samples):
            pred_samples[i] += inst.samples[i].T.dot(pair_inst.samples[i])[0,0]
        elif inst:
          pred += inst.empiric_mean
          for i in xrange(num_samples):
            pred_samples[i] += inst.samples[i]
      var_sum += std(pred_samples, ddof=1) ** 2
      sse += (truth - pred) ** 2
    self.update((float) (sse + var_sum) / size) K = 5 # size of user and review latent vectors
EM_ITER = [10]
BURN_IN = [0]
SAMPLES = [20]
NR_ITER = 50
NR_TOL = 1e-4
NR_STEP = 0.1     # In paper: 1
ETA = 0  def expectation_maximization(groups, votes):
  """ Expectation Maximization algorithm for CAP baseline. Iterates over E and
      M-steps, fitting latent variables and parameters, respectively.
      
      Observation:
      - The number of iterations was split in three stages. Each one has a 
      number of gibbs samples to compute. This was suggested in RLFM paper
      (http://dl.acm.org/citation.cfm?id=1557029).

      Args:
        groups: dictionary of Group of variables objects.
        votes: list of votes, each one represented as a dictionary, which is the
      training data.
       
      Returns:
        None. Variable and Parameter objects are changes in place.
  """
  for stage, num_iter in enumerate(const.EM_ITER):
    print 'Stage %d' % stage
    for i in xrange(num_iter):
      print 'EM iteration %d' % i
      print 'E-step'
      e_time = time()
      perform_e_step(groups, votes, const.SAMPLES[stage], const.BURN_IN[stage])
      print 'E-step Time:\t%f' % (time() - e_time)
      print 'M-step'
      m_time = time()
      perform_m_step(groups, votes)
      print 'M-step Time:\t%f' % (time() - m_time)
      print 'Total:\t\t%f' % (time() - e_time)
      print '------------------------' def map_features(votes, reviews, users, users_sim, users_conn, trusts):
  """ Maps all features related to each vote, in the same order.

      Args:
        votes: list of vote dictionaries.
        reviews: dictionary of review dictionaries indexed by review id.
        users: dictionary of user dictionaries indexed by user id.
        users_sim: dictionary of user similarity dictionaries indexed by a
          2-tuple of user ids.
        users_conn: dictionary of user connection dictionaries indexed by a
          2-tuple of user ids.
        trusts: networkx DiGraph with trust network.

      Returns:
        A dictionary of features indexed by related entity name (e.g.: voter)
      and containing a list with a feature array for each vote.
  """
  avg_user = compute_avg_user(users)
  avg_sim = compute_avg_model(users_sim)
  avg_conn = compute_avg_model(users_conn)
  features = {'review': [], 'author': [], 'voter': [], 'sim': [], 'conn': []}
  for vote in votes:
    r_id, a_id, v_id = vote['review'], vote['author'], vote['voter']
    r_feat = map_review_features(reviews[r_id])
    features['review'].append(r_feat)
    author = users[a_id] if a_id in users else avg_user
    a_feat = map_author_features(author, avg_user)
    features['author'].append(a_feat)
    voter = users[v_id] if v_id in users else avg_user
    v_feat = map_voter_features(voter, avg_user)
    features['voter'].append(v_feat)
    if v_id in users and a_id in users[v_id]['similars']:
      if (a_id, v_id) in users_sim:
        sim = users_sim[(a_id, v_id)]
        sim_feat = map_users_sim_features(sim, avg_sim)
        features['sim'].append(sim_feat)
    if v_id in trusts and a_id in trusts[v_id]:
      if (a_id, v_id) in users_conn:
        conn = users_conn[(a_id, v_id)]
        conn_feat = map_users_conn_features(conn, avg_conn)
        features['conn'].append(conn_feat)
  return features NUM_SETS = 5 RANK_SIZE = 5 REP = 3 def calculate_rmse(result, truth):
  """ Calculates RMSE (Root Mean Squared Error) of a prediction task.

      Args:
        result: a list of predicted values assigned to the instances.
        truth: a list with the corrected values.

      Returns:
        A real value representing the RMSE.
  """
  errors = calculate_squared_errors(result, truth)
  rmse = sqrt(float(sum(errors)) / len(errors))
  return rmse def calculate_avg_ndcg(votes, reviews, pred, truth, k):
  """ Calculates the average nDCG by grouping votes in (user, product) pairs in
      order to compose rankings.

      Args:
        votes: set of votes to evaluate through grouping and ranking.
        reviews: dictionary of reviews.
        pred: list of floats with predicted relevances for each vote in votes.
        truth: list of floats (or integers) with true relevances for each vote
      in votes.
        k: an integer with the limit position of the ranking to calculate the
      score.

      Returns:
        A float in range [0, 1] with nDCG@K score.
  """
  pred_group = {}
  truth_group = {}
  for i, vote in enumerate(votes):
    voter = vote['voter']
    product = reviews[vote['review']]['product']
    key = (voter, product)
    if key not in pred_group:
      pred_group[key] = []
      truth_group[key] =[]
    pred_group[key].append(pred[i])
    truth_group[key].append(truth[i])
  score_sum = 0.0
  for key in pred_group:
    ndcg = calculate_ndcg(pred_group[key], truth_group[key], k)
    score_sum += ndcg
  score = score_sum / len(pred_group)
  return score def sigmoid(value):
  """ Computes the sigmoid function applied to value. The function corresponds
      to 1 / (1 + exp(-value))

      Observation:
      - If an overflow or underflow is obtained ate each step of the
        computation, then a asymptotic approximate value is returned.

      Args:
        value: a float

      Returns:
        A float with the function return value.
  """
  return expit(value) def compute_avg_user(users):
  """ Creates an average user model for mean imputation.

      Args:
        users: the modeled dictionary of users.

      Returns:
        A dictionary representing a user with average feature values.
  """
  avg_user = {}
  for feature in users.itervalues().next():
    avg_user[feature] = 0.0
  missing = []
  for feature in avg_user:
    if feature in ['id', 'ratings', '_id', 'similars', 'trustees', 'trustors']:
      continue
    avg_user[feature] = 0.0
    count = 0
    for user in users.itervalues():
      if not isnan(user[feature]):
        try:
          avg_user[feature] += user[feature]
        except:
          print feature
          print avg_user
          print user
        count += 1
    if count == 0:
      missing.append(feature)
    else:
      avg_user[feature] /= float(count)
  for feature in missing:
    if feature == 'avg_help_giv_sim' or feature == 'avg_help_giv_tru_net':
      avg_user[feature] = avg_user['avg_help_giv']
    elif feature == 'avg_rating_sim' or feature == 'avg_rating_fir_net':
      avg_user[feature] = avg_user['avg_rating']
  avg_user['id'] = avg_user['_id'] = None
  avg_user['ratings'] = {}
  avg_user['similars'] = []
  avg_max = []
  avg_min = []
  for user in users.itervalues():
    if user['ratings']:
      avg_max.append(max(user['ratings'].values()))
      avg_min.append(min(user['ratings'].values()))
  avg_user['avg_max'] = mean(avg_max)
  avg_user['avg_min'] = mean(avg_min)
  return avg_user def compute_avg_model(model):
  """ Creates an average user model for mean imputation.

      Args:
        users: the modeled dictionary of users.

      Returns:
        A dictionary representing a user with average feature values.
  """
  avg_entity = {}
  for feature in model.itervalues().next():
    avg_entity[feature] = 0.0
  for feature in avg_entity:
    count = 0.0
    for user in model.itervalues():
      if not isnan(user[feature]):
        avg_entity[feature] += user[feature]
        count += 1.0
    avg_entity[feature] /= count
  return avg_entity def fit_scaler(scale_type, data):
  """ Fits a scaler to a data.

      Args:
        scale_type: indicates the type of scale to adopt. It can be 'standard' to 
          scale with zero mean and unit standard deviation, or 'minmax' for range
          between 0 and 1.
        data: list of arrays with instances to be scaled.

      Returns:
        A scaler that fits the data.
  """
  if scale_type == 'standard':
    return StandardScaler(copy=False).fit(data)
  if scale_type == 'minmax':
    return MinMaxScaler(copy=False).fit(data) def scale_features(scaler, data, qid=None, qid_dep_size=None, 
    scale_type='minmax'):
  """ Scales features from data given a fitted scaler. 

      Args:
        scaler: a scaler object with transform function.
        data: list of instances to scale. 
        qid: list of query ids associated to each instance.
        qid_dep_size: number of features query-dependent, which are in the end
          of the instance array of features.
        scale_type: indicates the type of scale to adopt. It can be 'standard' to 
          scale with zero mean and unit standard deviation, or 'minmax' for range
          between 0 and 1.

      Returns:
        A pair with scaled train and test sets. 
  """
  data = array(data)
  if qid is None:
    data = scaler.transform(data)
  else:
    dim = data.shape[1]
    overall_scaler = scaler 
    q_undep, q_dep = hsplit(data, [dim-qid_dep_size])
    q_undep = overall_scaler.transform(q_undep)
    qid_grouped = group_by_qid(q_dep, qid)
    q_scalers = {}
    for q in qid_grouped: 
      if scale_type == 'minmax':
        q_scalers[q] = MinMaxScaler(copy=False).fit(qid_grouped[q])
      if scale_type == 'standard':
        q_scalers[q] = StandardScaler(copy=False).fit(qid_grouped[q])
    for i in xrange(q_dep.shape[0]):
      q_dep[i] = q_scalers[qid[i]].transform([q_dep[i]])
    data = hstack((q_undep, q_dep))
  return data 
from math import sqrt
from sys import argv, exit
from numpy import zeros, isnan
from pickle import load
from algo.cap.models import EntityScalarGroup, EntityArrayGroup, \
    InteractionScalarGroup, EntityScalarParameter, EntityArrayParameter, \
    InteractionScalarParameter, ScalarVarianceParameter, \
    ArrayVarianceParameter, PredictionVarianceParameter
from algo.cap import const
from algo.cap.em import expectation_maximization
from algo.cap.map_features import map_features 
from algo.const import NUM_SETS, RANK_SIZE, REP 
from perf.metrics import calculate_rmse, calculate_avg_ndcg
from util.aux import sigmoid
from util.avg_model import compute_avg_user, compute_avg_model
from util.scaling import fit_scaler, scale_features
  if features['sim']:
    features['sim'] = scale_features(scaler['sim'], features['sim'])
  if features['conn']:
    features['conn'] = scale_features(scaler['conn'], features['conn'])
  return features


def populate_variables(var_groups, train, users, trusts, features):
  """ Populates the dictionary of Group objects by adding instances
      related to an entity.
  
      Args:
        var_groups: a dictionary of Group objects indexed by group names.
        train: list of votes in training set.
        users: dictionary o users. 
        trusts: networkx DiGraph with trust network. 
        features: dictionary of a list of feature arrays, indexed by entity or
      interaction id and containing features for each vote in training.

      Returns:
        The same dictionary of Group objects with instances added.
  """
  sim_i = 0
  conn_i = 0
  for i, vote in enumerate(train):
    # features are added in the same order of ids votes in train
    r_id, a_id, v_id = vote['review'], vote['author'], vote['voter']
    var_groups['alpha'].add_instance(v_id, features['voter'][i], train)
    var_groups['beta'].add_instance(r_id, features['review'][i], train) 
    var_groups['xi'].add_instance(a_id, features['author'][i], train)
    var_groups['u'].add_instance(v_id, features['voter'][i], train) 
    var_groups['v'].add_instance(r_id, features['review'][i], train) 
    if v_id in users and a_id in users[v_id]['similars']:
      var_groups['gamma'].add_instance((a_id, v_id), features['sim'][sim_i],
          train)
      sim_i += 1
    if v_id in trusts and a_id in trusts[v_id]:
      var_groups['lambda'].add_instance((a_id, v_id), features['conn'][conn_i],
          train)
      conn_i += 1
  return var_groups


def calculate_predictions(groups, test, users, trusts, features, sim, conn):
  """ Calculates the predictions after fitting values. If the vote to be
      predicted contains entities modeled as latent variables (i.e., present
      on training set), the latent variable is used; otherwise, it is
      approximated by linear regression over features.

      Args:
        groups: dictionary of Group objects.
        test: list of vote dictionaries on test set.
        users: dictionary of user dictionaries.
        trusts: networkx DiGraph with trust network. 
        features: dictionary of a list of feature arrays, indexed by entity or
      interaction id and containing features for each vote in training.
        sim: dictionary of similarity of users dictionaries.
        conn: dictionary of connection of users dictionaries.

      Returns:
        A list of floats containing prediction values for each vote in test, in
      the same order.
  """
  pred = []
  ignored = 0
  sim_i = 0
  conn_i = 0
  for i, vote in enumerate(test):
    v_feat = features['voter'][i]
    v_feat = v_feat.reshape((v_feat.size, 1))
    alfa = groups['alpha'].get_instance(vote).value if \
          groups['alpha'].contains(vote) else groups['alpha'].weight_param.value.T \
          .dot(v_feat)[0,0]
    u = groups['u'].get_instance(vote).value if groups['u'].contains(vote) \
        else groups['u'].weight_param.value.dot(v_feat)
    r_feat = features['review'][i]
    r_feat = r_feat.reshape((r_feat.size, 1))
    beta = groups['beta'].get_instance(vote).value if \
        groups['beta'].contains(vote) else groups['beta'].weight_param.value.T \
        .dot(r_feat)[0,0]
    v = groups['v'].get_instance(vote).value if groups['v'].contains(vote) \
        else groups['v'].weight_param.value.dot(r_feat)
    a_feat = features['author'][i]
    a_feat = a_feat.reshape((a_feat.size, 1))
    xi = groups['xi'].get_instance(vote).value if \
        groups['xi'].contains(vote) else groups['xi'].weight_param.value.T \
        .dot(a_feat)[0,0]
    a_id, v_id = vote['author'], vote['voter']
    gamma = 0.0
    if v_id in users and a_id in users[v_id]['similars'] and (a_id, v_id) in sim:
      sim_feat = features['sim'][sim_i]
      sim_feat = sim_feat.reshape((sim_feat.size, 1))
      gamma = groups['gamma'].get_instance(vote).value if \
          groups['gamma'].contains(vote) else \
          sigmoid(groups['gamma'].weight_param.value.T.dot(sim_feat)[0,0])
      sim_i += 1
    lambd = 0.0
    if v_id in trusts and a_id in trusts[v_id] and (a_id, v_id) in conn:
      conn_feat = features['conn'][conn_i]
      conn_feat = conn_feat.reshape((conn_feat.size, 1))
      lambd = groups['lambda'].get_instance(vote).value if \
          groups['lambda'].contains(vote) else \
          sigmoid(groups['lambda'].weight_param.value.T.dot(conn_feat)[0,0])
      conn_i += 1
    prediction = u.T.dot(v)[0,0] + alfa + beta + xi + gamma + lambd
    pred.append(prediction)
  return pred


def main():
  """ Main method performing fitting, prediction and outputting to file.

      Args:
        None.

      Returns:
        None.
  """
  load_args()
  

  for i in xrange(NUM_SETS):