Fleet  0.0.9
Inference in the LOT
Numerics.h
Go to the documentation of this file.
1 #pragma once
2 
3 // This includes our thread-safe lgamma and must be declared before math.h is imported
4 // This means that we should NOT import math.h anywhere else
5 #define _REENTRANT 1
6 
7 #include <map>
8 #include <random>
9 #include <iostream>
10 #include <assert.h>
11 #include <algorithm>
12 #include <math.h>
13 
15 // Constants
17 
18 const double LOG2 = log(2.0); // clang doesn't like constexpr??
19 const double ROOT2 = sqrt(2.0);
21 constexpr double NaN = std::numeric_limits<double>::quiet_NaN();
22 constexpr double pi = M_PI;
23 constexpr double tau = 2*pi; // fuck pi
24 
25 
27 // An inherited integral type so we can subtype double, int, etc.
29 
30 // here we give N so that we can define different types if we want to!
31 template<typename T, int N=1>
33  T value;
34  InheritedIntegral(T v) : value(v) {}
35  InheritedIntegral() : value(0) {}
36 
37  operator T() const { return value; }
38 };
39 
40 
47 template<typename T>
49  bool operator()(const T &x, const T &y) const {
50  if(std::isnan(x)) return false;
51  if(std::isnan(y)) return true;
52  return x < y;
53  }
54 };
55 
56 
58 // Rounding
60 
61 template<typename T>
62 T round(T v, int n) {
63  auto m = pow(10,n);
64  return std::round(v*m)/m;
65 }
66 
68 // A faster (?) logarithm
70 
71 //float __int_as_float(int32_t a) { float r; memcpy(&r, &a, sizeof(r)); return r;}
72 //int __float_as_int(float a) { int r; memcpy(&r, &a, sizeof(r)); return r;}
73 //
76 //float fastlogf(float a) {
77 // float m, r, s, t, i, f;
78 // int32_t e;
79 //
80 // e = (__float_as_int(a) - 0x3f2aaaab) & 0xff800000;
81 // m = __int_as_float (__float_as_int(a) - e);
82 // i = (float) e * 1.19209290e-7f; // 0x1.0p-23
83 // /* m in [2/3, 4/3] */
84 // f = m - 1.0f;
85 // s = f * f;
86 // /* Compute log1p(f) for f in [-1/3, 1/3] */
87 // r = fmaf(0.230836749f, f, -0.279208571f); // 0x1.d8c0f0p-3, -0x1.1de8dap-2
88 // t = fmaf(0.331826031f, f, -0.498910338f); // 0x1.53ca34p-2, -0x1.fee25ap-2
89 // r = fmaf(r, s, t);
90 // r = fmaf(r, s, f);
91 // r = fmaf(i, 0.693147182f, r); // 0x1.62e430p-1 // log(2)
92 // return r;
93 //}
94 
95 
97 // Probability
99 
100 
112 template<typename T>
113 T get_log1pexp_breakout_bound(const double precision, T f(T), const T lower=-1e6, const T upper=1e6) {
114 
115  assert(f(lower) > precision or f(upper) > precision);
116 
117  const T m = lower + (upper-lower)/2.0;
118  if (upper-lower < 1e-3) {
119  return m;
120  }
121  else if (f(m) < precision) { // bound on precision here
122  return get_log1pexp_breakout_bound<T>(precision, f, m, upper);
123  }
124  else {
125  return get_log1pexp_breakout_bound<T>(precision, f, lower, m);
126  }
127 }
128 
129 
130 template<typename T>
131 T logplusexp(const T a, const T b) {
132  // An approximate logplusexp -- the check on z<-25 makes it much faster since it saves many log,exp operations
133  // It is easy to derive a good polynomial approximation that is a bit faster (using sollya) on [-25,0] but that appears
134  // not to be worth it at this point.
135 
136  if (a == -infinity) return b;
137  else if(b == -infinity) return a;
138 
139  T mx = std::max(a,b);
140 
141  // compute a bound here at such that log1p(exp(z)) doesn't affect the result much (up to 1e-6)
142  // for floats, this ends up being about -13.8
143  // TODO: I Wish we could make this constexpr, but the math functions are not constexpr. Static is fine though
144  // it should only be run once at the start.
145  static const float breakout = get_log1pexp_breakout_bound<T>(1e-6, +[](T x) -> T { return log1p(exp(x)); });
146 
147  T z = std::min(a,b)-mx;
148  if(z < breakout) {
149  return mx; // save us from having to do anything
150  }
151  else {
152  return mx + log1p(exp(z)); // mx + log(exp(a-mx)+exp(b-mx));
153  }
154 }
155 
162 template<typename T>
163 T logplusexp_full(const T a, const T b) {
164  T mx = std::max(a,b);
165  return mx + log1p(exp(std::min(a,b)-mx));
166 }
167 
168 
175 template<typename t>
176 double logsumexp(const t& v) {
177  double lse = -infinity;
178  for(auto& x : v) {
179  lse = logplusexp(lse,x);
180  }
181  return lse;
182 }
183 
184 template<typename t>
185 double logsumexp(const std::vector<t>& v, double f(const t&) ) {
186  double lse = -infinity;
187  for(auto& x : v) {
188  lse = logplusexp(lse,f(x));
189  }
190  return lse;
191 }
192 
198 template<typename T> int sgn(T val) {
199  return (T(0) < val) - (val < T(0));
200 }
201 
207 template<typename T> T sgnlog(T val) {
208  return sgn(val)*log(std::abs(val));
209 }
210 
211 
213 // Numerical functions
215 
216 double mylgamma(double v) {
217  // thread safe version gives our own place to store sgn
218  int sgn = 1;
219  auto ret = lgamma_r(v, &sgn);
220  return ret;
221 }
222 
223 double mygamma(double v) {
224  // thread safe usage
225  int sgn = 1;
226  auto ret = lgamma_r(v, &sgn);
227  return sgn*exp(ret);
228 }
229 
230 double lfactorial(double x) {
231  return mylgamma(x+1);
232 }
233 
237 
238 template<typename T>
239 T inv_logit(const T z) {
240  return 1.0 / (1.0 + exp(-z));
241 }
242 
243 template<typename T>
244 T logit(const T p) {
245  return log(p)-log(1-p);
246 }
247 
251 
252 template<typename T>
253 bool approx_eq(const T x, const T y, double prec=0.00001) {
254  return abs(x-y)<prec;
255 }
256 
257 
261 
262 template<typename T>
263 T weighted_quantile(std::vector<std::pair<T,double>>& v, double q) {
264  // find the weighted quantile, from a list of pairs <T,logprobability>
265  // NOTE: may modify v..
266 
267  std::sort(v.begin(), v.end()); // sort by T
268 
269  // get the normalizer (log space)
270  double z = -infinity;
271  for(auto [x,lp] : v) z = logplusexp(z,lp);
272 
273  double cumulative = -infinity;
274  for(auto [x,lp] : v) {
275  cumulative = logplusexp(cumulative, lp);
276  if(exp(cumulative-z) >= q) {
277  return x;
278  }
279  }
280 
281  assert(false);
282 }
283 
287 
294 template<typename T>
295 T sum(std::vector<T>& v){
296  T s = 0.0;
297  for(auto& x : v) s += x;
298  return s;
299 }
300 
306 template<typename T>
307 T mean(std::vector<T>& v){
308  return sum(v)/v.size();
309 }
310 
317 template<typename T>
318 T sd(std::vector<T>& v) {
319  assert(v.size() > 1);
320  T m = mean(v);
321  T s = 0.0;
322  for(auto& x : v) {
323  s += pow(m-x,2.);
324  }
325  return sqrt(s / (v.size()-1));
326 }
327 
328 
334 template<typename T>
335 T MAD(std::vector<T>& v) {
336  auto m = median(v);
337  auto n = v.size();
338 
339  std::vector<T> d(n);
340  for(size_t i=0;i<n;i++) {
341  d[i] = std::abs(v.at(i) - m);
342  }
343 
344  return median(d);
345 }
346 
353 template<typename T>
354 T quantile(std::vector<T>& v, double q) {
355  const size_t n = v.size();
356  assert(n> 0);
357  std::sort(v.begin(), v.end(), floating_point_compare<T>());
358 
359  int i = floor(n*q); // floor for small n
360  return v.at(i);
361 }
362 
369 template<typename T>
370 T median(std::vector<T>& v) {
371  const size_t n = v.size();
372  if(n == 0) return NaN;
373  if(n == 1) return v.at(0);
374  std::sort(v.begin(), v.end(), floating_point_compare<T>());
375 
376  if(n % 2 == 0) {
377  return (v[n/2] + v[n/2-1]) / 2;
378  }
379  else {
380  return v[n/2];
381  }
382 }
383 
384 
393 template<typename T>
394 T trimmed_mean(std::vector<T>& v, float a, float b) {
395  const size_t n = v.size();
396  if(n == 0) return NaN;
397  if(n == 1) return v.at(0);
398 
399  std::sort(v.begin(), v.end(), floating_point_compare<T>());
400  assert(a >= 0.0 and b <= 1.0 and a<b && "*** Bad range in trimmed_mean");
401 
402  double s = 0.0;
403  int k = 0;
404  for(int i=a*n;i<b*n;i++) {
405  s += v.at(i);
406  k++;
407  }
408  return s/k;
409 }
410 
417 template<typename T>
418 T trimmed_mean(std::vector<T>& v, float pct) {
419  return trimmed_mean(v,pct,pct);
420 }
421 
422 
428 template<typename T>
429 T mymax(const std::vector<T>& v) {
430  const size_t n = v.size();
431  if(n == 0) return NaN;
432  if(n == 1) return v.at(0);
433 
434  auto m = v[0];
435  for(size_t i=0;i<n;i++)
436  m = std::max(v.at(i), m);
437 
438  return m;
439 }
440 
441 
447 template<typename T, typename K>
448 K mymax(std::map<T,K>& v) {
449  auto m = v.begin()->second;
450  for(const auto& x : v) {
451  m = std::max(x.second, m);
452  }
453  return m;
454 }
455 
456 
457 template<typename T>
458 std::strong_ordering fp_ordering(T& x, T& y) {
466  if( std::isnan(x) and std::isnan(y)) {
467  return std::strong_ordering::equivalent;
468  }
469  else if( std::isnan(x) ) {
470  return std::strong_ordering::less;
471  }
472  else if (std::isnan(y)) {
473  return std::strong_ordering::greater;
474  }
475  else {
476  auto v = (x <=> y);
477 
478  if (v == std::partial_ordering::less) return std::strong_ordering::less;
479  else if(v == std::partial_ordering::greater) return std::strong_ordering::greater;
480  else if(v == std::partial_ordering::equivalent) return std::strong_ordering::equivalent;
481  else assert(false);
482  }
483 }
InheritedIntegral()
Definition: Numerics.h:35
constexpr double pi
Definition: Numerics.h:22
const double ROOT2
Definition: Numerics.h:19
double mylgamma(double v)
Definition: Numerics.h:216
This allows sorting of things that contain NaN.
Definition: Numerics.h:48
T inv_logit(const T z)
logic / inverse logit are pretty useful
Definition: Numerics.h:239
T logplusexp_full(const T a, const T b)
logsumexp with no shortcuts for precision
Definition: Numerics.h:163
Definition: Numerics.h:32
constexpr double infinity
Definition: Numerics.h:20
bool operator()(const T &x, const T &y) const
Definition: Numerics.h:49
T logplusexp(const T a, const T b)
Definition: Numerics.h:131
double logsumexp(const t &v)
Compute log(sum(exp(v)). For now, this just unrolls logplusexp, but we might consider the faster (sta...
Definition: Numerics.h:176
const double LOG2
Definition: Numerics.h:18
constexpr double tau
Definition: Numerics.h:23
double lfactorial(double x)
Definition: Numerics.h:230
T get_log1pexp_breakout_bound(const double precision, T f(T), const T lower=-1e6, const T upper=1e6)
Tis takes a function f(x) and finds a bound B so that f(x) < precision for all x < B...
Definition: Numerics.h:113
T sgnlog(T val)
The log of |val| but with sign preserved.
Definition: Numerics.h:207
int sgn(T val)
Sign function.
Definition: Numerics.h:198
constexpr double NaN
Definition: Numerics.h:21
T value
Definition: Numerics.h:33
T round(T v, int n)
Definition: Numerics.h:62
T logit(const T p)
Definition: Numerics.h:244
T weighted_quantile(std::vector< std::pair< T, double >> &v, double q)
Definition: Numerics.h:263
double mygamma(double v)
Definition: Numerics.h:223
InheritedIntegral(T v)
Definition: Numerics.h:34