planc
Parallel Lowrank Approximation with Non-negativity Constraints
ntfnes.hpp
Go to the documentation of this file.
1 /* Copyright Ramakrishnan Kannan 2018 */
2 
3 #ifndef NTF_NTFNES_HPP_
4 #define NTF_NTFNES_HPP_
5 
6 #include "ntf/auntf.hpp"
7 
8 namespace planc {
9 class NTFNES : public AUNTF {
10  private:
11  // Update Variables
12  NCPFactors m_prox_t; // Proximal Term (H_*)
13  NCPFactors m_acc_t; // Acceleration term (Y)
14  NCPFactors m_grad_t; // Gradient Term (\nabla_f(Y))
15  NCPFactors m_prev_t; // Previous Iterate
16  MAT modified_gram;
17  // Termination Variables
18  double delta1; // Termination value for absmax
19  double delta2; // Termination value for min
20  // Acceleration Variables
21  int acc_start; // Starting iteration for acceleration
22  int acc_exp; // Step size exponent
23  int acc_fails; // Acceleration Fails
24  int fail_limit; // Acceleration Fail limit
25 
26  protected:
27  inline double get_lambda(double L, double mu) {
28  double q = L / mu;
29  double lambda = 0.0;
30 
31  if (q > 1e6)
32  lambda = 10 * mu;
33  else if (q > 1e3)
34  lambda = mu;
35  else
36  lambda = mu / 10;
37 
38  return lambda;
39  }
40 
41  inline double get_alpha(double alpha, double q) {
42  /* Solves the quadratic equation
43  x^2 + (\alpha^2 -q)x - \alpha^2 = 0
44  */
45  double a, b, c, D, x;
46  a = 1.0;
47  b = alpha * alpha - q;
48  c = -alpha * alpha;
49  D = b * b - 4 * a * c;
50  x = (-b + sqrt(D)) / 2;
51  return x;
52  }
53 
54  bool stop_iter(const int mode) {
55  bool stop = false;
56  double absmax, amin;
57 
58  absmax = (arma::abs(m_grad_t.factor(mode) % m_acc_t.factor(mode))).max();
59  amin = (m_grad_t.factor(mode)).min();
60 
61  if (absmax <= delta1 && amin >= -delta2) stop = true;
62 
63  return stop;
64  }
65 
66  void accelerate() {
67  int iter = this->current_it();
68  if (iter > acc_start) {
69  int num_modes = m_prox_t.modes();
70  double cur_err = this->current_error();
71 
72  double acc_step = std::pow(iter + 1, (1.0 / acc_exp));
73  // Adjust all the factors
74  // Reusing gradient/acceleration term to save memory
75  m_grad_t.zeros();
76  int lowrank = m_prox_t.rank();
77  MAT scalecur = arma::eye<MAT>(lowrank, lowrank);
78  MAT scaleprev = arma::eye<MAT>(lowrank, lowrank);
79  for (int mode = 0; mode < num_modes; mode++) {
80  // Make only the last mode unnormalised
81  if (mode == num_modes - 1) {
82  scalecur = arma::diagmat(this->m_ncp_factors.lambda());
83  scaleprev = arma::diagmat(m_prev_t.lambda());
84  }
85  // Step Matrix
86  MAT acc_mat = m_grad_t.factor(mode);
87  acc_mat = (scalecur * this->m_ncp_factors.factor(mode).t()) -
88  (scaleprev * m_prev_t.factor(mode));
89  acc_mat *= acc_step;
90  acc_mat += (scalecur * this->m_ncp_factors.factor(mode).t());
91  m_acc_t.set(mode, acc_mat);
92  m_acc_t.normalize_rows(mode);
93  }
94  // Compute Error
95  double acc_err = this->computeObjectiveError(m_acc_t);
96 
97  // Acceleration Accepted
98  if (acc_err < cur_err) {
99  // Set proximal term to current term
100  for (int mode = 0; mode < num_modes; mode++) {
101  m_prox_t.set(mode, m_acc_t.factor(mode));
102  m_prox_t.normalize_rows(mode);
103  }
104  INFO << "Acceleration Successful::relative_error::" << acc_err
105  << std::endl;
106  } else {
107  // Acceleration Failed reset to prev iterate
108  this->reset(m_prox_t, true);
109  acc_fails++;
110  if (acc_fails > fail_limit) {
111  acc_fails = 0;
112  acc_exp++;
113  }
114  INFO << "Acceleration Failed::relative_error::" << acc_err << std::endl;
115  }
116  }
117  }
118 
119  MAT update(const int mode) {
120  double L, mu, lambda, q, alpha, alpha_prev, beta;
121  if (mode == 0) {
122  m_prev_t.set_lambda(m_prox_t.lambda());
123  }
124  m_prev_t.set(mode, m_prox_t.factor(mode));
125  MAT Ht(m_prox_t.factor(mode));
126  MAT Htprev = Ht;
127  m_acc_t.set(mode, Ht);
128  modified_gram = this->gram_without_one;
129 
130  VEC eigval = arma::eig_sym(modified_gram);
131  L = eigval.max();
132  mu = eigval.min();
133  lambda = get_lambda(L, mu);
134  modified_gram.diag() += lambda;
135 
136  q = (mu + lambda) / (L + lambda);
137 
138  MAT modified_local_mttkrp_t =
139  (-1 * lambda) * m_acc_t.factor(mode) - this->ncp_mttkrp_t[mode];
140 
141  alpha = 1;
142  alpha_prev = 1;
143  beta = 1;
144 
145  while (true) {
146  m_grad_t.set(mode, modified_local_mttkrp_t +
147  (modified_gram * m_acc_t.factor(mode)));
148  if (stop_iter(mode)) break;
149 
150  Htprev = Ht;
151  Ht = m_acc_t.factor(mode) - ((1 / (L + lambda)) * m_grad_t.factor(mode));
152  fixNumericalError<MAT>(&Ht, EPSILON_1EMINUS16);
153  Ht.for_each([](MAT::elem_type &val) { val = val > 0.0 ? val : 0.0; });
154  alpha_prev = alpha;
155  alpha = get_alpha(alpha_prev, q);
156  beta =
157  (alpha_prev * (1 - alpha_prev)) / (alpha + alpha_prev * alpha_prev);
158  m_acc_t.set(mode, Ht + beta * (Ht - Htprev));
159  }
160 
161  m_prox_t.set(mode, Ht);
162  m_prox_t.normalize_rows(mode);
163 
164  return Ht;
165  }
166 
167  public:
168  NTFNES(const Tensor &i_tensor, const int i_k, algotype i_algo)
169  : AUNTF(i_tensor, i_k, i_algo),
170  m_prox_t(i_tensor.dimensions(), i_k, true),
171  m_acc_t(i_tensor.dimensions(), i_k, true),
172  m_grad_t(i_tensor.dimensions(), i_k, true),
173  m_prev_t(i_tensor.dimensions(), i_k, true) {
174  m_prox_t.zeros();
175  m_prev_t.zeros();
176  m_acc_t.zeros();
177  m_grad_t.zeros();
178  modified_gram.zeros(i_k, i_k);
179  delta1 = 1e-2;
180  delta2 = 1e-2;
181  acc_start = 5;
182  acc_exp = 3;
183  acc_fails = 0;
184  fail_limit = 5;
185  // Set Accerated to be true
186  this->accelerated(true);
187  }
188 }; // class NTFNES
189 } // namespace planc
190 
191 #endif // NTF_NTFNES_HPP_
double current_error() const
Definition: auntf.hpp:121
Data is stored such that the unfolding is column major.
Definition: tensor.hpp:32
void accelerated(const bool &set_acceleration)
Definition: auntf.hpp:173
#define EPSILON_1EMINUS16
Definition: utils.h:43
double computeObjectiveError()
Definition: auntf.hpp:195
void normalize_rows(unsigned int mode)
Row normalizes the factor matrix of the given mode and replaces the existing lambda.
Definition: ncpfactors.hpp:358
NTFNES(const Tensor &i_tensor, const int i_k, algotype i_algo)
Definition: ntfnes.hpp:168
algotype
Definition: utils.h:10
VEC lambda() const
returns the lambda vector
Definition: ncpfactors.hpp:104
void set(const int i_n, const MAT &i_factor)
Set the mode i_n with the given factor matrix.
Definition: ncpfactors.hpp:112
#define INFO
Definition: utils.h:36
void set_lambda(const VEC &new_lambda)
sets the lambda vector
Definition: ncpfactors.hpp:117
int rank() const
returns low rank
Definition: ncpfactors.hpp:96
#define MAT
Definition: utils.h:52
int modes() const
returns number of modes
Definition: ncpfactors.hpp:102
int current_it() const
Definition: auntf.hpp:194
void zeros()
this is for reinitializing zeros across different processors.
Definition: ncpfactors.hpp:382
void reset(const NCPFactors &new_factors, bool trans=false)
Definition: auntf.hpp:181
ncp_factors contains the factors of the ncp every ith factor is of size n_i * k number of factors is ...
Definition: ncpfactors.hpp:20
#define VEC
Definition: utils.h:61
MAT & factor(const int i_n) const
factor matrix of a mode i_n
Definition: ncpfactors.hpp:100