ANIMA  4.0
animaDistributionSampling.hxx
Go to the documentation of this file.
1 #pragma once
3 
4 #include <cmath>
5 #include <boost/math/distributions/beta.hpp>
6 
9 #include <animaBaseTensorTools.h>
10 #include <animaMatrixOperations.h>
11 
12 #include <itkMacro.h>
13 
14 namespace anima
15 {
16 
17 template <class T>
18 double SampleFromUniformDistribution(const T &a, const T &b, std::mt19937 &generator)
19 {
20  // Define distribution U[a,b) [double values]
21  std::uniform_real_distribution<T> uniDbl(a,b);
22  return uniDbl(generator);
23 }
24 
25 template <class VectorType>
26 void SampleFromUniformDistributionOn2Sphere(std::mt19937 &generator, VectorType &resVec)
27 {
28  std::uniform_real_distribution<double> uniDbl(-1.0,1.0);
29  double sqSum = 2;
30  while (sqSum > 1)
31  {
32  resVec[0] = uniDbl(generator);
33  resVec[1] = uniDbl(generator);
34 
35  sqSum = resVec[0] * resVec[0] + resVec[1] * resVec[1];
36  }
37 
38  double factor = 2.0 * std::sqrt(1.0 - sqSum);
39  resVec[0] *= factor;
40  resVec[1] *= factor;
41  resVec[2] = 2.0 * sqSum - 1.0;
42 }
43 
44 template <class T>
45 unsigned int SampleFromBernoulliDistribution(const T &p, std::mt19937 &generator)
46 {
47  std::bernoulli_distribution bernoulli(p);
48  return bernoulli(generator);
49 }
50 
51 template <class T>
52 double SampleFromGaussianDistribution(const T &mean, const T &std, std::mt19937 &generator)
53 {
54  std::normal_distribution<T> normalDist(mean,std);
55  return normalDist(generator);
56 }
57 
58 template <class VectorType, class ScalarType>
59 void SampleFromMultivariateGaussianDistribution(const VectorType &mean, const vnl_matrix <ScalarType> &mat, VectorType &resVec,
60  std::mt19937 &generator, bool isMatCovariance)
61 {
62  unsigned int vectorSize = mat.rows();
63 
64  vnl_matrix <ScalarType> stdMatrix = mat;
65 
66  if (isMatCovariance)
67  {
68  vnl_matrix <ScalarType> eVecs(vectorSize,vectorSize);
69  vnl_diag_matrix <ScalarType> eVals(vectorSize);
70 
71  itk::SymmetricEigenAnalysis < vnl_matrix <ScalarType>, vnl_diag_matrix<ScalarType>, vnl_matrix <ScalarType> > eigenComputer(vectorSize);
72  eigenComputer.ComputeEigenValuesAndVectors(mat, eVals, eVecs);
73 
74  for (unsigned int i = 0;i < vectorSize;++i)
75  eVals[i] = std::sqrt(eVals[i]);
76 
77  anima::RecomposeTensor(eVals,eVecs,stdMatrix);
78  }
79 
80  VectorType tmpVec (resVec);
81  for (unsigned int i = 0;i < vectorSize;++i)
82  tmpVec[i] = SampleFromGaussianDistribution(0.0,1.0,generator);
83 
84  for (unsigned int i = 0;i < vectorSize;++i)
85  {
86  resVec[i] = mean[i];
87  for (unsigned int j = 0;j < vectorSize;++j)
88  resVec[i] += stdMatrix(i,j) * tmpVec[j];
89  }
90 }
91 
92 template <class VectorType, class ScalarType>
93 void SampleFromVMFDistribution(const ScalarType &kappa, const VectorType &meanDirection, VectorType &resVec, std::mt19937 &generator)
94 {
95  VectorType tmpVec;
96 
97  for (unsigned int i = 0;i < 3;++i)
98  {
99  tmpVec[i] = 0;
100  resVec[i] = 0;
101  }
102 
103  // Code to compute rotation matrix from vectors, similar to registration code
104  tmpVec[2] = 1;
105 
106  vnl_matrix <double> rotationMatrix = anima::GetRotationMatrixFromVectors(tmpVec,meanDirection).GetVnlMatrix();
108 
109  if (std::abs(tmpVec[2] - 1.0) > 1.0e-6)
110  throw itk::ExceptionObject(__FILE__, __LINE__,"Von Mises & Fisher sampling requires mean direction of norm 1.",ITK_LOCATION);
111 
112  double tmpVal = std::sqrt(kappa * kappa + 1.0);
113  double b = (-2.0 * kappa + 2.0 * tmpVal) / 2.0;
114  double a = (1.0 + kappa + tmpVal) / 2.0;
115  double d = 4.0 * a * b / (1.0 + b) - 2.0 * anima::safe_log(2.0);
116 
117  boost::math::beta_distribution<double> betaDist(1.0, 1.0);
118 
119  double T = 1.0;
120  double U = std::exp(d);
121  double W = 0;
122 
123  while (2.0 * anima::safe_log(T) - T + d < anima::safe_log(U))
124  {
125  double Z = boost::math::quantile(betaDist, SampleFromUniformDistribution(0.0, 1.0, generator));
126  U = SampleFromUniformDistribution(0.0, 1.0, generator);
127  tmpVal = 1.0 - (1.0 - b) * Z;
128  T = 2.0 * a * b / tmpVal;
129  W = (1.0 - (1.0 + b) * Z) / tmpVal;
130  }
131 
132  double theta = anima::SampleFromUniformDistribution(0.0, 2.0 * M_PI, generator);
133  tmpVec[0] = std::sqrt(1.0 - W*W) * std::cos(theta);
134  tmpVec[1] = std::sqrt(1.0 - W*W) * std::sin(theta);
135  tmpVec[2] = W;
136 
137  for (unsigned int i = 0;i < 3;++i)
138  for (unsigned int j = 0;j < 3;++j)
139  resVec[i] += rotationMatrix(i,j) * tmpVec[j];
140 }
141 
142 template <class VectorType, class ScalarType>
143 void SampleFromVMFDistributionNumericallyStable(const ScalarType &kappa, const VectorType &meanDirection, VectorType &resVec, std::mt19937 &generator)
144 {
145  VectorType tmpVec;
146 
147  for (unsigned int i = 0;i < 3;++i)
148  {
149  tmpVec[i] = 0;
150  resVec[i] = 0;
151  }
152 
153  // Code to compute rotation matrix from vectors, similar to registration code
154  tmpVec[2] = 1;
155 
156  vnl_matrix <double> rotationMatrix = anima::GetRotationMatrixFromVectors(tmpVec,meanDirection).GetVnlMatrix();
158 
159  if (std::abs(tmpVec[2] - 1.0) > 1.0e-6)
160  throw itk::ExceptionObject(__FILE__, __LINE__,"Von Mises & Fisher sampling requires mean direction of norm 1.",ITK_LOCATION);
161 
162  double xi = SampleFromUniformDistribution(0.0, 1.0, generator);
163  double W = 1.0 + (anima::safe_log(xi) + anima::safe_log(1.0 - (xi - 1.0) * exp(-2.0 * kappa) / xi)) / kappa;
164  double theta = SampleFromUniformDistribution(0.0, 2.0 * M_PI, generator);
165 
166  tmpVec[0] = std::sqrt(1.0 - W*W) * std::cos(theta);
167  tmpVec[1] = std::sqrt(1.0 - W*W) * std::sin(theta);
168  tmpVec[2] = W;
169 
170  for (unsigned int i = 0;i < 3;++i)
171  for (unsigned int j = 0;j < 3;++j)
172  resVec[i] += rotationMatrix(i,j) * tmpVec[j];
173 }
174 
175 template <class ScalarType, class VectorType>
176 void
177 SampleFromWatsonDistribution(const ScalarType &kappa, const VectorType &meanDirection, VectorType &resVec, unsigned int DataDimension, std::mt19937 &generator)
178 {
179  /**********************************************************************************************/
201  VectorType tmpVec;
202 
203  for (unsigned int i = 0;i < DataDimension;++i)
204  {
205  tmpVec[i] = 0;
206  resVec[i] = 0;
207  }
208 
209  // Code to compute rotation matrix from vectors, taken from registration code
210  tmpVec[2] = 1;
211 
212  VectorType rotationNormal;
213  anima::ComputeCrossProduct(tmpVec, meanDirection, rotationNormal);
214  anima::Normalize(rotationNormal, rotationNormal);
215 
216  // Now resuming onto sampling around direction [0,0,1]
218 
219  double rotationAngle = tmpVec[0];
220 
221  if (std::abs(tmpVec[2] - 1.0) > 1.0e-6)
222  throw itk::ExceptionObject(__FILE__, __LINE__,"The Watson distribution is on the 2-sphere.",ITK_LOCATION);
223 
224  double U, V, S;
225  if (kappa > 1.0e-6) // Bipolar distribution
226  {
227  U = SampleFromUniformDistribution(0.0, 1.0, generator);
228  S = 1.0 + std::log(U + (1.0 - U) * std::exp(-kappa)) / kappa;
229 
230  V = SampleFromUniformDistribution(0.0, 1.0, generator);
231 
232  if (V > 1.0e-6)
233  {
234  while (std::log(V) > kappa * S * (S - 1.0))
235  {
236  U = SampleFromUniformDistribution(0.0, 1.0, generator);
237  S = 1.0 + std::log(U + (1.0 - U) * std::exp(-kappa)) / kappa;
238 
239  V = SampleFromUniformDistribution(0.0, 1.0, generator);
240 
241  if (V < 1.0e-6)
242  break;
243  }
244  }
245  }
246  else if (kappa < -1.0e-6) // Gridle distribution
247  {
248  double C1 = std::sqrt(std::abs(kappa));
249  double C2 = std::atan(C1);
250  U = SampleFromUniformDistribution(0.0, 1.0, generator);
251  V = SampleFromUniformDistribution(0.0, 1.0, generator);
252  S = (1.0 / C1) * std::tan(C2 * U);
253 
254  double T = kappa * S * S;
255  while (V > (1.0 - T) * std::exp(T))
256  {
257  U = SampleFromUniformDistribution(0.0, 1.0, generator);
258  V = SampleFromUniformDistribution(0.0, 1.0, generator);
259  S = (1.0 / C1) * std::tan(C2 * U);
260  T = kappa * S * S;
261  }
262  }
263  else
264  {
265  // Sampling uniformly on the sphere
266  S = std::cos(SampleFromUniformDistribution(0.0, M_PI, generator));
267  }
268 
269  double phi = SampleFromUniformDistribution(0.0, 2.0 * M_PI, generator);
270 
271  tmpVec[0] = std::sqrt(1.0 - S*S) * std::cos(phi);
272  tmpVec[1] = std::sqrt(1.0 - S*S) * std::sin(phi);
273  tmpVec[2] = S;
274 
275  anima::RotateAroundAxis(tmpVec, rotationAngle, rotationNormal, resVec);
276 
277  double resNorm = anima::ComputeNorm(resVec);
278 
279  if (std::abs(resNorm - 1.0) > 1.0e-4)
280  {
281  std::cout << "Sampled direction norm: " << resNorm << std::endl;
282  std::cout << "Mean direction: " << meanDirection << std::endl;
283  std::cout << "Concentration parameter: " << kappa << std::endl;
284  throw itk::ExceptionObject(__FILE__, __LINE__,"The Watson sampler should generate points on the 2-sphere.",ITK_LOCATION);
285  }
286 
287  anima::Normalize(resVec,resVec);
288 }
289 
290 template <class ScalarType, unsigned int DataDimension>
291 void
292 SampleFromWatsonDistribution(const ScalarType &kappa, const vnl_vector_fixed < ScalarType, DataDimension > &meanDirection, vnl_vector_fixed < ScalarType, DataDimension > &resVec, std::mt19937 &generator)
293 {
294  /**********************************************************************************************/
314  resVec.fill(0.0);
315  SampleFromWatsonDistribution(kappa, meanDirection, resVec, DataDimension, generator);
316 }
317 
318 template <class ScalarType, unsigned int DataDimension>
319 void
320 SampleFromWatsonDistribution(const ScalarType &kappa, const itk::Point < ScalarType, DataDimension > &meanDirection, itk::Point < ScalarType, DataDimension > &resVec, std::mt19937 &generator)
321 {
322  /**********************************************************************************************/
342  resVec.Fill(0.0);
343  SampleFromWatsonDistribution(kappa, meanDirection, resVec, DataDimension, generator);
344 }
345 
346 template <class ScalarType, unsigned int DataDimension>
347 void
348 SampleFromWatsonDistribution(const ScalarType &kappa, const itk::Vector < ScalarType, DataDimension > &meanDirection, itk::Vector < ScalarType, DataDimension > &resVec, std::mt19937 &generator)
349 {
350  /**********************************************************************************************/
370  resVec.Fill(0.0);
371  SampleFromWatsonDistribution(kappa, meanDirection, resVec, DataDimension, generator);
372 }
373 
374 } // end of namespace anima
void SampleFromUniformDistributionOn2Sphere(std::mt19937 &generator, VectorType &resVec)
STL namespace.
double ComputeNorm(const VectorType &v)
void RotateAroundAxis(const std::vector< T1 > &v, const T2 &phi, const std::vector< T3 > &normalVec, std::vector< T4 > &resVec)
double xi(const T &k)
itk::Matrix< double, 3, 3 > GetRotationMatrixFromVectors(const VectorType &first_direction, const VectorType &second_direction, const unsigned int dimension)
void SampleFromVMFDistribution(const ScalarType &kappa, const VectorType &meanDirection, VectorType &resVec, std::mt19937 &generator)
void SampleFromVMFDistributionNumericallyStable(const ScalarType &kappa, const VectorType &meanDirection, VectorType &resVec, std::mt19937 &generator)
unsigned int SampleFromBernoulliDistribution(const T &p, std::mt19937 &generator)
void RecomposeTensor(vnl_diag_matrix< T1 > &eigs, vnl_matrix< T1 > &eigVecs, vnl_matrix< T2 > &resMatrix)
Recompose tensor from values extracted using SymmetricEigenAnalysis (vnl_symmetric_eigensystem transp...
double SampleFromGaussianDistribution(const T &mean, const T &std, std::mt19937 &generator)
void Normalize(const VectorType &v, const unsigned int NDimension, VectorType &resVec)
double safe_log(const ScalarType x)
void SampleFromMultivariateGaussianDistribution(const VectorType &mean, const vnl_matrix< ScalarType > &mat, VectorType &resVec, std::mt19937 &generator, bool isMatCovariance=true)
void TransformCartesianToSphericalCoordinates(const VectorType &v, VectorType &resVec)
void ComputeCrossProduct(const VectorType &v1, const VectorType &v2, const unsigned int NDimension, VectorType &resVec)
void SampleFromWatsonDistribution(const ScalarType &kappa, const VectorType &meanDirection, VectorType &resVec, unsigned int DataDimension, std::mt19937 &generator)
double SampleFromUniformDistribution(const T &a, const T &b, std::mt19937 &generator)