Xmipp  v3.23.11-Nereus
kSVD.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: Carlos Oscar S. Sorzano (coss@cnb.csic.es)
4  *
5  * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  * 02111-1307 USA
21  *
22  * All comments concerning this program package may be sent to the
23  * e-mail address 'xmipp@cnb.csic.es'
24  ***************************************************************************/
25 
26 #include "kSVD.h"
27 
28 /* ------------------------------------------------------------------------- */
29 /* Orthogonal Matching Pursuit */
30 /* ------------------------------------------------------------------------- */
32  const Matrix2D<double> &D, int S, Matrix1D<double> &alpha)
33 {
34  // Compute approximation error
35  double approximationError=0;
37  approximationError+=x(i)*x(i);
38  double errorLimit=approximationError*1e-10;
39 
40  // Initialize the number of available atoms
41  int N=D.Ydim(); // Dimension of each atom
42  if (0 == N) REPORT_ERROR(ERR_PARAM_INCORRECT, "Ydim of each atom must not be zero (0)");
43  int K=D.Xdim(); // Number of atoms in the dictionary
44  Matrix1D<int> availableAtoms;
45  availableAtoms.initZeros(K);
46  availableAtoms.initConstant(1);
47 
48  // Compute the projection of x onto each of the atoms and look for the
49  // maximum
54  c.initZeros(K);
55  e.initZeros(K); e.initConstant(1);
56  u.initZeros(K); u.initConstant(1);
57 
58  // Compute dot product between x and di
59  int km=0;
60  double ckm=-1;
61  for (int k=0; k<K; k++)
62  {
63  for (int j=0; j<N; j++)
64  c(k)+=D(j,k)*x(j);
65 
66  // Check if this is the maximum
67  if (ABS(c(k))>ckm)
68  {
69  km=k;
70  ckm=ABS(c(k));
71  }
72  }
73 
74  // Delete Js from the available indexes and update the approximation error
75  availableAtoms(km)=0;
76  approximationError-=ckm*ckm;
77 
78  // Annotate Jk
79  std::vector<int> J;
80  int s=0;
81  J.push_back(km);
82 
83  // Build the R matrix
84  Matrix2D<double> R(S,K);
85  R(0,km)=u(km);
86 
87  while (s<S-1 && approximationError>errorLimit)
88  {
89  // Update all the rest of R except Js
90  ckm=0;
91  int nextKm=-1;
92  for (int k=0; k<K; k++)
93  {
94  // If already used, skip it
95  if (!availableAtoms(k)) continue;
96 
97  // Compute the product between the atoms km and k
98  double aux=0;
99  for (int j=0; j<N; j++)
100  aux+=D(j,km)*D(j,k);
101  R(s,k)=aux;
102 
103  // Readjust R
104  for (int n=0; n<s; n++)
105  R(s,k)-=R(n,km)*R(n,k);
106  if (u(km)>XMIPP_EQUAL_ACCURACY)
107  R(s,k)/=u(km);
108 
109  // Update the rest of variables
110  c(k)=c(k)*u(k)-c(km)*R(s,k);
111  e(k)-=R(s,k)*R(s,k);
112  u(k)=sqrt(ABS(e(k)));
113  if (u(k)!=0) c(k)/=u(k);
114 
115  // Check if this is the best c(k)
116  if (ABS(c(k))>ckm)
117  {
118  nextKm=k;
119  ckm=ABS(c(k));
120  }
121  }
122 
123  // Delete km from the available indexes and update the approximation error
124  km=nextKm;
125  J.push_back(km);
126  availableAtoms(km)=0;
127  s++;
128  R(s,km)=u(km);
129  approximationError-=ckm*ckm;
130  }
131 
132  // Perform backsubstitution
133  alpha.initZeros(K);
134  for (int k=s; k>=0; k--)
135  {
136  int Jk=J[k];
137  for (int n=s; n>=k+1; n--)
138  c(Jk)-=R(k,J[n])*c(J[n]);
139  if (R(k,Jk)!=0) c(Jk)/=R(k,Jk);
140  alpha(Jk)=c(Jk);
141  }
142 
143  return sqrt(ABS(approximationError));
144 }
145 
146 /* ------------------------------------------------------------------------- */
147 /* Lasso (shooting) */
148 /* ------------------------------------------------------------------------- */
149 double lasso(const Matrix1D<double> &x,
150  const Matrix2D<double> &D,
151  const Matrix2D<double> &DtD,
152  const Matrix2D<double> &DtDlambdaInv,
153  double lambda, Matrix1D<double> &alpha,
154  const int maxIter, const double tol)
155 {
156  // Compute the ridge least squares solution
157  // Compute D^t*x
158  Matrix1D<double> Dtx;
159  matrixOperation_Atx(D,x,Dtx); // Dtx=D^t*x
160  matrixOperation_Ax(DtDlambdaInv,Dtx,alpha); // alpha=DtDlambdaInv*Dtx
161 
162  // Iterate over alpha
163  Matrix1D<double> alphaOld;
164  int iter=0;
165  bool finished=false;
166  while (!finished)
167  {
168  // Keep the current alpha
169  alphaOld=alpha;
170 
171  // Update alpha
173  {
174  double S=-Dtx(i);
175  for (size_t j=0; j<alpha.size(); j++)
176  if (i!=j) S+=DtD(i,j)*alpha(j);
177  if (fabs(S)<lambda)
178  alpha(i)=0;
179  else if (S>lambda)
180  alpha(i)=(lambda-S)/DtD(i,i);
181  else
182  alpha(i)=(-lambda-S)/DtD(i,i);
183  }
184 
185  // Prepare for next iteration
186  iter++;
187  double normDiff=0;
188  double normAlpha=0;
190  {
191  double diff=alpha(i)-alphaOld(i);
192  normDiff+=diff*diff;
193  normAlpha+=alpha(i)*alpha(i);
194  }
195  finished=(iter>=maxIter) || normDiff/normAlpha<tol;
196  }
197 
198  // Compute the approximation error
199  Matrix1D<double> xp(x.size());
200  for (size_t j=0; j<D.Xdim(); j++)
201  if (alpha(j)!=0)
202  for (size_t i=0; i<D.Ydim(); i++)
203  xp(i)+=D(i,j)*alpha(j);
204  double approximationError=0;
206  {
207  double diff=xp(i)-x(i);
208  approximationError+=diff*diff;
209  }
210 
211  return sqrt(ABS(approximationError));
212 }
213 
214 #ifdef UNUSED // detected as unused 29.6.2018
215 /* ------------------------------------------------------------------------- */
216 /* kSVD */
217 /* ------------------------------------------------------------------------- */
218 //#define DEBUG
219 double kSVD(const std::vector< Matrix1D<double> > &X, int S,
220  Matrix2D<double> &D, std::vector< Matrix1D<double> > &Alpha,
221  bool keepFirstColumn, int maxIter, double minChange,
222  int projectionMethod, double lambda)
223 {
224  size_t Nvectors=X.size();
225  size_t K=D.Xdim(); // Number of atoms
226  size_t N=D.Ydim(); // Dimension of the atoms
227 
228  // Ask for memory for Alpha if necessary
229  if (Alpha.size()!=Nvectors)
230  {
231  int Nalpha=Alpha.size();
233  dummy.initZeros(K);
234  for (size_t i=0; i<Nvectors-Nalpha; i++)
235  Alpha.push_back(dummy);
236  }
237 
238  // Ask for memory for which vectors use which atoms
239  std::vector< std::vector<int> > listUsers;
240  for (size_t k=0; k<K; k++)
241  {
242  std::vector<int> dummy;
243  listUsers.push_back(dummy);
244  }
245 
246  // Compute the power of the input vectors
248  power.initZeros(Nvectors);
249  for (size_t n=0; n<Nvectors; n++)
251  power(n)+=X[n](i)*X[n](i);
252  double avgPower=power.sum(true);
253 
254  // Perform kSVD
255  int iterations=0;
257  error.initZeros(Nvectors);
258  double previousError=0, currentError=0;
259  bool limitAchieved=false;
260  do {
261  // Sparse coding step
262  Matrix2D<double> DtD, DtDlambda, DtDlambdaInv;
263  if (projectionMethod==LASSO_PROJECTION)
264  {
265  DtD.initZeros(K,K);
267  // Compute the dot product between the columns i and j of D
268  for (size_t k=0; k<D.Ydim(); k++)
269  DtD(i,j)+=D(k,i)*D(k,j);
270  DtDlambda=DtD;
271  for (size_t i=0; i<K; i++)
272  DtDlambda(i,i)+=lambda;
273  DtDlambda.inv(DtDlambdaInv);
274  }
275  std::cout << "Sparse coding\n";
276  init_progress_bar(Nvectors);
277  for (size_t n=0; n<Nvectors; n++)
278  {
279  // Compute alpha
280  if (projectionMethod==LASSO_PROJECTION)
281  error(n)=lasso(X[n],D,DtD,DtDlambdaInv,lambda,Alpha[n]);
282  else
283  error(n)=orthogonalMatchingPursuit(X[n],D,S,Alpha[n]);
284 
285  // Check which are the atoms this vector is using
286  for (size_t k=0; k<K; k++)
287  if (Alpha[n](k)!=0)
288  listUsers[k].push_back(n);
289  if (n%100==0) progress_bar(n);
290  }
291  progress_bar(Nvectors);
292  double Noise=error.sum(true);
293  std::cout << "kSVD error=" << Noise << " "
294  << " (" << 100*Noise/avgPower << "%)" << std::endl;
295  if (projectionMethod==LASSO_PROJECTION)
296  {
297  int countNonZeros=0;
298  for (size_t n=0; n<Nvectors; n++)
300  if (Alpha[n](i)!=0) countNonZeros++;
301  std::cout << "Average sparsity = " << (double)countNonZeros/
302  Nvectors << std::endl;
303  }
304 
305  // Codebook update
306  Matrix2D<double> EkR, U, V;
307  Matrix1D<double> xp, W;
308  int firstKtoUpdate=0;
309  if (keepFirstColumn) firstKtoUpdate=1;
310  std::cout << "Updating codebook\n";
312  for (size_t k=firstKtoUpdate; k<K; k++)
313  {
314  // Compute the error that would be committed if the
315  // atom k were not used
316  size_t Nk=listUsers[k].size();
317  // std::cout << "Atom k=" << k << " is used by " << Nk << " vectors\n";
318  if (Nk>1)
319  {
320  EkR.initZeros(N,Nk);
321  for (size_t nk=0; nk<Nk; nk++)
322  {
323  // Select vector nk
324  int n=listUsers[k][nk];
325 
326  // Compute the represented vector if atom is not used
327  xp.initZeros(N);
328  for (size_t kp=0; kp<K; kp++)
329  {
330  double w=Alpha[n](kp);
331  if (kp!=k && w!=0)
332  for (size_t j=0; j<N; j++)
333  xp(j)+=w*D(j,kp);
334  }
335 
336  // Fill the error matrix EkR
337  const Matrix1D<double> &x=X[n];
338  for (size_t j=0; j<N; j++)
339  EkR(j,nk)=x(j)-xp(j);
340  }
341 
342  // Compute the SVD decomposition of the EkR matrix
343  svdcmp(EkR,U,W,V); // EkR=U * diag(W) * V^t
344 
345  // Update the dictionary with the first column of U
346  double normU0=0;
347  for (size_t j=0; j<N; j++)
348  {
349  double uj0=U(j,0);
350  normU0+=uj0*uj0;
351  }
352  normU0=sqrt(normU0);
353  double inormU0=1/normU0;
354  for (size_t j=0; j<N; j++)
355  D(j,k)=U(j,0)*inormU0;
356 
357  // Update the coefficients of the users of atom k
358  double W0=W(0)*normU0;
359  for (size_t nk=0; nk<Nk; nk++)
360  {
361  // Select vector nk
362  int n=listUsers[k][nk];
363  Alpha[n](k)=V(nk,0)*W0;
364  }
365 
366  #ifdef DEBUG
367  for (int n=0; n<Nvectors; n++)
368  {
369  Matrix1D<double> xp=D*Alpha[n];
370  std::cout << "x= " << X[n].transpose() << std::endl
371  << "xp=" << xp.transpose() << std::endl
372  << "diff norm=" << (X[n]-xp).module() << std::endl;
373  }
374  #endif
375  }
376  else if (Nk==0)
377  {
378  // Pick the vector that is worse represented
379  int iworse=error.maxIndex();
380  error(iworse)=0;
381  for (size_t j=0; j<N; j++)
382  D(j,k)=X[iworse](j);
383  }
384  progress_bar(k);
385  }
386  progress_bar(K);
387 
388  // Prepare for next iteration
389  iterations++;
390  currentError=error.sum(true);
391  if (previousError==0) previousError=currentError;
392  else {
393  limitAchieved=
394  ABS((previousError-currentError)/previousError)<minChange;
395  std::cout << "Change=" << ((previousError-currentError)/previousError) << std::endl;
396  previousError=currentError;
397  }
398  } while (iterations<maxIter && !limitAchieved);
399 
400  return currentError;
401 }
402 #undef DEBUG
403 #endif
#define FOR_ALL_ELEMENTS_IN_MATRIX2D(m)
Definition: matrix2d.h:104
void init_progress_bar(long total)
size_t Xdim() const
Definition: matrix2d.h:575
Parameter incorrect.
Definition: xmipp_error.h:181
#define REPORT_ERROR(nerr, ErrormMsg)
Definition: xmipp_error.h:211
size_t size() const
Definition: matrix1d.h:508
doublereal * c
void sqrt(Image< double > &op)
void inv(Matrix2D< T > &result) const
Definition: matrix2d.cpp:663
doublereal * w
Matrix2D< T > transpose() const
Definition: matrix2d.cpp:1314
glob_prnt iter
doublereal * x
#define i
ql0001_ & k(htemp+1),(cvec+1),(atemp+1),(bj+1),(bl+1),(bu+1),(x+1),(clamda+1), &iout, infoqp, &zero,(w+1), &lenw,(iw+1), &leniw, &glob_grd.epsmac
Matrix1D< T > transpose() const
Definition: matrix1d.cpp:644
int maxIndex() const
Definition: matrix1d.cpp:610
#define FOR_ALL_ELEMENTS_IN_MATRIX1D(v)
Definition: matrix1d.h:72
double * lambda
#define XMIPP_EQUAL_ACCURACY
Definition: xmipp_macros.h:119
void progress_bar(long rlen)
#define ABS(x)
Definition: xmipp_macros.h:142
size_t Ydim() const
Definition: matrix2d.h:584
double lasso(const Matrix1D< double > &x, const Matrix2D< double > &D, const Matrix2D< double > &DtD, const Matrix2D< double > &DtDlambdaInv, double lambda, Matrix1D< double > &alpha, const int maxIter, const double tol)
Definition: kSVD.cpp:149
double sum(bool average=false) const
Definition: matrix1d.cpp:652
void matrixOperation_Ax(const Matrix2D< double > &A, const Matrix1D< double > &x, Matrix1D< double > &y)
Definition: matrix2d.cpp:424
double dummy
void initZeros()
Definition: matrix1d.h:592
double orthogonalMatchingPursuit(const Matrix1D< double > &x, const Matrix2D< double > &D, int S, Matrix1D< double > &alpha)
Definition: kSVD.cpp:31
void svdcmp(const Matrix2D< T > &a, Matrix2D< double > &u, Matrix1D< double > &w, Matrix2D< double > &v)
Definition: matrix2d.cpp:125
void matrixOperation_Atx(const Matrix2D< double > &A, const Matrix1D< double > &x, Matrix1D< double > &y)
Definition: matrix2d.cpp:488
#define j
void power(Image< double > &op)
void error(char *s)
Definition: tools.cpp:107
void initZeros()
Definition: matrix2d.h:626
doublereal * u
constexpr int K
void initConstant(T val)
Definition: matrix1d.cpp:83
int * n
constexpr int LASSO_PROJECTION
Definition: kSVD.h:72