334 lines
9.9 KiB
C++
334 lines
9.9 KiB
C++
/* ----------------------------------------------------------------------------
|
|
|
|
* GTSAM Copyright 2010, Georgia Tech Research Corporation,
|
|
* Atlanta, Georgia 30332-0415
|
|
* All Rights Reserved
|
|
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
|
|
|
|
* See LICENSE for the license information
|
|
|
|
* -------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* @file Vector.cpp
|
|
* @brief typedef and functions to augment Eigen's Vectors
|
|
* @author Kai Ni
|
|
* @author Frank Dellaert
|
|
* @author Varun Agrawal
|
|
*/
|
|
|
|
#include <gtsam/base/Vector.h>
|
|
#include <boost/optional.hpp>
|
|
#include <stdexcept>
|
|
#include <cstdarg>
|
|
#include <limits>
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <vector>
|
|
|
|
using namespace std;
|
|
|
|
namespace gtsam {
|
|
|
|
/* *************************************************************************
|
|
* References:
|
|
* 1. https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
|
|
* 2. https://floating-point-gui.de/errors/comparison/
|
|
* ************************************************************************* */
|
|
bool fpEqual(double a, double b, double tol, bool check_relative_also) {
|
|
using std::abs;
|
|
using std::isnan;
|
|
using std::isinf;
|
|
|
|
double DOUBLE_MIN_NORMAL = numeric_limits<double>::min() + 1.0;
|
|
double larger = (abs(b) > abs(a)) ? abs(b) : abs(a);
|
|
|
|
// handle NaNs
|
|
if(isnan(a) || isnan(b)) {
|
|
return isnan(a) && isnan(b);
|
|
}
|
|
// handle inf
|
|
else if(isinf(a) || isinf(b)) {
|
|
return isinf(a) && isinf(b);
|
|
}
|
|
// If the two values are zero or both are extremely close to it
|
|
// relative error is less meaningful here
|
|
else if(a == 0 || b == 0 || (abs(a) + abs(b)) < DOUBLE_MIN_NORMAL) {
|
|
return abs(a-b) <= tol * DOUBLE_MIN_NORMAL;
|
|
}
|
|
// Check if the numbers are really close.
|
|
// Needed when comparing numbers near zero or tol is in vicinity.
|
|
else if (abs(a - b) <= tol) {
|
|
return true;
|
|
}
|
|
// Check for relative error
|
|
else if (abs(a - b) <=
|
|
tol * min(larger, std::numeric_limits<double>::max()) &&
|
|
check_relative_also) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
//3 argument call
|
|
void print(const Vector& v, const string& s, ostream& stream) {
|
|
size_t n = v.size();
|
|
|
|
stream << s << "[";
|
|
for(size_t i=0; i<n; i++) {
|
|
stream << setprecision(9) << v(i) << (i<n-1 ? "; " : "");
|
|
}
|
|
stream << "];" << endl;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
//1 or 2 argument call
|
|
void print(const Vector& v, const string& s) {
|
|
print(v, s, cout);
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
void save(const Vector& v, const string &s, const string& filename) {
|
|
fstream stream(filename.c_str(), fstream::out | fstream::app);
|
|
print(v, s + "=", stream);
|
|
stream.close();
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool operator==(const Vector& vec1,const Vector& vec2) {
|
|
if (vec1.size() != vec2.size()) return false;
|
|
size_t m = vec1.size();
|
|
for(size_t i=0; i<m; i++)
|
|
if(vec1[i] != vec2[i])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool greaterThanOrEqual(const Vector& vec1, const Vector& vec2) {
|
|
size_t m = vec1.size();
|
|
for(size_t i=0; i<m; i++)
|
|
if(!(vec1[i] >= vec2[i]))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool equal_with_abs_tol(const Vector& vec1, const Vector& vec2, double tol) {
|
|
if (vec1.size()!=vec2.size()) return false;
|
|
size_t m = vec1.size();
|
|
for(size_t i=0; i<m; ++i) {
|
|
if (!fpEqual(vec1[i], vec2[i], tol))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool equal_with_abs_tol(const SubVector& vec1, const SubVector& vec2, double tol) {
|
|
if (vec1.size()!=vec2.size()) return false;
|
|
size_t m = vec1.size();
|
|
for(size_t i=0; i<m; ++i) {
|
|
if (!fpEqual(vec1[i], vec2[i], tol))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool assert_equal(const Vector& expected, const Vector& actual, double tol) {
|
|
if (equal_with_abs_tol(expected,actual,tol)) return true;
|
|
cout << "not equal:" << endl;
|
|
print(expected, "expected");
|
|
print(actual, "actual");
|
|
return false;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool assert_inequal(const Vector& expected, const Vector& actual, double tol) {
|
|
if (!equal_with_abs_tol(expected,actual,tol)) return true;
|
|
cout << "Erroneously equal:" << endl;
|
|
print(expected, "expected");
|
|
print(actual, "actual");
|
|
return false;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool assert_equal(const SubVector& expected, const SubVector& actual, double tol) {
|
|
if (equal_with_abs_tol(expected,actual,tol)) return true;
|
|
cout << "not equal:" << endl;
|
|
print(static_cast<Vector>(expected), "expected");
|
|
print(static_cast<Vector>(actual), "actual");
|
|
return false;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool assert_equal(const ConstSubVector& expected, const ConstSubVector& actual, double tol) {
|
|
if (equal_with_abs_tol(Vector(expected),Vector(actual),tol)) return true;
|
|
cout << "not equal:" << endl;
|
|
print(Vector(expected), "expected");
|
|
print(Vector(actual), "actual");
|
|
return false;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
bool linear_dependent(const Vector& vec1, const Vector& vec2, double tol) {
|
|
if (vec1.size()!=vec2.size()) return false;
|
|
bool flag = false; double scale = 1.0;
|
|
size_t m = vec1.size();
|
|
for(size_t i=0; i<m; i++) {
|
|
if((std::abs(vec1[i])>tol && std::abs(vec2[i])<tol) || (std::abs(vec1[i])<tol && std::abs(vec2[i])>tol))
|
|
return false;
|
|
if(vec1[i] == 0 && vec2[i] == 0) continue;
|
|
if (!flag) {
|
|
scale = vec1[i] / vec2[i];
|
|
flag = true ;
|
|
}
|
|
else if (std::abs(vec1[i] - vec2[i]*scale) > tol) return false;
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
Vector ediv_(const Vector &a, const Vector &b) {
|
|
size_t n = a.size();
|
|
assert (b.size()==a.size());
|
|
Vector c(n);
|
|
for( size_t i = 0; i < n; i++ ) {
|
|
const double &ai = a(i), &bi = b(i);
|
|
c(i) = (bi==0.0 && ai==0.0) ? 0.0 : ai/bi;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
// imperative version, pass in x
|
|
double houseInPlace(Vector &v) {
|
|
const double x0 = v(0);
|
|
const double x02 = x0*x0;
|
|
|
|
// old code - GSL verison was actually a bit slower
|
|
const double sigma = v.squaredNorm() - x02;
|
|
|
|
v(0) = 1.0;
|
|
|
|
if( sigma == 0.0 )
|
|
return 0.0;
|
|
else {
|
|
double mu = sqrt(x02 + sigma);
|
|
if( x0 <= 0.0 )
|
|
v(0) = x0 - mu;
|
|
else
|
|
v(0) = -sigma / (x0 + mu);
|
|
|
|
const double v02 = v(0)*v(0);
|
|
v = v / v(0);
|
|
return 2.0 * v02 / (sigma + v02);
|
|
}
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
pair<double, Vector > house(const Vector &x) {
|
|
Vector v(x);
|
|
double beta = houseInPlace(v);
|
|
return make_pair(beta, v);
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
// Fast version *no error checking* !
|
|
// Pass in initialized vector of size m or will crash !
|
|
double weightedPseudoinverse(const Vector& a, const Vector& weights,
|
|
Vector& pseudo) {
|
|
|
|
size_t m = weights.size();
|
|
static const double inf = std::numeric_limits<double>::infinity();
|
|
|
|
// Check once for zero entries of a. TODO: really needed ?
|
|
vector<bool> isZero;
|
|
for (size_t i = 0; i < m; ++i) isZero.push_back(std::abs(a[i]) < 1e-9);
|
|
|
|
// If there is a valid (a!=0) constraint (sigma==0) return the first one
|
|
for (size_t i = 0; i < m; ++i) {
|
|
if (weights[i] == inf && !isZero[i]) {
|
|
// Basically, instead of doing a normal QR step with the weighted
|
|
// pseudoinverse, we enforce the constraint by turning
|
|
// ax + AS = b into x + (A/a)S = b/a, for the first row where a!=0
|
|
pseudo = Vector::Unit(m,i)*(1.0/a[i]);
|
|
return inf;
|
|
}
|
|
}
|
|
|
|
// Form psuedo-inverse inv(a'inv(Sigma)a)a'inv(Sigma)
|
|
// For diagonal Sigma, inv(Sigma) = diag(precisions)
|
|
double precision = 0;
|
|
for (size_t i = 0; i < m; i++) {
|
|
double ai = a[i];
|
|
if (!isZero[i]) // also catches remaining sigma==0 rows
|
|
precision += weights[i] * (ai * ai);
|
|
}
|
|
|
|
// precision = a'inv(Sigma)a
|
|
if (precision < 1e-9)
|
|
for (size_t i = 0; i < m; i++)
|
|
pseudo[i] = 0;
|
|
else {
|
|
// emul(precisions,a)/precision
|
|
double variance = 1.0 / precision;
|
|
for (size_t i = 0; i < m; i++)
|
|
pseudo[i] = isZero[i] ? 0 : variance * weights[i] * a[i];
|
|
}
|
|
return precision; // sum of weights
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
// Slow version with error checking
|
|
pair<Vector, double>
|
|
weightedPseudoinverse(const Vector& a, const Vector& weights) {
|
|
DenseIndex m = weights.size();
|
|
if (a.size() != m)
|
|
throw invalid_argument("a and weights have different sizes!");
|
|
Vector pseudo(m);
|
|
double precision = weightedPseudoinverse(a, weights, pseudo);
|
|
return make_pair(pseudo, precision);
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
Vector concatVectors(const std::list<Vector>& vs)
|
|
{
|
|
size_t dim = 0;
|
|
for(Vector v: vs)
|
|
dim += v.size();
|
|
|
|
Vector A(dim);
|
|
size_t index = 0;
|
|
for(Vector v: vs) {
|
|
for(int d = 0; d < v.size(); d++)
|
|
A(d+index) = v(d);
|
|
index += v.size();
|
|
}
|
|
|
|
return A;
|
|
}
|
|
|
|
/* ************************************************************************* */
|
|
Vector concatVectors(size_t nrVectors, ...)
|
|
{
|
|
va_list ap;
|
|
list<Vector> vs;
|
|
va_start(ap, nrVectors);
|
|
for( size_t i = 0 ; i < nrVectors ; i++) {
|
|
Vector* V = va_arg(ap, Vector*);
|
|
vs.push_back(*V);
|
|
}
|
|
va_end(ap);
|
|
return concatVectors(vs);
|
|
}
|
|
|
|
} // namespace gtsam
|