se2ez
distribution.h
Go to the documentation of this file.
1 /* Author: Zachary Kingston */
2 
3 #ifndef SE2EZ_CORE_KDE_
4 #define SE2EZ_CORE_KDE_
5 
6 #include <ompl/datastructures/PDF.h>
7 #include <ompl/datastructures/Grid.h>
8 #include <ompl/datastructures/NearestNeighborsGNAT.h>
9 
10 #include <se2ez/core/log.h>
11 #include <se2ez/core/math.h>
13 
14 namespace se2ez
15 {
16  SE2EZ_CLASS_FORWARD(Distribution)
18  {
19  public:
20  Distribution(unsigned int dimension) : dimension_(dimension)
21  {
22  }
23 
24  virtual ~Distribution() = default;
25 
26  virtual double pdf(const Eigen::Ref<const Eigen::VectorXd> &x) = 0;
27  virtual void sample(Eigen::Ref<Eigen::VectorXd> sample) = 0;
28 
29  void addPoint(const Eigen::Ref<const Eigen::VectorXd> &x)
30  {
31  if (x.size() != dimension_)
32  throw std::runtime_error(log::format("Invalid dimension %1% for data point (should be %2%)",
33  x.size(), dimension_));
34 
35  auto entry = new Eigen::VectorXd(dimension_);
36  *entry = x;
37 
38  data_.emplace_back(entry);
39  addedPoint(entry);
40  }
41 
43  {
44  data_.reserve(data_.size() + xs.size());
45  for (const auto &x : xs)
46  addPoint(x);
47  }
48 
49  protected:
50  virtual void addedPoint(const Eigen::VectorXd * /*x*/)
51  {
52  }
53 
54  unsigned int dimension_;
56  };
57 
58  class Histogram : public Distribution
59  {
60  public:
61  Histogram(unsigned int dimension, //
62  const Eigen::Ref<const Eigen::VectorXd> &min, const Eigen::Ref<const Eigen::VectorXd> &max,
63  unsigned int cells)
64  : Distribution(dimension)
65  , min_(min)
66  , max_(max)
67  , width_((max_ - min_) / (double)cells)
68  , cells_(cells)
70  {
71  if (min.size() != dimension_)
72  throw std::runtime_error(
73  log::format("Invalid dimension %1% for minimum (should be %2%)", min.size(), dimension_));
74 
75  if (max.size() != dimension_)
76  throw std::runtime_error(
77  log::format("Invalid dimension %1% for maximum (should be %2%)", max.size(), dimension_));
78 
79  if (cells == 0)
80  throw std::runtime_error(log::format("Number of cells must be positive"));
81 
82  for (unsigned int i = 0; i < dimension_; ++i)
83  if (max_[i] < min_[i])
84  throw std::runtime_error(log::format("On index %1%: minimum %2% is greater than max %3%!",
85  i, min_[i], max_[i]));
86  }
87 
89  {
90  Bins::CellArray cells;
91  histogram_.getCells(cells);
92  for (const auto &cell : cells)
93  delete cell->data;
94  }
95 
96  double pdf(const Eigen::Ref<const Eigen::VectorXd> &x) override
97  {
98  if (not data_.size())
99  throw std::runtime_error(log::format("Cannot compute PDF without data."));
100 
101  if (x.size() != dimension_)
102  throw std::runtime_error(log::format("Invalid dimension %1% for data point (should be %2%)",
103  x.size(), dimension_));
104  Bin *bin = getCell(x);
105  return bin->data->points.size() / data_.size();
106  }
107 
108  void sample(Eigen::Ref<Eigen::VectorXd> sample) override
109  {
110  if (sample.size() != dimension_)
111  throw std::runtime_error(log::format("Invalid dimension %1% for sample (should be %2%)",
112  sample.size(), dimension_));
113 
114  auto data = pdf_.sample(math::uniformReal(0, 1.));
115  const Index &index = data->bin->coord;
116 
117  for (unsigned int i = 0; i < dimension_; ++i)
118  {
119  double l = min_[i] + index[i] * width_[i];
120  double u = l + width_[i];
121 
122  sample[i] = math::uniformReal(l, u);
123  }
124  }
125 
127  {
128  if (dimension_ > 2)
129  throw std::runtime_error("Dimension to high to print!");
130 
131  if (dimension_ == 1)
132  {
133  unsigned int size = 30;
134  for (unsigned int i = 0; i < cells_; ++i)
135  {
136  Index index(dimension_);
137  index[0] = i;
138 
139  Bin *bin = histogram_.getCell(index);
140  unsigned int v = (bin) ? bin->data->points.size() : 0;
141 
142  unsigned int ch = math::remap(0, maxCell_, v, 0, size);
143  unsigned int j = 0;
144  for (; j < ch; ++j)
145  out << "@";
146  for (; j < size; ++j)
147  out << " ";
148 
149  out << " : " << (double)v / data_.size();
150  out << std::endl;
151  }
152  }
153 
154  if (dimension_ == 2)
155  {
156  const char ramp[11] = " .:-=+*#%@";
157  for (unsigned int i = 0; i < cells_; ++i)
158  {
159  for (unsigned int j = 0; j < cells_; ++j)
160  {
161  Index index(dimension_);
162  index[0] = i;
163  index[1] = j;
164 
165  Bin *bin = histogram_.getCell(index);
166  unsigned int v = (bin) ? bin->data->points.size() : 0;
167 
168  unsigned int ch = math::remap(0, maxCell_, v, 0, 10);
169  out << ramp[ch];
170  }
171 
172  out << std::endl;
173  }
174  }
175  }
176 
177  protected:
178  void addedPoint(const Eigen::VectorXd *x) override
179  {
180  Bin *bin = getCell(*x);
181  bin->data->points.emplace_back(x);
182 
183  unsigned int points = bin->data->points.size();
184  pdf_.update(bin->data->element, points);
185 
186  maxCell_ = (maxCell_ < points) ? points : maxCell_;
187  }
188 
189  private:
190  struct Data;
191 
192  using Bins = ompl::Grid<Data *>;
193  using Bin = Bins::Cell;
194  using Index = Bins::Coord;
195  using PDF = ompl::PDF<Data *>;
196 
197  struct Data
198  {
200  PDF::Element *element;
202  };
203 
204  Index getIndex(const Eigen::Ref<const Eigen::VectorXd> &x)
205  {
206  Index index(dimension_);
207  for (unsigned int i = 0; i < dimension_; ++i)
208  index[i] = (x[i] - min_[i]) / width_[i];
209 
210  return index;
211  }
212 
213  Bin *getCell(const Eigen::Ref<const Eigen::VectorXd> &x)
214  {
215  Index index = getIndex(x);
216  Bin *bin = histogram_.getCell(index);
217 
218  if (not bin)
219  {
220  bin = histogram_.createCell(index);
221  bin->data = new Data;
222  bin->data->bin = bin;
223  bin->data->element = pdf_.add(bin->data, 0.);
224 
225  histogram_.add(bin);
226  }
227 
228  return bin;
229  }
230 
231  const Eigen::VectorXd min_;
232  const Eigen::VectorXd max_;
233  const Eigen::VectorXd width_;
234  const unsigned int cells_;
235 
236  unsigned int maxCell_{0};
237 
240  }; // namespace se2ez
241 
242  class KDE : public Distribution
243  {
244  public:
245  KDE(unsigned int dimension)
246  : Distribution(dimension)
247  , min_(Eigen::VectorXd::Zero(dimension))
248  , max_(Eigen::VectorXd::Zero(dimension))
249  , mean_(Eigen::VectorXd::Zero(dimension))
250  , sqMean_(Eigen::VectorXd::Zero(dimension))
251  , H_(Eigen::VectorXd::Zero(dimension))
252  {
253  neighbors_.setDistanceFunction([](const auto &a, const auto &b) { return (*a - *b).norm(); });
254  }
255 
256  double pdf(const Eigen::Ref<const Eigen::VectorXd> &x) override
257  {
258  if (not data_.size())
259  throw std::runtime_error(log::format("Cannot compute PDF without data."));
260 
261  if (x.size() != dimension_)
262  throw std::runtime_error(log::format("Invalid dimension %1% for data point (should be %2%)",
263  x.size(), dimension_));
264 
265  bandwidth();
266 
268  entries.reserve(NEIGHBORS);
269 
270  const Eigen::VectorXd &v = x;
271  neighbors_.nearestK(&v, NEIGHBORS, entries);
272 
273  // std::vector<const Eigen::VectorXd *> entries;
274  // entries.reserve(NEIGHBORS);
275 
276  // const Eigen::VectorXd &v = x;
277  // neighbors_.nearestR(&v, H_.maxCoeff() * 10, entries);
278 
279  double pdf = 0;
280  for (const auto &entry : entries)
281  {
282  double a = 1.0;
283  for (unsigned int i = 0; i < dimension_; ++i)
284  a *= kernel(x[i], (*entry)[i], H_[i]) / H_[i];
285 
286  pdf += a;
287  }
288 
289  if (entries.size())
290  return pdf / entries.size();
291  return 0.;
292  }
293 
294  void sample(Eigen::Ref<Eigen::VectorXd> sample) override
295  {
296  if (sample.size() != dimension_)
297  throw std::runtime_error(log::format("Invalid dimension %1% for sample (should be %2%)",
298  sample.size(), dimension_));
299 
300  bandwidth();
301  sample = *data_[math::uniformInteger(0, data_.size() - 1)];
302  for (unsigned int i = 0; i < dimension_; ++i)
303  sample[i] = generate(sample[i], H_[i]);
304  }
305 
306  void toString(std::ostream &out, const Eigen::Ref<const Eigen::VectorXd> &min,
307  const Eigen::Ref<const Eigen::VectorXd> &max, unsigned int resolution)
308  {
309  if (dimension_ > 2)
310  throw std::runtime_error("Dimension to high to print!");
311 
312  if (dimension_ == 1)
313  {
314  unsigned int size = 30;
315  double width = (max[0] - min[0]) / resolution;
316  double map[resolution];
317  double m = 0;
318 
319  for (unsigned int i = 0; i < resolution; ++i)
320  {
321  Eigen::VectorXd s(1);
322  s[0] = min[0] + i * width;
323 
324  map[i] = pdf(s);
325  m = (m < map[i]) ? map[i] : m;
326  }
327 
328  for (unsigned int i = 0; i < resolution; ++i)
329  {
330  unsigned int ch = math::remap(0, m, map[i], 0, size);
331  unsigned int j = 0;
332  for (; j < ch; ++j)
333  out << "@";
334  for (; j < size; ++j)
335  out << " ";
336 
337  out << " : " << map[i];
338 
339  out << std::endl;
340  }
341  }
342 
343  if (dimension_ == 2)
344  {
345  const char ramp[11] = " .:-=+*#%@";
346 
347  Eigen::VectorXd width = (max - min) / resolution;
348  double map[resolution][resolution];
349  double m = 0;
350 
351  for (unsigned int i = 0; i < resolution; ++i)
352  {
353  Eigen::VectorXd s(2);
354  s[0] = min[0] + i * width[0];
355  for (unsigned int j = 0; j < resolution; ++j)
356  {
357  s[1] = min[1] + j * width[1];
358 
359  map[i][j] = pdf(s);
360  m = (m < map[i][j]) ? map[i][j] : m;
361  }
362  }
363 
364  for (unsigned int i = 0; i < resolution; ++i)
365  {
366  for (unsigned int j = 0; j < resolution; ++j)
367  {
368  unsigned int ch = math::remap(0, m, map[i][j], 0, 10);
369  ch = (ch == 10) ? 9 : ch;
370 
371  out << ramp[ch];
372  }
373 
374  out << std::endl;
375  }
376  }
377  }
378 
379  protected:
380  void addedPoint(const Eigen::VectorXd *x) override
381  {
382  neighbors_.add(x);
383 
384  mean_ += *x;
385  sqMean_ += (*x).cwiseProduct(*x);
386 
387  dirty_ = true;
388  }
389 
390  virtual double kernel(double x, double m, double s) = 0;
391  virtual double generate(double m, double s) = 0;
392 
393  private:
394  void bandwidth()
395  {
396  if (not dirty_)
397  return;
398 
399  unsigned int num = data_.size();
400  for (unsigned int i = 0; i < dimension_; ++i)
401  {
402  double mean = mean_[i] / num;
403  double sqMean = sqMean_[i] / num;
404 
405  double sigma = std::sqrt(sqMean - mean * mean);
406  H_[i] = std::pow(4. / (dimension_ + 2), 1. / (dimension_ + 4)) //
407  * std::pow(num, -1. / (dimension_ + 4)) //
408  * sigma;
409  }
410 
411  dirty_ = false;
412  }
413 
414  Eigen::VectorXd min_;
415  Eigen::VectorXd max_;
416 
417  Eigen::VectorXd mean_;
418  Eigen::VectorXd sqMean_;
419 
420  bool dirty_{true};
421  Eigen::VectorXd H_;
422  ompl::NearestNeighborsGNAT<const Eigen::VectorXd *> neighbors_;
423 
424  const unsigned int NEIGHBORS = 1000;
425  };
426 
427  class GaussianKDE : public KDE
428  {
429  public:
430  GaussianKDE(unsigned int dimension) : KDE(dimension)
431  {
432  }
433 
434  protected:
435  double kernel(double x, double m, double s) override
436  {
437  double z = (x - m) / s;
438  // return std::exp(-0.5 * z * z) / (s * std::sqrt(math::pipi));
439  // return std::exp(-1. * z * z / (2. * s * s));
440  return std::exp(-1. * z * z / 2.);
441  }
442 
443  double generate(double m, double s) override
444  {
445  return math::gaussianReal(m, s);
446  }
447  };
448 } // namespace se2ez
449 
450 #endif
Bins::Cell Bin
Definition: distribution.h:193
double gaussianReal(double mean, double std)
Return a random number sampled from a gaussain with given std and mean.
Definition: math.cpp:236
Eigen::VectorXd sqMean_
Definition: distribution.h:418
double pdf(const Eigen::Ref< const Eigen::VectorXd > &x) override
Definition: distribution.h:96
Bin * getCell(const Eigen::Ref< const Eigen::VectorXd > &x)
Definition: distribution.h:213
void addPoints(const tf::EigenVector< Eigen::VectorXd > &xs)
Definition: distribution.h:42
ompl::Grid< Data * > Bins
Definition: distribution.h:192
Distribution(unsigned int dimension)
Definition: distribution.h:20
const Eigen::VectorXd max_
Definition: distribution.h:232
T exp(T... args)
virtual void addedPoint(const Eigen::VectorXd *)
Definition: distribution.h:50
double uniformReal(double lower, double upper)
Return a uniform random number between lower and upper.
Definition: math.cpp:217
std::vector< const Eigen::VectorXd * > points
Definition: distribution.h:201
void bandwidth()
Definition: distribution.h:394
const unsigned int cells_
Definition: distribution.h:234
T endl(T... args)
Eigen::VectorXd mean_
Definition: distribution.h:417
const Eigen::VectorXd width_
Definition: distribution.h:233
GaussianKDE(unsigned int dimension)
Definition: distribution.h:430
void toString(std::ostream &out, const Eigen::Ref< const Eigen::VectorXd > &min, const Eigen::Ref< const Eigen::VectorXd > &max, unsigned int resolution)
Definition: distribution.h:306
PDF::Element * element
Definition: distribution.h:200
Eigen::VectorXd min_
Definition: distribution.h:414
void addedPoint(const Eigen::VectorXd *x) override
Definition: distribution.h:380
unsigned int dimension_
Definition: distribution.h:54
KDE(unsigned int dimension)
Definition: distribution.h:245
Eigen::VectorXd max_
Definition: distribution.h:415
void sample(Eigen::Ref< Eigen::VectorXd > sample) override
Definition: distribution.h:108
double kernel(double x, double m, double s) override
Definition: distribution.h:435
void addedPoint(const Eigen::VectorXd *x) override
Definition: distribution.h:178
Histogram(unsigned int dimension, const Eigen::Ref< const Eigen::VectorXd > &min, const Eigen::Ref< const Eigen::VectorXd > &max, unsigned int cells)
Definition: distribution.h:61
T size(T... args)
unsigned int maxCell_
Definition: distribution.h:236
Index getIndex(const Eigen::Ref< const Eigen::VectorXd > &x)
Definition: distribution.h:204
Bins::Coord Index
Definition: distribution.h:194
void sample(Eigen::Ref< Eigen::VectorXd > sample) override
Definition: distribution.h:294
T pow(T... args)
std::string format(const std::string &fmt, Args &&... args)
Definition: log.h:25
void addPoint(const Eigen::Ref< const Eigen::VectorXd > &x)
Definition: distribution.h:29
const Eigen::VectorXd min_
Definition: distribution.h:231
ompl::NearestNeighborsGNAT< const Eigen::VectorXd * > neighbors_
Definition: distribution.h:422
int uniformInteger(int lower, int upper)
Return a uniform random integer between lower and upper (inclusive).
Definition: math.cpp:228
double generate(double m, double s) override
Definition: distribution.h:443
Main namespace.
Definition: collision.h:11
void toString(std::ostream &out)
Definition: distribution.h:126
T sqrt(T... args)
double remap(double a1, double a2, double av, double b1, double b2)
Remap a value av in the interval a1, a2 to the interval b1, b2.
Definition: math.cpp:160
double pdf(const Eigen::Ref< const Eigen::VectorXd > &x) override
Definition: distribution.h:256
std::vector< const Eigen::VectorXd * > data_
Definition: distribution.h:55
#define SE2EZ_CLASS_FORWARD(C)
Definition: class_forward.h:9
Eigen::VectorXd H_
Definition: distribution.h:421
T reserve(T... args)
ompl::PDF< Data * > PDF
Definition: distribution.h:195