Statismo  0.10.1
 All Classes Namespaces Functions Typedefs
Nystrom.h
1 #ifndef NYSTROM_H
2 #define NYSTROM_H
3 
4 /*
5  * This file is part of the statismo library.
6  *
7  * Author: Marcel Luethi (marcel.luethi@unibas.ch)
8  *
9  * Copyright (c) 2011 University of Basel
10  * All rights reserved.
11  *
12  * Statismo is licensed under the BSD licence (3 clause) license
13  */
14 
15 #include <boost/thread.hpp>
16 
17 #include "CommonTypes.h"
18 #include "Config.h"
19 #include "Kernels.h"
20 #include "RandSVD.h"
21 #include "Representer.h"
22 
23 namespace statismo {
24 
29 template <class T>
30 class Nystrom {
31  public:
32 
33 
34  typedef typename Representer<T>::PointType PointType;
36  typedef typename DomainType::DomainPointsListType DomainPointsListType;
37 
38  static Nystrom* Create(const Representer<T>* representer, const MatrixValuedKernel<PointType>& kernel, unsigned numEigenfunctions, unsigned numberOfPointsForApproximation) {
39  return new Nystrom(representer, kernel, numEigenfunctions, numberOfPointsForApproximation);
40  }
41 
42 
46  MatrixType computeEigenfunctionsAtPoint(const PointType& pt) const {
47 
48  unsigned kernelDim = m_kernel.GetDimension();
49 
50  // for every domain point x in the list, we compute the kernel vector
51  // kx = (k(x, x1), ... k(x, xm))
52  // since the kernel is matrix valued, kx is actually a matrix
53  MatrixType kxi = MatrixType::Zero(kernelDim, m_nystromPoints.size() * kernelDim);
54 
55  for (unsigned j = 0; j < m_nystromPoints.size(); j++) {
56  kxi.block(0, j * kernelDim, kernelDim, kernelDim) = m_kernel(pt, m_nystromPoints[j]);
57  }
58 
59 
60  MatrixType resMat = MatrixType::Zero(kernelDim, m_numEigenfunctions);
61  for (unsigned j = 0; j < m_numEigenfunctions; j++) {
62  MatrixType x = (kxi * m_nystromMatrix.col(j));
63  resMat.block(0, j, kernelDim, 1) = x;
64  }
65  return resMat;
66  }
67 
68 
72  const VectorType& getEigenvalues() const {
73  return m_eigenvalues;
74  }
75 
76 
77  private:
78 
79 
80  Nystrom(const Representer<T>* representer, const MatrixValuedKernel<PointType>& kernel, unsigned numEigenfunctions, unsigned numberOfPointsForApproximation)
81  : m_representer(representer), m_kernel(kernel), m_numEigenfunctions(numEigenfunctions) {
82 
83  DomainType domain = m_representer->GetDomain();
84  m_nystromPoints = getNystromPoints(domain, numberOfPointsForApproximation);
85  unsigned numDomainPoints = domain.GetNumberOfPoints();
86 
87  // compute a eigenvalue decomposition of the kernel matrix, evaluated at the points used for the
88  // nystrom approximation
89 
90  MatrixType U; // will hold the eigenvectors (principal components)
91  VectorType D; // will hold the eigenvalues (variance)
92  computeKernelMatrixDecomposition(&kernel, m_nystromPoints, numEigenfunctions, U, D);
93 
94 
95  // precompute the part of the nystrom approximation, which is independent of the domain point
96  float normFactor = static_cast<float>(m_nystromPoints.size()) / static_cast<float>(numDomainPoints);
97  m_nystromMatrix = std::sqrt(normFactor) * (U.leftCols(numEigenfunctions)
98  * D.topRows(numEigenfunctions).asDiagonal().inverse());
99 
100  m_eigenvalues = (1.0f / normFactor) * D.topRows(numEigenfunctions);
101 
102  }
103 
104 
105  /*
106  * Returns a random set of points from the domain.
107  *
108  * @param domain the domain to sample from
109  * @param numberOfPoints the size of the sample
110  */
111  std::vector<PointType> getNystromPoints(DomainType& domain, unsigned numberOfPoints) const {
112 
113  numberOfPoints = std::min(numberOfPoints, domain.GetNumberOfPoints());
114 
115  std::vector<PointType> shuffledDomainPoints = domain.GetDomainPoints();
116  std::random_shuffle ( shuffledDomainPoints.begin(), shuffledDomainPoints.end() );
117 
118  return std::vector<PointType>(shuffledDomainPoints.begin(), shuffledDomainPoints.begin() + numberOfPoints);
119  }
120 
121 
122 
123 
129  void computeKernelMatrixDecomposition(const MatrixValuedKernel<PointType>* kernel,
130  const std::vector<PointType>& xs, unsigned numComponents,
131  MatrixType& U, VectorType& D) const {
132  unsigned kernelDim = kernel->GetDimension();
133 
134  unsigned n = xs.size();
135  MatrixTypeDoublePrecision K = MatrixTypeDoublePrecision::Zero(
136  n * kernelDim, n * kernelDim);
137  for (unsigned i = 0; i < n; ++i) {
138  for (unsigned j = i; j < n; ++j) {
139  MatrixType k_xixj = (*kernel)(xs[i], xs[j]);
140  for (unsigned d1 = 0; d1 < kernelDim; d1++) {
141  for (unsigned d2 = 0; d2 < kernelDim; d2++) {
142  double elem_d1d2 = k_xixj(d1, d2);
143  K(i * kernelDim + d1, j * kernelDim + d2) = elem_d1d2;
144  K(j * kernelDim + d2, i * kernelDim + d1) = elem_d1d2;
145  }
146  }
147  }
148  }
149 
150  typedef RandSVD<double> SVDType;
151  SVDType svd(K, numComponents * kernelDim);
152  U = svd.matrixU().cast<ScalarType>();
153  D = svd.singularValues().cast<ScalarType>();
154  }
155 
156 
157  // private, to prevent use
158  Nystrom();
159  Nystrom<T>& operator=(const Nystrom<T>& rhs);
160  Nystrom(const Nystrom<T>& orig);
161 
162 
163  //
164  // members
165  //
166 
167  const Representer<T>* m_representer;
168  MatrixType m_nystromMatrix;
169  VectorType m_eigenvalues;
170  std::vector<PointType> m_nystromPoints;
171  const MatrixValuedKernel<PointType>& m_kernel;
172  unsigned m_numEigenfunctions;
173 
174 
175 };
176 
177 
178 } // namespace statismo
179 #endif // NYSTROM_H
MatrixType computeEigenfunctionsAtPoint(const PointType &pt) const
Definition: Nystrom.h:46
const unsigned GetNumberOfPoints() const
Definition: Domain.h:72
Definition: Nystrom.h:30
virtual unsigned GetDimension() const
Definition: Kernels.h:82
const DomainPointsListType & GetDomainPoints() const
Definition: Domain.h:67
float ScalarType
the type that is used for all vector and matrices throughout the library.
Definition: CommonTypes.h:60
const VectorType & getEigenvalues() const
Definition: Nystrom.h:72
Definition: Domain.h:51