planc
Parallel Lowrank Approximation with Non-negativity Constraints
bppnmf.hpp
Go to the documentation of this file.
1 /* Copyright 2016 Ramakrishnan Kannan */
2 
3 #ifndef NMF_BPPNMF_HPP_
4 #define NMF_BPPNMF_HPP_
5 
6 #include <omp.h>
7 #include "common/nmf.hpp"
8 #include "nnls/bppnnls.hpp"
9 
10 // needed for precondition with hals
11 #ifdef BUILD_SPARSE
12 #include "hals.hpp"
13 #endif
14 
15 #define ONE_THREAD_MATRIX_SIZE 2000
16 
17 namespace planc {
18 
19 template <class T>
20 class BPPNMF : public NMF<T> {
21  private:
22  T At;
23  MAT giventGiven;
24  // designed as if W is given and H is found.
25  // The transpose is the other problem.
26  void updateOtherGivenOneMultipleRHS(const T &input, const MAT &given,
27  char worh, MAT *othermat) {
28  double t2;
29  UINT numThreads = (input.n_cols / ONE_THREAD_MATRIX_SIZE) + 1;
30  tic();
31  MAT giventInput(this->k, input.n_cols);
32  // This is WtW
33  giventGiven = given.t() * given;
34  // This is WtA
35  // tic();
36  giventInput = given.t() * input;
37  // INFO << "matmul ::" << toc() << std::endl;
38  t2 = toc();
39  INFO << "starting " << worh << ". Prereq for " << worh << " took=" << t2
40  << " NumThreads=" << numThreads << PRINTMATINFO(giventGiven)
41  << PRINTMATINFO(giventInput) << std::endl;
42  tic();
43 #pragma omp parallel for schedule(dynamic)
44  for (UINT i = 0; i < numThreads; i++) {
45  UINT spanStart = i * ONE_THREAD_MATRIX_SIZE;
46  UINT spanEnd = (i + 1) * ONE_THREAD_MATRIX_SIZE - 1;
47  if (spanEnd > input.n_cols - 1) {
48  spanEnd = input.n_cols - 1;
49  }
50  // if it is exactly divisible, the last iteration is unnecessary.
51  BPPNNLS<MAT, VEC> *subProblem;
52  if (spanStart <= spanEnd) {
53  if (spanStart == spanEnd) {
54  subProblem = new BPPNNLS<MAT, VEC>(
55  giventGiven, (VEC)giventInput.col(spanStart), true);
56  } else { // if (spanStart < spanEnd)
57  subProblem = new BPPNNLS<MAT, VEC>(
58  giventGiven, (MAT)giventInput.cols(spanStart, spanEnd), true);
59  }
60 #ifdef _VERBOSE
61  INFO << "Scheduling " << worh << " start=" << spanStart
62  << ", end=" << spanEnd << ", tid=" << omp_get_thread_num()
63  << std::endl;
64 #endif
65  // tic();
66  subProblem->solveNNLS();
67  // t2 = toc();
68 #ifdef _VERBOSE
69  INFO << "completed " << worh << " start=" << spanStart
70  << ", end=" << spanEnd << ", tid=" << omp_get_thread_num()
71  << " cpu=" << sched_getcpu() << " time taken=" << t2
72  << " num_iterations()=" << numIter << std::endl;
73 #endif
74  if (spanStart == spanEnd) {
75  ROWVEC solVec = subProblem->getSolutionVector().t();
76  (*othermat).row(i) = solVec;
77  } else { // if (spanStart < spanEnd)
78  (*othermat).rows(spanStart, spanEnd) =
79  subProblem->getSolutionMatrix().t();
80  }
81  subProblem->clear();
82  delete subProblem;
83  }
84  }
85  double totalH2 = toc();
86  INFO << worh << " total time taken :" << totalH2 << std::endl;
87  giventGiven.clear();
88  giventInput.clear();
89  }
90 
91  public:
92  BPPNMF(const T &A, int lowrank) : NMF<T>(A, lowrank) {
93  giventGiven = arma::zeros<MAT>(lowrank, lowrank);
94  this->At = A.t();
95  }
96  BPPNMF(const T &A, const MAT &llf, const MAT &rlf) : NMF<T>(A, llf, rlf) {
97  this->At = A.t();
98  }
100  int currentIteration = 0;
101  T At = this->A.t();
102  this->computeObjectiveErr();
103  while (currentIteration < this->num_iterations() &&
104  this->objectiveErr > CONV_ERR) {
105 #ifdef COLLECTSTATS
106  this->collectStats(currentIteration);
107 #endif
108  // solve for H given W;
109  MAT Wt = this->W.t();
110  MAT WtW = Wt * this->W;
111  MAT WtA = Wt * this->A;
112  Wt.clear();
113  {
114 #pragma omp parallel for
115  // int i=251;
116  for (UINT i = 0; i < this->n; i++) {
117  BPPNNLS<MAT, VEC> *subProblemforH =
118  new BPPNNLS<MAT, VEC>(WtW, (VEC)WtA.col(i), true);
119 #ifdef _VERBOSE
120  INFO << "Initialized subproblem and calling solveNNLS for "
121  << "H(" << i << "/" << this->n << ")";
122 #endif
123  tic();
124  int numIter = subProblemforH->solveNNLS();
125  double t2 = toc();
126 #ifdef _VERBOSE
127  INFO << subProblemforH->getSolutionVector();
128 #endif
129  this->H.row(i) = subProblemforH->getSolutionVector().t();
130  INFO << "Comp H(" << i << "/" << this->n
131  << ") of it=" << currentIteration << " time taken=" << t2
132  << " num_iterations()=" << numIter << std::endl;
133  }
134  }
135 #ifdef _VERBOSE
136  INFO << "H: at it = " << currentIteration << std::endl << this->H;
137 #endif
138  // #pragma omp parallel
139  {
140  // clear previous allocations.
141  WtW.clear();
142  WtA.clear();
143  MAT Ht = this->H.t();
144  MAT HtH = Ht * this->H;
145  MAT HtAt = Ht * At;
146  Ht.clear();
147 // solve for W given H;
148 #pragma omp parallel for
149  for (UINT i = 0; i < this->m; i++) {
150  BPPNNLS<MAT, VEC> *subProblemforW =
151  new BPPNNLS<MAT, VEC>(HtH, (VEC)HtAt.col(i), true);
152 #ifdef _VERBOSE
153  INFO << "Initialized subproblem and calling solveNNLS for "
154  << "W(" << i << "/" << this->m << ")";
155 #endif
156  tic();
157  int numIter = subProblemforW->solveNNLS();
158  double t2 = toc();
159 #ifdef _VERBOSE
160  INFO << subProblemforW->getSolutionVector();
161 #endif
162 
163  this->W.row(i) = subProblemforW->getSolutionVector().t();
164  INFO << "Comp W(" << i << "/" << this->n
165  << ") of it=" << currentIteration << " time taken=" << t2
166  << " num_iterations()=" << numIter << std::endl;
167  }
168  HtH.clear();
169  HtAt.clear();
170  }
171 #ifdef _VERBOSE
172  INFO << "W: at it = " << currentIteration << std::endl << this->W;
173 #endif
174 #ifdef COLLECTSTATS
175  // INFO << "iteration = " << currentIteration << " currentObjectiveError="
176  // << this->objective_err << std::endl;
177 #endif
178  currentIteration++;
179  }
180  }
181  void computeNMF() {
182  unsigned int currentIteration = 0;
183 #ifdef COLLECTSTATS
184  // this->objective_err;
185 #endif
186  // tic();
187  // this->At = this->A.t(); // do it once
188  // INFO << "At time::" << toc() << std::endl;
189 #ifdef BUILD_SPARSE
190  // run hals once to get proper initializations
191  HALSNMF<T> tempHals(this->A, this->W, this->H);
192  tempHals.num_iterations(2);
193  this->W = tempHals.getLeftLowRankFactor();
194  this->H = tempHals.getRightLowRankFactor();
195 #endif
196  INFO << PRINTMATINFO(this->At);
197 #ifdef BUILD_SPARSE
198  INFO << " nnz = " << this->At.n_nonzero << std::endl;
199 #endif
200  INFO << "Starting BPP for num_iterations()=" << this->num_iterations()
201  << std::endl;
202  while (currentIteration < this->num_iterations()) {
203 #ifdef COLLECTSTATS
204  this->collectStats(currentIteration);
205  this->stats(currentIteration + 1, 0) = currentIteration + 1;
206 #endif
207  tic();
208  updateOtherGivenOneMultipleRHS(this->At, this->H, 'W', &(this->W));
209  double totalW2 = toc();
210  tic();
211  updateOtherGivenOneMultipleRHS(this->A, this->W, 'H', &(this->H));
212  double totalH2 = toc();
213 
214 #ifdef COLLECTSTATS
215  // end of H and start of W are almost same.
216  this->stats(currentIteration + 1, 1) = totalH2;
217  this->stats(currentIteration + 1, 2) = totalW2;
218 
219  this->stats(currentIteration + 1, 3) = totalW2 + totalH2;
220 #endif
221  INFO << "completed it=" << currentIteration
222  << " time taken = " << totalW2 + totalH2 << std::endl;
223  this->computeObjectiveError();
224  INFO << "error:it = " << currentIteration
225  << " bpperr =" << sqrt(this->objective_err) / this->normA
226  << std::endl;
227  currentIteration++;
228  }
229  this->normalize_by_W();
230 #ifdef COLLECTSTATS
231  this->collectStats(currentIteration);
232  INFO << "NMF Statistics:" << std::endl << this->stats << std::endl;
233 #endif
234  }
235  double getObjectiveError() { return this->objectiveErr; }
236 
237  /*
238  * I dont like this function here. But this seems to be the
239  * easy place for having it. This function really should have been
240  * in BPPNNLS.hpp. It will take some time to refactor this.
241  * Given, A and W, solve for H.
242  */
244  updateOtherGivenOneMultipleRHS(this->A, this->W, 'H', &(this->H));
245  return this->H;
246  }
247  ~BPPNMF() { this->At.clear(); }
248 };
249 
250 } // namespace planc
251 
252 #endif // NMF_BPPNMF_HPP_
MATTYPE getSolutionMatrix()
Definition: nnls.hpp:79
BPPNMF(const T &A, int lowrank)
Definition: bppnmf.hpp:92
void tic()
start the timer. easy to call as tic(); some code; double t=toc();
Definition: utils.hpp:42
double getObjectiveError()
Definition: bppnmf.hpp:235
const unsigned int num_iterations() const
Returns the number of iterations.
Definition: nmf.hpp:350
double toc()
Definition: utils.hpp:48
MAT getRightLowRankFactor()
Returns the right low rank factor matrix H.
Definition: nmf.hpp:169
void computeNMF()
Definition: bppnmf.hpp:181
void num_iterations(const int it)
Sets number of iterations for the NMF algorithms.
Definition: nmf.hpp:340
void computeNMFSingleRHS()
Definition: bppnmf.hpp:99
#define INFO
Definition: utils.h:36
int solveNNLS()
Definition: bppnnls.hpp:30
VECTYPE getSolutionVector()
Definition: nnls.hpp:76
unsigned int UINT
Definition: utils.h:68
#define MAT
Definition: utils.h:52
MAT solveScalableNNLS()
Definition: bppnmf.hpp:243
void clear()
Definition: nnls.hpp:82
#define ONE_THREAD_MATRIX_SIZE
Definition: bppnmf.hpp:15
MAT getLeftLowRankFactor()
Returns the left low rank factor matrix W.
Definition: nmf.hpp:167
#define CONV_ERR
Definition: nmf.hpp:13
#define ROWVEC
Definition: utils.h:54
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
#define PRINTMATINFO(A)
Definition: utils.h:63
void computeObjectiveError()
Definition: nmf.hpp:238
BPPNMF(const T &A, const MAT &llf, const MAT &rlf)
Definition: bppnmf.hpp:96