Program Listing for File math_numeric.hpp¶
↰ Return to documentation for file (lib/utilities/math_numeric.hpp
)
// ---------------------------------------------------------------------
// This file is part of falcon-core.
//
// Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders
//
// Falcon-server is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Falcon-server is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with falcon-core. If not, see <http://www.gnu.org/licenses/>.
// ---------------------------------------------------------------------
/*
* Utilities for mathematical and numeric operations
*
*/
#pragma once
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iterator>
#include <limits>
#include <memory>
#include <random>
#include <vector>
#include <functional>
#include <string>
// compares two double-precision floating points (or one with zero) and returns
// true if their relative error is within a certain absolute or relative
// tolerance
bool compare_doubles(
double A, double B = 0,
double maxAbsoluteError = std::numeric_limits<double>::epsilon(),
double maxRelativeError = std::numeric_limits<double>::epsilon());
int next_pow2(int n);
template <typename T> class Range {
public:
Range(T a) : lower_(a), upper_(a) {}
Range(T lower, T upper) : lower_(lower), upper_(upper) {
assert(upper_ >= lower_);
}
Range(std::vector<T> limits) {
assert(limits.size() == 2);
lower_ = limits[0];
upper_ = limits[1];
}
T lower() const { return lower_; }
T upper() const { return upper_; }
bool inrange(T value) const { return (value >= lower_ && value <= upper_); }
bool inopenrange(T value) const { return (value > lower_ && value < upper_); }
template <typename R> bool inrange(const Range<R> &other) const {
return (static_cast<T>(other.lower()) >= lower_ &&
static_cast<T>(other.upper()) <= upper_);
}
template <typename R> bool inopenrange(const Range<R> &other) const {
return (static_cast<T>(other.lower()) > lower_ &&
static_cast<T>(other.upper()) < upper_);
}
template <typename R> bool overlapping(const Range<R> &other) const {
return (static_cast<T>(other.lower()) <= upper_ &&
static_cast<T>(other.upper()) >= lower_);
}
std::string to_string() const {
std::string s =
"[" + std::to_string(lower_) + ", " + std::to_string(upper_) + "]";
return s;
}
protected:
T lower_;
T upper_;
};
/* compute sum excluding NaN values */
template <typename ForwardIterator>
double nan_sum(ForwardIterator first, ForwardIterator last);
/* compute mean excluding NaN values;
* if the number of elements is not provided, it will be internally computed */
template <typename ForwardIterator>
double nan_mean(ForwardIterator first, ForwardIterator last, int n_elem = -1);
/* linspace:
* generate n elements equally spaced from min and max;
* min and max are included in the sequence of values.
*/
template <typename T> std::vector<T> linspace(T min, T max, std::size_t n);
template <typename T1, typename T2> class Linear {
public:
Linear(size_t n, T1 *x, T2 *y) {
assert(n > 0);
// calculate the averages of arrays x and y
double xa = 0, ya = 0;
for (size_t i = 0; i < n; i++) {
xa += x[i];
ya += y[i];
}
xa /= n;
ya /= n;
// calculate auxiliary sums
double xx = 0, yy = 0, xy = 0;
for (size_t i = 0; i < n; i++) {
double tmpx = x[i] - xa, tmpy = y[i] - ya;
xx += tmpx * tmpx;
yy += tmpy * tmpy;
xy += tmpx * tmpy;
}
// calculate regression line parameters
// make sure slope is not infinite
assert(fabs(xx) != 0);
m_b = xy / xx;
m_a = ya - m_b * xa;
m_coeff = (fabs(yy) == 0) ? 1 : xy / sqrt(xx * yy);
}
double getValue(T1 x) {
return m_a + m_b * x;
}
double getSlope() {
return m_b;
}
double getIntercept() {
return m_a;
}
double getCoefficient() {
return m_coeff;
}
private:
double m_a, m_b, m_coeff;
};
// http://stackoverflow.com/questions/3738349/fast-algorithm-for-repeated-calculation-of-percentile
// A known disadvantage is that two heaps grow without bounds. User class should
// use clear() periodically.
// TODO: limit the size of the heaps, possibly with about periodic random
// sampling and recreating the heaps (std::make_heap)
template <class T> class IterativePercentile {
public:
IterativePercentile(double percentile = 0.5) : _percentile(percentile) {}
std::size_t size() { return (_lower.size() + _upper.size()); }
// Adds a number in O(log(n))
void add(const T &x) {
if (_lower.empty() || x <= _lower.front()) {
_lower.push_back(x);
std::push_heap(_lower.begin(), _lower.end(), std::less<T>());
} else {
_upper.push_back(x);
std::push_heap(_upper.begin(), _upper.end(), std::greater<T>());
}
unsigned size_lower =
static_cast<unsigned>((_lower.size() + _upper.size()) * _percentile) +
1;
if (_lower.size() > size_lower) {
// lower to upper
std::pop_heap(_lower.begin(), _lower.end(), std::less<T>());
_upper.push_back(_lower.back());
std::push_heap(_upper.begin(), _upper.end(), std::greater<T>());
_lower.pop_back();
} else if (_lower.size() < size_lower) {
// upper to lower
std::pop_heap(_upper.begin(), _upper.end(), std::greater<T>());
_lower.push_back(_upper.back());
std::push_heap(_lower.begin(), _lower.end(), std::less<T>());
_upper.pop_back();
}
}
const T &get() const { return _lower.front(); }
void clear() {
_lower.clear();
_upper.clear();
}
private:
double _percentile;
std::vector<T> _lower;
std::vector<T> _upper;
};
#include "math_numeric.ipp"