planc
Parallel Lowrank Approximation with Non-negativity Constraints
bppnnls.hpp
Go to the documentation of this file.
1 /*Copyright 2016 Ramakrishnan Kannan*/
2 
3 #ifndef NNLS_BPPNNLS_HPP_
4 #define NNLS_BPPNNLS_HPP_
5 
6 #ifdef MKL_FOUND
7 #include <mkl.h>
8 #else
9 #include <lapacke.h>
10 #endif
11 #include <assert.h>
12 #include "ActiveSetNNLS.h"
13 #include "nnls.hpp"
14 #include "utils.hpp"
15 #include <set>
16 #include <algorithm>
17 #include <iomanip>
18 #include "ActiveSetNNLS.hpp"
19 #include "SortBooleanMatrix.hpp"
20 
21 template <class MATTYPE, class VECTYPE>
22 class BPPNNLS : public NNLS<MATTYPE, VECTYPE> {
23  public:
24  BPPNNLS(MATTYPE input, VECTYPE rhs, bool prodSent = false):
25  NNLS<MATTYPE, VECTYPE>(input, rhs, prodSent) {
26  }
27  BPPNNLS(MATTYPE input, MATTYPE RHS, bool prodSent = false) :
28  NNLS<MATTYPE, VECTYPE>(input, RHS, prodSent) {
29  }
30  int solveNNLS() {
31  int rcIterations = 0;
32  if (this->r == 1) {
33  rcIterations = solveNNLSOneRHS();
34  } else {
35  // r must be greater than 1 in this case.
36  // we initialized r appropriately in the
37  // constructor.
38  rcIterations = solveNNLSMultipleRHS();
39  }
40  return rcIterations;
41  }
42  private:
43  /*
44  * This implementation is based on Algorithm 1 on Page 6 of paper
45  * http://www.cc.gatech.edu/~hpark/papers/SISC_082117RR_Kim_Park.pdf.
46  *
47  */
48 
49  int solveNNLSOneRHS() {
50  // local to this function.
51  UINT MAX_ITERATIONS = this->q * 2;
52  UVEC F;
53  UVEC G(this->q);
54  STDVEC allIdxs;
55  // initalizations
56  for (UINT i = 0; i < G.n_rows; i++) {
57  G(i) = i;
58  allIdxs.push_back(i);
59  }
60  VECTYPE y = -this->Ctb;
61 #ifdef _VERBOSE
62  INFO << endl << "C : " << this->CtC;
63  INFO << "b : " << this->Ctb;
64  INFO << "Init y:" << y;
65 #endif
66  UINT alpha = 3;
67  UINT beta = this->q + 1;
68  unsigned int numIterations = 0;
69  setprecision(64);
70  bool solutionFound = false;
71  // main loop
72  while (numIterations < MAX_ITERATIONS) {
73  // compute V = {i \in F : x_i < 0} union {i \in G : y_i < 0}
74  // iterate over F to find x_i < 0
75  UVEC V1, V2;
76  STDVEC Vs;
77  V1 = find(this->x(F) < 0);
78  V2 = find(y(G) < 0);
79 
80  STDVEC Fv1 = arma::conv_to<STDVEC>::from(F(V1));
81  STDVEC Gv2 = arma::conv_to<STDVEC>::from(G(V2));
82  std::set_union(Fv1.begin(),
83  Fv1.end(),
84  Gv2.begin(),
85  Gv2.end(),
86  std::inserter(Vs, Vs.begin()));
87 
88  UVEC V = arma::conv_to<UVEC>::from(Vs);
89 #ifdef _VERBOSE
90  INFO << "xf<0 : " << V1.size() << endl << V1;
91  INFO << "yg<0 : " << V2.size() << endl << V2;
92  INFO << "V :" << V.size() << endl << V;
93 #endif
94 
95  // Terminate loop if there is nothing to swap.
96  // solution found.
97  if (V.empty()) {
98  solutionFound = true;
99  break;
100  }
101  // Step 5 of the algorithm.
102  if (V.size() < beta) {
103  beta = V.size();
104  alpha = 3;
105  } else {
106  if (alpha >= 1) {
107  alpha--; // step 6 of the algorithm
108  } else {
109  // Step 7 of the algorithm.
110  alpha = 0;
111  int temp = V(V.n_rows - 1);
112  V.clear();
113  V = temp;
114  }
115  }
116  // step 8 of the algorithm 1.
117  fixAllSets(F, G, V, allIdxs);
118 #ifdef _VERBOSE
119  INFO << "V:" << V.size() << endl << V;
120  INFO << "a : " << alpha << ", b:" << beta << endl;
121  INFO << "F:" << F.size() << endl << F << endl;
122  INFO << "G:" << G.size() << endl << G << endl;
123  INFO << "b4 x:" << this->x << endl;
124  INFO << "b4 y:" << y << endl;
125 #endif
126  y.zeros();
127  this->x(G).zeros();
128  // clear up and save only the passive set with the optimal solution
129  // for x and active set in y.
130  // Step 9 of algorithm 1;
131  // this->x(F) = arma::solve(this->CtC(F,F),this->Ctb.rows(F));
132  this->x(F) = solveSymmetricLinearEquations(this->CtC(F, F),
133  this->Ctb.rows(F));
134  VECTYPE lhs = (this->CtC * this->x);
135  y = lhs - this->Ctb;
136 #ifdef _VERBOSE
137  INFO << "after x:" << this->x << endl;
138  INFO << "lhs y:" << lhs;
139  INFO << "lhs(0) " << lhs.row(0) << " first b" << this->Ctb.row(0)
140  << "diff value " << lhs.row(0) - this->Ctb.row(0) << endl;
141  INFO << "after y:" << y(G) << endl;
142 #endif
143  this->x(G).zeros();
144  y(F).zeros();
145  fixNumericalError<VECTYPE>(&this->x);
146  fixNumericalError<VECTYPE>(&y);
147  // according to lawson and hanson if for alpha==0, the computed
148  // x at V is negative, it is because of the numerical error of
149  // y at V being negative. Set it to zero.
150  if (alpha == 0) {
151  int temp = V(0);
152  if (this->x(temp) < 0) {
153  WARN << "invoking lawson and hanson's fix" << endl;
154  y(temp) = 0;
155  this->x(temp) = 0;
156  }
157  }
158  numIterations++;
159  }
160  if (numIterations >= MAX_ITERATIONS && !solutionFound) {
161  ERR << "max iterations passed. calling Hanson's ActivesetNNLS"
162  << endl;
163  ERR << "current x initialized for Hanson's algo : " << endl
164  << this->x;
165  ActiveSetNNLS<double> anls(this->q, this->q);
166  double rNorm;
167  anls.solve(this->CtC.memptr(), static_cast<int>(this->q),
168  this->Ctb.memptr(), this->x.memptr(), rNorm);
169  double *nnlsDual = anls.getDual();
170  INFO << "Activeset NNLS Dual:" << endl;
171  for (unsigned int i = 0; i < this->q; i++) {
172  INFO << nnlsDual[i] << endl;
173  }
174  }
175  return numIterations;
176  }
177  /*
178  * This is the implementation of Algorithm 2 at Page 8 of the paper
179  * http:// www.cc.gatech.edu/~hpark/papers/SISC_082117RR_Kim_Park.pdf.
180  */
181 
182  int solveNNLSMultipleRHS() {
183  UINT currentIteration = 0;
184  UINT MAX_ITERATIONS = this->q * 2;
185  MATTYPE Y = -this->CtB;
186  UVEC Fv(this->q * this->r);
187  Fv.zeros();
188  UVEC Gv(this->q * this->r);
189  arma::umat V(this->q, this->r);
190  STDVEC allIdxs;
191  IVEC alphaZeroIdxs(this->r);
192  bool solutionFound = false;
193  for (UINT i = 0; i < this->q * this->r; i++) {
194  Gv(i) = i;
195  allIdxs.push_back(i);
196  }
197  UVEC alpha(this->r), beta(this->r);
198  alpha.ones();
199  alpha = alpha * 3;
200  beta.ones();
201  beta = beta * (this->q + 1);
202 #ifdef _VERBOSE
203  INFO << "Gv :" << Gv.size() << endl << Gv;
204  INFO << "Rank : " << arma::rank(this->CtC) << endl;
205  INFO << "Condition : " << cond(this->CtC) << endl;
206  INFO << "a: " << endl << alpha;
207  INFO << "b: " << endl << beta;
208  INFO << "Y: " << endl << Y;
209  INFO << "Rank : " << arma::rank(this->CtC) << endl;
210  INFO << "Condition : " << cond(this->CtC) << endl;
211 #endif
212  while (currentIteration < MAX_ITERATIONS) {
213  UVEC V1 = find(this->X(Fv) < 0);
214  UVEC V2 = find(Y(Gv) < 0);
215  // Fv was initialized to zeros because the find during
216  // first iteration with empty Fv gave an error.
217  // However, zero Fv was giving wrong Gv during fixAllSets.
218  // Hence clearing Fv as the zero Idx really does not
219  // belong to Fv in the initialization.
220  if (currentIteration == 0) {
221  Fv.clear();
222  }
223 #ifdef _VERBOSE
224  INFO << "X(Fv)<0 : " << V1.size() << endl << V1;
225  INFO << "Y(Gv)<0 : " << V2.size() << endl << V2;
226 #endif
227  STDVEC Vs;
228 
229  STDVEC Fvv1 = arma::conv_to<STDVEC>::from(Fv(V1));
230  STDVEC Gvv2 = arma::conv_to<STDVEC>::from(Gv(V2));
231  std::set_union(Fvv1.begin(),
232  Fvv1.end(),
233  Gvv2.begin(),
234  Gvv2.end(),
235  std::inserter(Vs, Vs.begin()));
236 
237  UVEC VIdx = arma::conv_to<UVEC>::from(Vs);
238  if (VIdx.empty()) {
239 #ifdef _VERBOSE
240  INFO << "Terminating the loop" << endl;
241 #endif
242  solutionFound = true;
243  break;
244  }
245  V.zeros();
246  V(VIdx).ones();
247 #ifdef _VERBOSE
248  INFO << "V:" << V.size() << endl << V;
249 #endif
250  STDVEC nonOptCols;
251  /*
252  * Armadillo gives linearIdx for find function.
253  * finding non optimum columns from the non optimum linear indices
254  * is done here.
255  */
256  // for (UINT i = 0; i < VIdx.size(); i++)
257  // {
258  // nonOptCols.push_back(VIdx(i) % r);
259  // }
260  // std::sort(nonOptCols.begin(), nonOptCols.end());
261  // // remove the duplicates.
262  // nonOptCols.erase(std::unique(nonOptCols.begin(), nonOptCols.end()),
263  // nonOptCols.end());
264  UVEC NonOptCols = find(sum(V) != 0);
265 #ifdef _VERBOSE
266  INFO << "NonOptCols:" << NonOptCols.size() << NonOptCols;
267 #endif
268  alphaZeroIdxs.ones();
269  alphaZeroIdxs = alphaZeroIdxs * -1;
270  for (UINT i = 0; i < NonOptCols.size(); i++) {
271  int currentIdx = NonOptCols(i);
272  // Step 7 of the algorithm.
273  if (sum(V.col(currentIdx)) < beta(currentIdx)) {
274  beta(currentIdx) = sum(V.col(currentIdx));
275  alpha(currentIdx) = 3;
276  } else {
277  if (alpha(currentIdx) >= 1) {
278  alpha(currentIdx)--; // step 8 of the algorithm
279  } else {
280  // Step 9 of the algorithm.
281  alpha(currentIdx) = 0;
282  UVEC temp = find(V.col(currentIdx) != 0);
283 #ifdef _VERBOSE
284  INFO << "temp : " << endl << temp << "max :"
285  << temp.max() << endl;
286 #endif
287  V.col(currentIdx).zeros();
288  V(temp.max(), currentIdx) = 1;
289  alphaZeroIdxs(currentIdx) = temp.max();
290  }
291  }
292 #ifdef _VERBOSE
293  INFO << "idx:" << currentIdx << endl;
294  INFO << "V:" << V.col(currentIdx);
295  INFO << "a : " << alpha(currentIdx)
296  << ", b:" << beta(currentIdx) << endl;
297 #endif
298  }
299  VIdx = find(V != 0);
300  // step 10 of the algorithm 2
301  fixAllSets(Fv, Gv, VIdx, allIdxs);
302 #ifdef _VERBOSE
303  INFO << "F:" << endl << Fv << endl;
304  INFO << "G:" << endl << Gv << endl;
305  INFO << "VIdx:" << endl << VIdx << endl;
306 #endif
307  Y(Fv).zeros();
308  this->X(Gv).zeros();
309 #ifdef _VERBOSE
310  INFO << "b4 x:" << endl << this->X << endl;
311  INFO << "b4 y:" << endl << Y << endl;
312 #endif
313  // solve LSQ with multiple RHS.
314  // Step 11 of Algorithm 2.
315  arma::umat PassiveSet(this->q, this->r);
316  PassiveSet.zeros();
317  PassiveSet(Fv).ones();
318  UVEC FvCols = find(sum(PassiveSet) != 0);
319  this->X.cols(FvCols) = solveNormalEqComb(this->CtC,
320  this->CtB.cols(FvCols),
321  PassiveSet.cols(FvCols));
322  Y.cols(FvCols) = (this->CtC * this->X.cols(FvCols))
323  - this->CtB.cols(FvCols);
324  fixNumericalError<MATTYPE>(&this->X);
325  fixNumericalError<MATTYPE>(&Y);
326  // according to lawson and hanson if for alpha==0, the computed
327  // x at V is negative, it is because of the numerical error of
328  // y at V being negative. Set it to zero.
329  for (UINT i = 0; i < alphaZeroIdxs.size(); i++) {
330  if (alphaZeroIdxs(i) != -1) {
331  if (this->X(alphaZeroIdxs(i), i) < 0) {
332  WARN << "invoking lawson and hanson's fix for col"
333  << i << "it = " << currentIteration << endl;
334  Y(alphaZeroIdxs(i), i) = 0;
335  this->X(alphaZeroIdxs(i), i) = 0;
336  }
337  }
338  }
339 
340 #ifdef _VERBOSE
341  INFO << "after x:" << endl << this->X;
342  INFO << "after y:" << endl << Y;
343 #endif
344  currentIteration++;
345  }
346  if (currentIteration >= MAX_ITERATIONS && !solutionFound) {
347  ERR << "something wrong. appears to be infeasible" << endl;
348 #ifdef _VERBOSE
349  INFO << "X : " << this->X.n_rows << "x" << this->X.n_cols
350  << " CtB:" << this->CtB.n_rows << "x" << this->CtB.n_cols
351  << " CtC" << this->CtC.n_rows << "x" << this->CtC.n_cols
352  << " : r=" << this->r << " :p=" << this->p
353  << " :q=" << this->q << endl;
354  std::ostringstream fileName, fileName2;
355  int temp = rand();
356  fileName << "errinputmatrix" << temp;
357  INFO << "input file matrix " << fileName.str() << endl;
358  this->CtC.save(fileName.str());
359  fileName2 << "errrhsmatrix" << temp;
360  INFO << "rhs file matrix " << fileName2.str() << endl;
361  this->CtB.save(fileName2.str());
362  sleep(60);
363  exit(EXIT_FAILURE);
364 #endif
365  INFO << "calling classical activeset" << endl;
366  for (UINT i = 0; i < this->r; i++) {
367  UVEC V1 = find(this->X.col(i) < 0);
368  UVEC V2 = find(Y.col(i) < 0);
369  if (!V1.empty() || !V2.empty()) {
370 #ifdef _VERBOSE
371  WARN << "col " << i << " not optimal " << endl;
372  WARN << "current x initialized for Hanson's algo : "
373  << endl << this->X.col(i);
374 #endif
375  ActiveSetNNLS<double> anls(this->q, this->q);
376  double rNorm;
377  double *currentX = new double[this->q];
378  double *currentRHS = new double[this->q];
379  for (UINT j = 0; j < this->q; j++) {
380  currentX[j] = this->X(j, i);
381  currentRHS[j] = this->CtB(j, i);
382  }
383  anls.solve(this->CtC.memptr(), static_cast<int>(this->q),
384  currentRHS, currentX, rNorm);
385  for (UINT j = 0; j < this->q; j++) {
386  this->X(j, i) = currentX[j];
387  }
388  }
389  }
390  }
391  return currentIteration;
392  }
393  /*
394  * This function to support the step 10 of the algorithm 2.
395  * This is implementation of the paper
396  * Fast algorithm for the solution of large-scale non-negativity-constrained least squares problems
397  * M. H. Van Benthem and M. R. Keenan, J. Chemometrics 2004; 18: 441-450
398  * Motivated out of implementation from Jingu's solveNormalEqComb.m
399  */
400  MATTYPE solveNormalEqComb(MATTYPE AtA, MATTYPE AtB, arma::umat PassSet) {
401  MATTYPE Z;
402  UVEC Pv = find(PassSet != 0);
403  UVEC anyZeros = find(PassSet == 0);
404  if (anyZeros.empty()) {
405  // Everything is the in the passive set.
406  // INFO << "starting empty activeset solve" << endl;
407  // Z = arma::solve(AtA,AtB);
408  // INFO << "exiting empty activeset solve" << endl;
409  Z = solveSymmetricLinearEquations(AtA, AtB);
410  } else {
411  Z.resize(AtB.n_rows, AtB.n_cols);
412  Z.zeros();
413  UINT k1 = PassSet.n_cols;
414  if (k1 == 1) {
415  // INFO << "entry one col pass set" << endl;
416  // Z(Pv)=arma::solve(AtA(Pv,Pv),AtB(Pv));
417  // INFO << "exit one col pass set" << endl;
418  Z(Pv) = solveSymmetricLinearEquations(AtA(Pv, Pv), AtB(Pv));
419  } else {
420  // we have to group passive set columns that are same.
421  // find the correlation matrix of passive set matrix.
422  std::vector<UWORD> sortedIdx, beginIdx;
423  computeCorrelationScore(PassSet, sortedIdx, beginIdx);
424  // UVEC solved(k1);
425  // solved.zeros();
426  // assert(corrPassMat.n_rows==PassSet.n_cols);
427  // assert(corrPassMat.n_cols==PassSet.n_cols);
428  for (UINT i = 1; i < beginIdx.size(); i++) {
429  // if(!solved(i))
430  // {
431  // UVEC samePassiveSetCols=find(corrPassMat.col(i)==1);
432  UWORD sortedBeginIdx = beginIdx[i - 1];
433  UWORD sortedEndIdx = beginIdx[i];
434  UVEC samePassiveSetCols(std::vector<UWORD>
435  (sortedIdx.begin() + sortedBeginIdx,
436  sortedIdx.begin() + sortedEndIdx));
437  // solved(samePassiveSetCols).ones();
438  UVEC currentPassiveSet = find(PassSet.col( sortedIdx[sortedBeginIdx] ) == 1);
439 #ifdef _VERBOSE
440  INFO << "samePassiveSetCols:" << endl
441  << samePassiveSetCols;
442  INFO << "currPassiveSet : " << endl
443  << currentPassiveSet;
444  INFO << "AtA:" << endl
445  << AtA(currentPassiveSet, currentPassiveSet);
446  INFO << "AtB:" << endl
447  << AtB(currentPassiveSet, samePassiveSetCols);
448 #endif
449  Z(currentPassiveSet, samePassiveSetCols) =
450  solveSymmetricLinearEquations(AtA(currentPassiveSet, currentPassiveSet),
451  AtB(currentPassiveSet, samePassiveSetCols));
452  }
453  }
454  }
455 #ifdef _VERBOSE
456  INFO << "Returning mat Z:" << endl << Z;
457 #endif
458  return Z;
459  }
460  /*
461  * This constructs the sets F, G and V based on
462  * equation 3.5a and 3.5b. This is also the
463  * step 8 of algorithm1 and step 10 of algorithm 2.
464  */
465  void fixAllSets(UVEC &F, UVEC &G, UVEC &V, STDVEC &allIdxs) {
466  // G = (G-V) union (V intersection F)
467  std::set<int> temp1, temp2;
468  STDVEC vecG = arma::conv_to<STDVEC>::from(G);
469  STDVEC vecV = arma::conv_to<STDVEC>::from(V);
470  STDVEC vecF = arma::conv_to<STDVEC>::from(F);
471  std::set_difference(vecG.begin(), vecG.end(), vecV.begin(), vecV.end(),
472  std::inserter(temp1, temp1.begin()));
473  std::set_intersection(vecV.begin(), vecV.end(), vecF.begin(), vecF.end(),
474  std::inserter(temp2, temp2.begin()));
475  STDVEC Gs;
476  std::set_union(temp1.begin(), temp1.end(),
477  temp2.begin(), temp2.end(),
478  std::inserter(Gs, Gs.begin()));
479  G = arma::conv_to<UVEC>::from(Gs);
480 
481 
482 
483  // F = (F-V) union (V intersection G)
484  // Generally FUG = allIdxs and FnG=null.
485  // G is small in size. Hence we find G first
486  // and use it to find F.
487  // std::set<int> temp1, temp2, temp3, temp4;
488  STDVEC newF;
489  // // std library didn't work. Hence used boost.
490  std::set_difference(allIdxs.begin(), allIdxs.end(),
491  Gs.begin(), Gs.end(),
492  std::inserter(newF, newF.begin()));
493  // boost::set_intersection(arma::conv_to<STDVEC>::from(V),
494  // arma::conv_to<STDVEC>::from(G),
495  // std::inserter(temp2, temp2.begin()));
496  // boost::set_union(temp1, temp2, std::inserter(newF, newF.begin()));
497  F.clear();
498  F = arma::conv_to<UVEC>::from(newF);
499  }
500 #ifdef _VERBOSE
501  void printSet(std::set<int> a) {
502  for (std::set<int>::iterator it = a.begin(); it != a.end(); it++) {
503  cout << *it << ",";
504  }
505  }
506 #endif
507 
508  MATTYPE solveSymmetricLinearEquations(MATTYPE A, MATTYPE B) {
509  lapack_int info = 0;
510  lapack_int n = A.n_cols;
511  lapack_int nrhs = B.n_cols;
512  lapack_int lda = A.n_rows;
513  lapack_int ldb = A.n_rows;
514  if (n <= 0 || nrhs <= 0) {
515  ERR << "something wrong in input" << " n=" << n
516  << " nrhs=" << nrhs << endl;
517  exit(EXIT_FAILURE);
518  }
519  LAPACKE_dposv(LAPACK_COL_MAJOR, 'U', n, nrhs, A.memptr(), lda, B.memptr(), ldb);
520  if ((signed int)info != 0) {
521  ERR << "something wrong in dpotsv call to blas info = "
522  << (signed int)info << endl;
523  ERR << " A = " << A.n_rows << "x" << A.n_cols << "r(A)="
524  << arma::rank(A) << endl << A;
525  ERR << " B = " << B.n_rows << "x" << B.n_cols << endl << B;
526  exit(EXIT_FAILURE);
527  }
528  return B;
529  }
530  /*
531  * Passset is a binary matrix where every column represents
532  * one datapoint. The objective is to returns a low triangular
533  * correlation matrix with 1 if the strings are equal. Zero otherwise
534  */
535  void computeCorrelationScore(arma::umat &PassSet, std::vector<UWORD> &sortedIdx,
536  std::vector<UWORD> &beginIndex) {
537  SortBooleanMatrix<arma::umat> sbm(PassSet);
538  sortedIdx = sbm.sortIndex();
540  uint beginIdx = 0;
541  beginIndex.clear();
542  beginIndex.push_back(beginIdx);
543  for (uint i = 0; i < sortedIdx.size(); i++) {
544  if (i == sortedIdx.size() - 1 || bac(sortedIdx[i], sortedIdx[i + 1]) == true) {
545  beginIdx = i + 1;
546  beginIndex.push_back(beginIdx);
547  }
548  }
549  }
550 
551  /*
552  * Given a matrix, the last column of the matrix will be
553  * checked if it reappears again. Every column in the matrix
554  * is sorted index.
555  */
556  bool detectCycle(arma::umat &X) {
557  UVEC lastColumn = X.col(X.n_cols - 1);
558  for (uint i = 0; i < X.n_cols - 2; i++) {
559  arma::umat compVec = (X.col(i) == lastColumn);
560  if (sum(compVec.col(0)) == X.n_rows)
561  return true;
562  }
563  }
564 };
565 #endif /* NNLS_BPPNNLS_HPP_ */
#define IVEC
Definition: utils.h:59
BPPNNLS(MATTYPE input, MATTYPE RHS, bool prodSent=false)
Definition: bppnnls.hpp:27
BPPNNLS(MATTYPE input, VECTYPE rhs, bool prodSent=false)
Definition: bppnnls.hpp:24
#define UVEC
Definition: utils.h:58
#define ERR
Definition: utils.h:28
#define INFO
Definition: utils.h:36
int solveNNLS()
Definition: bppnnls.hpp:30
#define UWORD
Definition: utils.h:60
Definition: nnls.hpp:12
unsigned int UINT
Definition: utils.h:68
std::vector< int > STDVEC
Definition: utils.h:67
#define WARN
Definition: utils.h:32