def computeGradientLogZ(self):
   if 'grad_logZ' in self.__dict__:
     return
   self.computeLogZ()
   N = len(self.theta)
   L = self.traj.L
   assert not np.isnan(self.theta).any()
   log_grad_Zs0_dirs = self.traj.features_np[0]
   log_grad_Zs0_norms = dot(self.traj.features_np[0], self.theta)
   self.log_grad_Zs_norms = [log_grad_Zs0_norms]
   self.log_grad_Zs_dirs = [log_grad_Zs0_dirs]
   # Recursion:
   for l in range(1, L):
     N_l = self.traj.num_choices[l]
     l_vec_dirs = np.zeros((N_l, N))
     l_vec_norms = MINUS_INF * np.ones(N_l)
     conns_back = self.traj.connections_backward[l]
     w = dot(self.traj.features_np[l], self.theta)
     assert not np.isnan(w).any()
     for i in range(N_l):
       vs0_dir = [self.traj.features_np[l][i]]
       vs0_norm = [self.logZs[l][i]]
       if i not in conns_back:
         (n, d) = lse_vec_2(vs0_dir, vs0_norm)
         l_vec_dirs[i] = d
         l_vec_norms[i] = n
       else:
         vs_dirs = [vs0_dir, \
                            self.log_grad_Zs_dirs[l-1][conns_back[i]]]
         vs_norms = [vs0_norm, \
                               w[i] + \
                                 self.log_grad_Zs_norms[l-1][conns_back[i]]]
         (n, d) = lse_vec_2(vs_dirs, vs_norms)
         l_vec_dirs[i] = d
         l_vec_norms[i] = n
     self.log_grad_Zs_norms.append(l_vec_norms)
     self.log_grad_Zs_dirs.append(l_vec_dirs)
   assert(len(self.log_grad_Zs_norms) == L)
   self.log_grad_Z = lse_vec_2(self.log_grad_Zs_dirs[L-1], 
                               self.log_grad_Zs_norms[L-1])
   (l_norm, v) = self.log_grad_Z
   if l_norm < 100 and l_norm > -100:
     self.grad_Z = exp(l_norm) * v
   self.grad_logZ = exp(l_norm - self.logZ) * v
 def computeHessianLogZ(self):
   """ Hessian of log(Z). """
   if 'hess_logZ' in self.__dict__:
     return
   inf = float('inf')
   self.computeLogZ()
   self.computeGradientLogZ()
   N = len(self.theta)
   L = self.traj.L
   # The initial values:
   N_0 = self.traj.num_choices[0]
   log_hess_Zs0_norms = np.dot(self.traj.features_np[0], self.theta)
   log_hess_Zs0_dirs = np.zeros((N_0, N, N))
   for i in range(N_0):
     log_hess_Zs0_dirs[i] = np.outer(self.traj.features_np[0][i], \
                                     self.traj.features_np[0][i])
   self.log_hess_Zs_norms = [log_hess_Zs0_norms]
   self.log_hess_Zs_dirs = [log_hess_Zs0_dirs]
   # Recursion:
   for l in range(1, L):
     N_l = self.traj.num_choices[l]
     l_vec_norm = -inf * np.ones(N_l)
     l_vec_dir = np.zeros((N_l, N, N))
     conns_back = self.traj.connections_backward[l]
     w = dot(self.traj.features_np[l], self.theta)
     for i in range(N_l):
       T_i_l = self.traj.features_np[l][i]
       vs0_norm = np.array([self.logZs[l][i]])
       vs0_dir = np.array([outer(T_i_l, T_i_l)])
       if i in conns_back:
         us_norm = self.log_grad_Zs_norms[l-1][conns_back[i]]
         us_dir = self.log_grad_Zs_dirs[l-1][conns_back[i]]
         (l_norm, u_g_vec) = lse_vec_2(us_dir, us_norm)
         vs_norm = (vs0_norm, \
                              w[i] + \
                                self.log_hess_Zs_norms[l-1][conns_back[i]], \
                              w[i] + l_norm, \
                              w[i] + l_norm)
         M = np.array([outer(u_g_vec, T_i_l)])
         Mt = np.array([outer(T_i_l, u_g_vec)])
         vs_dir = (vs0_dir, \
                             self.log_hess_Zs_dirs[l-1][conns_back[i]], \
                             M, Mt)
         (li_vec_norm, li_vec_dir) = lse_vec_2(vs_dir, vs_norm)
         l_vec_norm[i] = li_vec_norm
         l_vec_dir[i] = li_vec_dir          
       else:
         l_vec_norm[i] = vs0_norm
         l_vec_dir[i] = vs0_dir
     self.log_hess_Zs_dirs.append(l_vec_dir)
     self.log_hess_Zs_norms.append(l_vec_norm)
   assert(len(self.log_hess_Zs_dirs) == L)
   self.log_hess_Z = lse_vec_2(self.log_hess_Zs_dirs[-1], \
                                 self.log_hess_Zs_norms[-1])
   (l_norm, h) = self.log_hess_Z
   if l_norm < 100 and l_norm > -100:
     self.hess_Z = exp(l_norm) * h
   (l_norm_g, g) = self.log_grad_Z
   self.hess_logZ = np.zeros_like(h)
   if l_norm - self.logZ > -60: 
     self.hess_logZ += exp(l_norm - self.logZ) * h
   if l_norm_g - self.logZ > -30:
     self.hess_logZ -= exp(2 * l_norm_g - 2 * self.logZ) * outer(g, g)