# Path: algo/cap/const.py
# 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 
# 
# Path: algo/cap/newton_raphson.py
# def newton_raphson(fun, der, variable_group, theta_0, n_iter=NR_ITER, eps=NR_TOL, 
#     step=NR_STEP):
#   """ Applies Newton-Raphson's Method. This method finds an approximation for a 
#       root of a function by continuously updating according to the
#       derivative and the function at the current value of the variable. 
#       
#       Observation:
#       - In CAP, it is used for finding the optimal value of a function by
#       calculating an approximation of the root of its derivative, thus it uses
#       the first and the second order derivative.
# 
#       Args:
#         fun: function which evaluates over theta to calculate the root of.
#         der: function which evaluates over theta and represents the derivative.
#         theta_0: value of initial theta.
#         n_iter: number of iterations to perform.
#         eps: tolerance for difference to zero.
#         step: update rate.
# 
#       Returns:
#         The approximated value for the root of the function.
#   """
#   der_val = der(theta_0, variable_group)
#   fun_val = fun(theta_0, variable_group)
#   if any(isnan(der_val)) or any(isinf(der_val)):
#     return theta_0
#   der_inv = pinv(der_val)
#   dot = der_inv.dot(fun_val)
#   theta = theta_0 - step * dot
#   der_val = der(theta, variable_group)
#   old_fun_val = float('inf')
#   fun_val = fun(theta, variable_group)
#   i = 1
#   while i < n_iter and not allclose(fun_val, old_fun_val, atol=eps):
#     if any(isnan(der_val)) or any(isinf(der_val)):
#       return theta
#     der_inv = pinv(der_val)
#     dot = der_inv.dot(fun_val)
#     new_theta = theta - step * dot
#     theta = new_theta
#     der_val = der(theta, variable_group)
#     old_fun_val = fun_val
#     fun_val = fun(theta, variable_group)
#     i += 1
#   return theta
# 
# Path: util/aux.py
# 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 sigmoid_der1(value):
#   """ Computes the sigmoid function applied to value. The function corresponds
#       to exp(value) / (1 + exp(value)) ** 2
# 
#       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.
#   """
#   e_val = expit(value)
#   return e_val * (1 - e_val)
# 
# def sigmoid_der2(value):
#   """ Computes the sigmoid function applied to value. The function corresponds
#       to - exp(value) * (exp(value) - 1) / (1 + exp(value)) ** 3
# 
#       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.
#   """
#   e_val = expit(value)
#   return e_val * (2 * e_val ** 2 - 3 * e_val + 1)


from numpy import array, reshape, mean, std, identity, zeros, ones
from numpy.random import uniform
from scipy.linalg import pinv2 as pinv
from algo.cap import const
from algo.cap.newton_raphson import newton_raphson
from util.aux import sigmoid, sigmoid_der1, sigmoid_der2
      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)

# Path: algo/cap/const.py
# 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 
# 
# Path: algo/cap/newton_raphson.py
# def newton_raphson(fun, der, variable_group, theta_0, n_iter=NR_ITER, eps=NR_TOL, 
#     step=NR_STEP):
#   """ Applies Newton-Raphson's Method. This method finds an approximation for a 
#       root of a function by continuously updating according to the
#       derivative and the function at the current value of the variable. 
#       
#       Observation:
#       - In CAP, it is used for finding the optimal value of a function by
#       calculating an approximation of the root of its derivative, thus it uses
#       the first and the second order derivative.
# 
#       Args:
#         fun: function which evaluates over theta to calculate the root of.
#         der: function which evaluates over theta and represents the derivative.
#         theta_0: value of initial theta.
#         n_iter: number of iterations to perform.
#         eps: tolerance for difference to zero.
#         step: update rate.
# 
#       Returns:
#         The approximated value for the root of the function.
#   """
#   der_val = der(theta_0, variable_group)
#   fun_val = fun(theta_0, variable_group)
#   if any(isnan(der_val)) or any(isinf(der_val)):
#     return theta_0
#   der_inv = pinv(der_val)
#   dot = der_inv.dot(fun_val)
#   theta = theta_0 - step * dot
#   der_val = der(theta, variable_group)
#   old_fun_val = float('inf')
#   fun_val = fun(theta, variable_group)
#   i = 1
#   while i < n_iter and not allclose(fun_val, old_fun_val, atol=eps):
#     if any(isnan(der_val)) or any(isinf(der_val)):
#       return theta
#     der_inv = pinv(der_val)
#     dot = der_inv.dot(fun_val)
#     new_theta = theta - step * dot
#     theta = new_theta
#     der_val = der(theta, variable_group)
#     old_fun_val = fun_val
#     fun_val = fun(theta, variable_group)
#     i += 1
#   return theta
# 
# Path: util/aux.py
# 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 sigmoid_der1(value):
#   """ Computes the sigmoid function applied to value. The function corresponds
#       to exp(value) / (1 + exp(value)) ** 2
# 
#       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.
#   """
#   e_val = expit(value)
#   return e_val * (1 - e_val)
# 
# def sigmoid_der2(value):
#   """ Computes the sigmoid function applied to value. The function corresponds
#       to - exp(value) * (exp(value) - 1) / (1 + exp(value)) ** 3
# 
#       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.
#   """
#   e_val = expit(value)
#   return e_val * (2 * e_val ** 2 - 3 * e_val + 1)


# Path: algo/cap/models.py
from numpy import array, reshape, mean, std, identity, zeros, ones
from numpy.random import uniform
from scipy.linalg import pinv2 as pinv
from algo.cap import const
from algo.cap.newton_raphson import newton_raphson
from util.aux import sigmoid, sigmoid_der1, sigmoid_der2

      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)