planc
Parallel Lowrank Approximation with Non-negativity Constraints
ncpfactors.hpp
Go to the documentation of this file.
1 /* Copyright 2017 Ramakrishnan Kannan */
2 
3 #ifndef COMMON_NCPFACTORS_HPP_
4 #define COMMON_NCPFACTORS_HPP_
5 
6 #include <cassert>
7 #include "common/tensor.hpp"
8 #include "common/utils.h"
9 #ifdef MPI_DISTNTF
10 #include <mpi.h>
11 #endif
12 
20 namespace planc {
21 
22 class NCPFactors {
23  MAT *ncp_factors;
24  unsigned int m_modes;
25  unsigned int m_k;
26  UVEC m_dimensions;
27  VEC m_lambda;
30  bool freed_ncp_factors;
31 
32  public:
41  NCPFactors(const UVEC &i_dimensions, const int &i_k, bool trans) {
42  this->m_dimensions = i_dimensions;
43  this->m_modes = i_dimensions.n_rows;
44  ncp_factors = new MAT[this->m_modes];
45  this->m_k = i_k;
46  arma::arma_rng::set_seed(103);
47  for (unsigned int i = 0; i < this->m_modes; i++) {
48  // ncp_factors[i] = arma::randu<MAT>(i_dimensions[i], this->m_k);
49  int rsize = (i_dimensions[i] > 0) ? i_dimensions[i] : 1;
50  if (trans) {
51  ncp_factors[i] = arma::randu<MAT>(this->m_k, rsize);
52  } else {
53  // ncp_factors[i] = arma::randi<MAT>(i_dimensions[i], this->m_k,
54  // arma::distr_param(0, numel));
55  ncp_factors[i] = arma::randu<MAT>(rsize, this->m_k);
56  }
57  }
58  m_lambda = arma::ones<VEC>(this->m_k);
59  freed_ncp_factors = false;
60  }
61 
62  // copy constructor
63  /*NCPFactors(const NCPFactors &src) {
64 m_dimensions = src.dimensions();
65 m_modes = src.modes();
66 m_lambda = src.lambda();
67 m_k = src.rank();
68 if (ncp_factors == NULL) {
69  ncp_factors = new MAT[this->m_modes];
70  for (int i = 0; i < this->m_modes; i++) {
71  ncp_factors[i] = arma::randu<MAT>(this->m_dimensions[i],
72  this->m_k);
73  }
74 }
75 for (int i = 0; i < this->m_modes; i++) {
76  if (ncp_factors[i].n_elem == 0) {
77  ncp_factors[i] = arma::zeros<MAT>(src.factor(i).n_rows,
78  src.factor(i).n_cols);
79  }
80  ncp_factors[i] = src.factor(i);
81 }
82 }*/
83 
85  /*for (int i=0; i < this->m_modes; i++){
86 ncp_factors[i].clear();
87 }*/
88  if (!freed_ncp_factors) {
89  delete[] ncp_factors;
90  freed_ncp_factors = true;
91  }
92  }
93 
94  // getters
96  int rank() const { return m_k; }
98  UVEC dimensions() const { return m_dimensions; }
100  MAT &factor(const int i_n) const { return ncp_factors[i_n]; }
102  int modes() const { return m_modes; }
104  VEC lambda() const { return m_lambda; }
105 
106  // setters
112  void set(const int i_n, const MAT &i_factor) {
113  assert(i_factor.size() == this->ncp_factors[i_n].size());
114  this->ncp_factors[i_n] = i_factor;
115  }
117  void set_lambda(const VEC &new_lambda) { m_lambda = new_lambda; }
118  // compute gram of all local factors
123  void gram(MAT *o_UtU) {
124  MAT currentGram(this->m_k, this->m_k);
125  for (unsigned int i = 0; i < this->m_modes; i++) {
126  currentGram = ncp_factors[i].t() * ncp_factors[i];
127  (*o_UtU) = (*o_UtU) % currentGram;
128  }
129  }
130 
131  // find the hadamard product of all the factor grams
132  // except the n. This is equation 50 of the JGO paper.
139  void gram_leave_out_one(const unsigned int i_n, MAT *o_UtU) {
140  MAT currentGram(this->m_k, this->m_k);
141  (*o_UtU) = arma::ones<MAT>(this->m_k, this->m_k);
142  for (unsigned int i = 0; i < this->m_modes; i++) {
143  if (i != i_n) {
144  currentGram = ncp_factors[i].t() * ncp_factors[i];
145  (*o_UtU) = (*o_UtU) % currentGram;
146  }
147  }
148  }
154  MAT krp_leave_out_one(const unsigned int i_n) {
155  UWORD krpsize = arma::prod(this->m_dimensions);
156  krpsize /= this->m_dimensions[i_n];
157  MAT krp(krpsize, this->m_k);
158  krp_leave_out_one(i_n, &krp);
159  return krp;
160  }
161  // construct low rank tensor using the factors
171  void krp_leave_out_one(const unsigned int i_n, MAT *o_krp) {
172  // matorder = length(A):-1:1;
173  // Always krp for mttkrp is computed in
174  // reverse. Hence assuming the same.
175  UVEC matorder = arma::zeros<UVEC>(this->m_modes - 1);
176  int j = 0;
177  for (int i = this->m_modes - 1; i >= 0; i--) {
178  if (i != i_n) {
179  matorder(j++) = i;
180  }
181  }
182 #ifdef NTF_VERBOSE
183  INFO << "::" << __PRETTY_FUNCTION__ << "::" << __LINE__
184  << "::matorder::" << matorder << std::endl;
185 #endif
186  o_krp->zeros();
187  // N = ncols(1);
188  // This is our k. So keep N = k in our case.
189  // P = A{matorder(1)};
190  // take the first factor of matorder
191  /*UWORD current_nrows = ncp_factors[matorder(0)].n_rows - 1;
192 (*o_krp).rows(0, current_nrows) = ncp_factors[matorder(0)];
193 // this is factor by factor
194 for (int i = 1; i < this->m_modes - 1; i++) {
195 // remember always krp in reverse order.
196 // That is if A krp B krp C, we compute as
197 // C krp B krp A.
198 // prev_nrows = current_nrows;
199 // rightkrp.n_rows;
200 // we are populating column by column
201 MAT& rightkrp = ncp_factors[matorder[i]];
202 for (int j = 0; j < this->m_k; j++) {
203 VEC krpcol = (*o_krp)(arma::span(0, current_nrows), j);
204 // krpcol.each_rows*rightkrp.col(i);
205 for (int k = 0; k < rightkrp.n_rows; k++) {
206  (*o_krp)(arma::span(k * krpcol.n_rows, (k + 1)*krpcol.n_rows -
207 1), j) = krpcol * rightkrp(k, j);
208 }
209 }
210 current_nrows *= rightkrp.n_rows;
211 }*/
212  // Loop through all the columns
213  // for n = 1:N
214  // % Loop through all the matrices
215  // ab = A{matorder(1)}(:,n);
216  // for i = matorder(2:end)
217  // % Compute outer product of nth columns
218  // ab = A{i}(:,n) * ab(:).';
219  // end
220  // % Fill nth column of P with reshaped result
221  // P(:,n) = ab(:);
222  // end
223  for (unsigned int n = 0; n < this->m_k; n++) {
224  MAT ab = ncp_factors[matorder[0]].col(n);
225  for (unsigned int i = 1; i < this->m_modes - 1; i++) {
226  VEC oldabvec = arma::vectorise(ab);
227  VEC currentvec = ncp_factors[matorder[i]].col(n);
228  ab.clear();
229  ab = currentvec * oldabvec.t();
230  }
231  (*o_krp).col(n) = arma::vectorise(ab);
232  }
233  }
239  void krp(const UVEC i_modes, MAT *o_krp) {
240  // matorder = length(A):-1:1;
241  // Always krp for mttkrp is computed in
242  // reverse. Hence assuming the same.
243  UVEC matorder = arma::zeros<UVEC>(i_modes.n_rows - 1);
244  int j = 0;
245  for (int i = i_modes.n_rows - 1; i >= 0; i--) {
246  matorder(j++) = i_modes[i];
247  }
248 #ifdef NTF_VERBOSE
249  INFO << "::" << __PRETTY_FUNCTION__ << "::" << __LINE__
250  << "::matorder::" << matorder << std::endl;
251 #endif
252  (*o_krp).zeros();
253  for (unsigned int n = 0; n < this->m_k; n++) {
254  MAT ab = ncp_factors[matorder[0]].col(n);
255  for (unsigned int i = 1; i < i_modes.n_rows - 1; i++) {
256  VEC oldabvec = arma::vectorise(ab);
257  VEC currentvec = ncp_factors[matorder[i]].col(n);
258  ab.clear();
259  ab = currentvec * oldabvec.t();
260  }
261  (*o_krp).col(n) = arma::vectorise(ab);
262  }
263  }
264 
265  // caller must free
266  // Tensor rankk_tensor() {
267  // UWORD krpsize = arma::prod(this->m_dimensions);
268  // krpsize /= this->m_dimensions[0];
269  // MAT krpleavingzero = arma::zeros<MAT>(krpsize, this->m_k);
270  // krp_leave_out_one(0, &krpleavingzero);
271  // MAT lowranktensor(this->m_dimensions[0], krpsize);
272  // lowranktensor = this->ncp_factors[0] * krpleavingzero.t();
273  // Tensor rc(this->m_dimensions, lowranktensor.memptr());
274  // return rc;
275  // }
282  void rankk_tensor(Tensor &out) {
283  UWORD krpsize = arma::prod(this->m_dimensions);
284  krpsize /= this->m_dimensions[0];
285  MAT krpleavingzero = arma::zeros<MAT>(krpsize, this->m_k);
286  krp_leave_out_one(0, &krpleavingzero);
287  MAT lowranktensor(this->m_dimensions[0], krpsize);
288  lowranktensor = this->ncp_factors[0] * krpleavingzero.t();
289  Tensor rc(this->m_dimensions, lowranktensor.memptr());
290  out = rc;
291  }
296  void printinfo() {
297  INFO << "modes::" << this->m_modes << "::k::" << this->m_k << std::endl;
298  INFO << "lambda::" << this->m_lambda << std::endl;
299  INFO << "::dims::" << std::endl << this->m_dimensions << std::endl;
300  }
302  void print() {
303  printinfo();
304  for (unsigned int i = 0; i < this->m_modes; i++) {
305  std::cout << i << "th factor" << std::endl
306  << "=============" << std::endl;
307  std::cout << this->ncp_factors[i];
308  }
309  }
314  void print(const unsigned int i_n) {
315  std::cout << i_n << "th factor" << std::endl
316  << "=============" << std::endl;
317  std::cout << this->ncp_factors[i_n];
318  }
323  void trans(NCPFactors &factor_t) {
324  for (unsigned int i = 0; i < this->m_modes; i++) {
325  factor_t.set(i, this->ncp_factors[i].t());
326  }
327  }
329  void normalize() {
330  double colNorm = 0.0;
331  m_lambda.ones();
332  for (unsigned int i = 0; i < this->m_modes; i++) {
333  for (unsigned int j = 0; j < this->m_k; j++) {
334  colNorm = arma::norm(this->ncp_factors[i].col(j));
335  if (colNorm > 0) this->ncp_factors[i].col(j) /= colNorm;
336  m_lambda(j) *= colNorm;
337  }
338  }
339  }
340  // replaces the existing lambdas
346  void normalize(int mode) {
347  for (unsigned int i = 0; i < this->m_k; i++) {
348  m_lambda(i) = arma::norm(this->ncp_factors[mode].col(i));
349  if (m_lambda(i) > 0) this->ncp_factors[mode].col(i) /= m_lambda(i);
350  }
351  }
352  // replaces the existing lambdas
358  void normalize_rows(unsigned int mode) {
359  for (unsigned int i = 0; i < this->m_k; i++) {
360  m_lambda(i) = arma::norm(this->ncp_factors[mode].row(i));
361  if (m_lambda(i) > 0) this->ncp_factors[mode].row(i) /= m_lambda(i);
362  }
363  }
364 
371  void randu(const int i_seed) {
372  arma::arma_rng::set_seed(i_seed);
373  for (unsigned int i = 0; i < this->m_modes; i++) {
374  if (m_dimensions[i] > 0) {
375  ncp_factors[i].randu();
376  } else {
377  ncp_factors[i].zeros();
378  }
379  }
380  }
382  void zeros() {
383  for (unsigned int i = 0; i < this->m_modes; i++) {
384  ncp_factors[i].zeros();
385  }
386  }
387 #ifdef MPI_DISTNTF
388  // Distribution normalization of factor matrices
389  // To be used for MPI code only
394  void distributed_normalize() {
395  double local_colnorm;
396  double global_colnorm;
397  for (unsigned int i = 0; i < this->m_modes; i++) {
398  for (unsigned int j = 0; j < this->m_k; j++) {
399  local_colnorm = arma::norm(this->ncp_factors[i].col(j));
400  local_colnorm *= local_colnorm;
401  MPI_Allreduce(&local_colnorm, &global_colnorm, 1, MPI_DOUBLE, MPI_SUM,
402  MPI_COMM_WORLD);
403  global_colnorm = std::sqrt(global_colnorm);
404  if (global_colnorm > 0) this->ncp_factors[i].col(j) /= global_colnorm;
405  m_lambda(j) *= global_colnorm;
406  }
407  }
408  }
414  void distributed_normalize(unsigned int mode) {
415  double local_colnorm;
416  double global_colnorm;
417  for (unsigned int j = 0; j < this->m_k; j++) {
418  local_colnorm = arma::norm(this->ncp_factors[mode].col(j));
419  local_colnorm *= local_colnorm;
420  MPI_Allreduce(&local_colnorm, &global_colnorm, 1, MPI_DOUBLE, MPI_SUM,
421  MPI_COMM_WORLD);
422  global_colnorm = std::sqrt(global_colnorm);
423  if (global_colnorm > 0) this->ncp_factors[mode].col(j) /= global_colnorm;
424  m_lambda(j) = global_colnorm;
425  }
426  }
432  void distributed_normalize_rows(unsigned int mode) {
433  double local_rownorm;
434  double global_rownorm;
435  for (unsigned int j = 0; j < this->m_k; j++) {
436  local_rownorm = arma::norm(this->ncp_factors[mode].row(j));
437  local_rownorm *= local_rownorm;
438  MPI_Allreduce(&local_rownorm, &global_rownorm, 1, MPI_DOUBLE, MPI_SUM,
439  MPI_COMM_WORLD);
440  global_rownorm = std::sqrt(global_rownorm);
441  if (global_rownorm > 0) this->ncp_factors[mode].row(j) /= global_rownorm;
442  m_lambda(j) = global_rownorm;
443  }
444  }
445 #endif
446 }; // NCPFactors
447 } // namespace planc
448 
449 #endif // COMMON_NCPFACTORS_HPP_
void normalize(int mode)
Column normalizes the factor matrix of the given mode and replaces the existing lambda.
Definition: ncpfactors.hpp:346
void randu(const int i_seed)
initializes the local tensor with the given seed.
Definition: ncpfactors.hpp:371
UVEC dimensions() const
dimensions of every mode
Definition: ncpfactors.hpp:98
Data is stored such that the unfolding is column major.
Definition: tensor.hpp:32
NCPFactors(const UVEC &i_dimensions, const int &i_k, bool trans)
constructor that takes the dimensions of every mode, low rank k All the factors will be initialized w...
Definition: ncpfactors.hpp:41
void print()
prints the entire NCPFactors including the factor matrices
Definition: ncpfactors.hpp:302
void krp_leave_out_one(const unsigned int i_n, MAT *o_krp)
khatrirao leaving out one.
Definition: ncpfactors.hpp:171
void gram_leave_out_one(const unsigned int i_n, MAT *o_UtU)
Returns the hadamard of the factor grams except i_n.
Definition: ncpfactors.hpp:139
void printinfo()
prints just the information about the factors.
Definition: ncpfactors.hpp:296
void rankk_tensor(Tensor &out)
Construct the rank k tensor out of the factor matrices Determine the KRP of the n-1 modes leaving out...
Definition: ncpfactors.hpp:282
void normalize()
only during initialization. Reset&#39;s all lambda.
Definition: ncpfactors.hpp:329
void trans(NCPFactors &factor_t)
Transposes the entire factor matrix.
Definition: ncpfactors.hpp:323
void gram(MAT *o_UtU)
Return the hadamard of the factor grams.
Definition: ncpfactors.hpp:123
void normalize_rows(unsigned int mode)
Row normalizes the factor matrix of the given mode and replaces the existing lambda.
Definition: ncpfactors.hpp:358
#define UVEC
Definition: utils.h:58
MAT krp_leave_out_one(const unsigned int i_n)
KRP leaving out the mode i_n.
Definition: ncpfactors.hpp:154
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 print(const unsigned int i_n)
print the ith factor matrix alone
Definition: ncpfactors.hpp:314
void set_lambda(const VEC &new_lambda)
sets the lambda vector
Definition: ncpfactors.hpp:117
#define UWORD
Definition: utils.h:60
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
void zeros()
this is for reinitializing zeros across different processors.
Definition: ncpfactors.hpp:382
void krp(const UVEC i_modes, MAT *o_krp)
KRP of the given vector of modes.
Definition: ncpfactors.hpp:239
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