Canopy  1.0
The header-only random forests library
vonMisesDistribution.hpp
Go to the documentation of this file.
1 #ifndef VONMISESDISTRIBUTION_HPP
2 #define VONMISESDISTRIBUTION_HPP
3 
11 #include <cmath>
12 #include <unsupported/Eigen/NonLinearOptimization>
13 #include <boost/math/special_functions/bessel.hpp>
14 #include <stdexcept>
15 #include <iostream>
17 
18 namespace canopy
19 {
20 
29 {
30  public:
31  // Methods
32  // -------
33 
37  : mu(0.0), kappa(0.0), S(0.0), C(0.0), pdf_normaliser(1.0)
38  {
39  // Nothing to do here
40  }
41 
44  void initialise()
45  {
46  mu = 0.0;
47  kappa = 0.0;
48  S = 0.0;
49  C = 0.0;
50  pdf_normaliser = 1.0;
51  }
52 
68  template <class TLabelIterator, class TIdIterator>
69  void fit(TLabelIterator first_label, const TLabelIterator last_label, TIdIterator /*unused*/)
70  {
71  S = 0.0, C = 0.0;
72 
73  const int n_data = std::distance(first_label,last_label);
74 
75  // Loop through the data points, accumulating statistics
76  for( ; first_label != last_label; ++first_label)
77  {
78  S += std::sin(*first_label);
79  C += std::cos(*first_label);
80  }
81 
82  // Length of resultant vector
83  const double R2 = S*S + C*C;
84 
85  // Mean direction
86  mu = std::atan2(S,C);
87 
88  // Unbiased R statistic
89  const double Re = std::sqrt(R2) / n_data;
90 
91  // Find the kappa parameter
92  if(Re > 0.98)
93  {
94  // There appears to be no solution for kappa in this case (look into this further!)
95  // Saturate at roughly the value for when Re = 0.98
96  kappa = 25.0;
97  }
98  else
99  {
100  // Set up and solve the non-linear equation for kappa
101  vonMisesKappaFunctor vmftrinstance(Re);
102  Eigen::VectorXd kappa_vec(1);
103  kappa_vec << 25.0;
104  Eigen::HybridNonLinearSolver<vonMisesKappaFunctor> solver(vmftrinstance);
105  solver.hybrj1(kappa_vec);
106  kappa = kappa_vec(0,0);
107  pdf_normaliser = 1.0/(2.0*M_PI*boost::math::cyl_bessel_i(0,kappa));
108  }
109  }
110 
111 
121  template<class TId>
122  float pdf(const float x, const TId /*id*/) const
123  {
124  return pdf_normaliser*std::exp(kappa*std::cos(x - mu));
125  }
126 
135  float pdf(const float x) const
136  {
137  return pdf(x,0);
138  }
139 
145  void printOut(std::ofstream& stream) const
146  {
147  stream << mu << " " << kappa;
148  }
149 
155  void readIn(std::ifstream& stream)
156  {
157  stream >> mu;
158  stream >> kappa;
159 
160  S = std::sin(mu);
161  C = std::cos(mu);
162  pdf_normaliser = 1.0/(2.0*M_PI*boost::math::cyl_bessel_i(0,kappa));
163  }
164 
166  float entropy() const
167  {
168  using boost::math::cyl_bessel_i;
169  return std::log(2.0*M_PI*cyl_bessel_i(0,kappa)) - kappa*cyl_bessel_i(1,kappa)/cyl_bessel_i(0,kappa);
170  }
171 
185  template <class TId>
186  void combineWith(const vonMisesDistribution& dist, const TId /*id*/)
187  {
188  // Add the weighted mu value to the sine and cosine sums
189  S += (dist.kappa)*(dist.S);
190  C += (dist.kappa)*(dist.C);
191  }
192 
193  // Normalises a distribution to find the resulting mu and kappa
194  // Uses the sensor fusion approach of Stienne 2011
200  void normalise()
201  {
202  mu = std::atan2(S,C);
203  kappa = std::hypot(S,C);
204 
205  // Don't want this to raise an exception if kappa is very large (can happen
206  // if there are many trees)
207  try
208  {
209  const double kappa_bessel = boost::math::cyl_bessel_i(0,kappa);
210  pdf_normaliser = 1.0/(2.0*M_PI*kappa_bessel);
211  }
212  catch (std::overflow_error)
213  {
214  kappa = 500.0;
215  pdf_normaliser = 6.35397e-217;
216  }
217  }
218 
224  void reset()
225  {
226  initialise();
227  }
228 
232  float getMu() const
233  {
234  return mu;
235  }
236 
240  float getKappa() const
241  {
242  return kappa;
243  }
244 
248  friend std::ofstream& operator<< (std::ofstream& stream, const vonMisesDistribution& dist) { dist.printOut(stream); return stream;}
249 
253  friend std::ifstream& operator>> (std::ifstream& stream, vonMisesDistribution& dist) { dist.readIn(stream); return stream;}
254 
255  protected:
256  // Data
257  float mu;
258  float kappa;
259  double S;
260  double C;
262 
263 };
264 
265 } // end of namespace
266 
267 #endif
268 // VONMISESDISTRIBUTION_HPP
Contains declaration of the canopy::vonMisesKappaFunctor struct, used for numerically solving for the...
double S
Sum of sines, used during fitting and combining distributions.
Definition: vonMisesDistribution.hpp:259
float kappa
The distribution&#39;s concentration parameter.
Definition: vonMisesDistribution.hpp:258
A functor object to work with Eigen&#39;s non-linear solver to numerically solve for the kappa parameter ...
Definition: vonMisesKappaFunctor.hpp:20
void printOut(std::ofstream &stream) const
Prints the defining parameters of the distribution to an output filestream.
Definition: vonMisesDistribution.hpp:145
friend std::ifstream & operator>>(std::ifstream &stream, vonMisesDistribution &dist)
Allows the distribution to be written to read from a file via the streaming operator &#39;>>&#39;...
Definition: vonMisesDistribution.hpp:253
float pdf(const float x, const TId) const
Returns the probability of a particular label.
Definition: vonMisesDistribution.hpp:122
void reset()
Reset method.
Definition: vonMisesDistribution.hpp:224
A distribution that defines the probabilities over a circular-valued label.
Definition: vonMisesDistribution.hpp:28
void readIn(std::ifstream &stream)
Reads the defining parameters of the distribution from a filestream.
Definition: vonMisesDistribution.hpp:155
Namespace containing the canopy library for random forest models.
Definition: circularRegressor.hpp:13
void normalise()
Normalise the distribution to ensure it is valid.
Definition: vonMisesDistribution.hpp:200
void fit(TLabelIterator first_label, const TLabelIterator last_label, TIdIterator)
Fit the distribution to a set of labels.
Definition: vonMisesDistribution.hpp:69
vonMisesDistribution()
Basic constructor.
Definition: vonMisesDistribution.hpp:36
double C
Sum of cosines, used during fitting and combining distributions.
Definition: vonMisesDistribution.hpp:260
friend std::ofstream & operator<<(std::ofstream &stream, const vonMisesDistribution &dist)
Allows the distribution to be written to a file via the streaming operator &#39;<<&#39;.
Definition: vonMisesDistribution.hpp:248
float pdf_normaliser
Pre-calculated normalisation constant of the pdf equation.
Definition: vonMisesDistribution.hpp:261
float mu
The distribution&#39;s circular mean parameter.
Definition: vonMisesDistribution.hpp:257
float getMu() const
Get the mu parameter.
Definition: vonMisesDistribution.hpp:232
float getKappa() const
Get the kappa parameter.
Definition: vonMisesDistribution.hpp:240
void combineWith(const vonMisesDistribution &dist, const TId)
Combine this distribution with a second by summing the probability values, without normalisation...
Definition: vonMisesDistribution.hpp:186
float pdf(const float x) const
Returns the probability of a particular label.
Definition: vonMisesDistribution.hpp:135
void initialise()
Initialise the distribution before fitting.
Definition: vonMisesDistribution.hpp:44
float entropy() const
Return the (differential) entropy of the distribution.
Definition: vonMisesDistribution.hpp:166