error testing stuff

This commit is contained in:
Bill March
2008-04-20 18:21:42 +00:00
parent 22169f89d6
commit f7fb517061
5 changed files with 844 additions and 0 deletions
@@ -0,0 +1,70 @@
#include "hybrid_error.h"
#include "hybrid_error_stat.h"
#include "hybrid_error_analysis.h"
#include "naive_kernel_sum.h"
#include "fastlib/fastlib.h"
int main(int argc, char* argv[]) {
fx_init(argc, argv);
GaussianKernelErrorTester<AbsoluteErrorStat> absolute;
GaussianKernelErrorTester<RelativeErrorStat> relative;
GaussianKernelErrorTester<ExponentialErrorStat> exponential;
GaussianKernelErrorTester<GaussianErrorStat> gaussian;
GaussianKernelErrorTester<HybridErrorStat> hybrid;
Matrix centers;
const char* dataset = fx_param_str(NULL, "data", "test_data.csv");
data::Load(dataset, &centers);
double bandwidth = fx_param_double(NULL, "bandwidth", 0.1);
DEBUG_ASSERT(bandwidth > 0.0);
Vector abs_results;
struct datanode* abs_mod = fx_submodule(NULL, "abs", "absolute");
absolute.Init(abs_mod, centers, bandwidth);
absolute.ComputeTotalSum(&abs_results);
Vector rel_results;
struct datanode* rel_mod = fx_submodule(NULL, "rel", "relative");
relative.Init(rel_mod, centers, bandwidth);
relative.ComputeTotalSum(&rel_results);
Vector exp_results;
struct datanode* exp_mod = fx_submodule(NULL, "exp", "exponential");
exponential.Init(exp_mod, centers, bandwidth);
exponential.ComputeTotalSum(&exp_results);
Vector gauss_results;
struct datanode* gauss_mod = fx_submodule(NULL, "gauss", "gaussian");
gaussian.Init(gauss_mod, centers, bandwidth);
gaussian.ComputeTotalSum(&gauss_results);
Vector hybrid_results;
struct datanode* hybrid_mod = fx_submodule(NULL, "hybrid", "hybrid");
hybrid.Init(hybrid_mod, centers, bandwidth);
hybrid.ComputeTotalSum(&hybrid_results);
NaiveKernelSum naive;
Vector naive_results;
struct datanode* naive_mod = fx_submodule(NULL, "naive", "naive");
naive.Init(naive_mod, centers, bandwidth);
naive.ComputeTotalSum(&naive_results);
ErrorAnalysis analysis;
analysis.Init(abs_results, rel_results, exp_results, gauss_results,
hybrid_results, naive_results, abs_mod, rel_mod, exp_mod,
gauss_mod, hybrid_mod, naive_mod);
analysis.ComputeResults();
fx_done();
return 0;
}
+258
View File
@@ -0,0 +1,258 @@
/**
* @file hybrid_error.h
*
* @author Bill March (march@gatech.edu)
*
* Contains a class for running timing tests for hybrid error schemes.
*/
#ifndef HYBRID_ERROR_H
#define HYBRID_ERROR_H
#include "fastlib/fastlib.h"
/*
* I need a templated error class. It needs to:
* - return the correct error bound - relative, absolute, hybrid
* - Be able to inform the tree building so I get the right stats for each kind
* of error
*
* It might be possible to just templatize with the Stat class. It can have a
* function to compute the allowed error
*
* TODO: write a naive version too, to evaluate the approximations against
*/
/**
* Dual-tree implementation of a Gaussian kernel summation with monopole
* error bounds.
*
* TODO: I think this should be templatized to allow for different error styles
* I could templatize with the stat class, which has a function to determine if
* pruning is possible.
*/
template <typename TErrorStat>
class GaussianKernelErrorTester {
//FORBID_ACCIDENTAL_COPY(GaussianKernelErrorTester);
public:
GaussianKernelErrorTester() {}
~GaussianKernelErrorTester() {}
typedef BinarySpaceTree<DHrectBound<2>, Matrix, TErrorStat> ErrorTree;
private:
// The common, global bandwidth of the Gaussians
double bandwidth_;
// The centers of the Gaussians
Matrix centers_;
// The result of the sum
Vector results_;
// The fx module for timing
struct datanode* module_;
// The total number of points
index_t num_points_;
// The dimension
index_t dimension_;
ErrorTree* tree_;
ArrayList<index_t> old_from_new_;
/**
* Computes the value of the gaussian centered at r at the point referred to
* by q.
*
* I don't think I'll worry about normalization for now.
*/
double ComputeGaussian_(index_t q, index_t r) {
Vector q_vec;
centers_.MakeColumnVector(q, &q_vec);
Vector r_vec;
centers_.MakeColumnVector(r, &r_vec);
double dist = la::DistanceSqEuclidean(q_vec, r_vec);
return(exp(-bandwidth_ * dist));
} // ComputeGaussian_()
double ComputeGaussian_(double dist_sq) {
return (exp(-bandwidth_ * dist_sq));
} // ComputeGaussian_
// This function needs to decrease the stat's query_count, since I don't need
// to allocate any more error to those query points
void ComputeSumBaseCase_(ErrorTree* query, ErrorTree* reference) {
for (index_t query_index = query->begin(); query_index < query->end();
query_index++) {
double query_value = results_[query_index];
for (index_t ref_index = reference->begin(); ref_index < reference->end();
ref_index++) {
query_value = query_value + ComputeGaussian_(query_index, ref_index);
}
DEBUG_ASSERT(query_value >= 0.0);
results_[query_index] = query_value;
}
index_t query_count = query->stat().query_count();
index_t reference_count = reference->count();
query->stat().set_query_count(query_count - reference_count);
} // ComputeSumBaseCase_()
void ComputeSumRecursion_(ErrorTree* query, ErrorTree* reference) {
double q_min_dist = query->bound().MinDistanceSq(reference->bound());
double q_max_dist = query->bound().MaxDistanceSq(reference->bound());
double q_upper_bound = ComputeGaussian_(q_min_dist) * reference->count();
double q_lower_bound = ComputeGaussian_(q_max_dist) * reference->count();
if (query->is_leaf() && reference->is_leaf()) {
ComputeSumBaseCase_(query, reference);
} // Base case
// Maybe I should put the prune check before the base case
else if(query->stat().CanPrune(
q_upper_bound, q_lower_bound, reference->count())) {
double approximate_result = 0.5 * (q_upper_bound + q_lower_bound);
DEBUG_ASSERT(approximate_result >= 0.0);
Vector subvec;
results_.MakeSubvector(query->begin(), query->count(), &subvec);
Vector approx;
approx.Init(query->count());
approx.SetAll(approximate_result);
// I'm pretty sure this will work, but I should check
la::AddOverwrite(subvec, approx, &subvec);
} // Pruning case
else if(query->is_leaf()) {
ComputeSumRecursion_(query, reference->left());
ComputeSumRecursion_(query, reference->right());
} // only split references
else if(reference->is_leaf()) {
query->left()->stat().set_query_count(query->stat().query_count());
query->right()->stat().set_query_count(query->stat().query_count());
ComputeSumRecursion_(query->left(), reference);
ComputeSumRecursion_(query->right(), reference);
index_t left_count = query->left()->stat().query_count();
// This gives an unused variable warning in fast mode
index_t right_count = query->right()->stat().query_count();
DEBUG_ASSERT(left_count == right_count);
query->stat().set_query_count(left_count);
} // only split queries
else {
// Should consider some kind of priority here
// which side could use the error more effectively?
query->left()->stat().set_query_count(query->stat().query_count());
query->right()->stat().set_query_count(query->stat().query_count());
ComputeSumRecursion_(query->left(), reference->left());
ComputeSumRecursion_(query->left(), reference->right());
ComputeSumRecursion_(query->right(), reference->left());
ComputeSumRecursion_(query->right(), reference->right());
index_t left_count = query->left()->stat().query_count();
index_t right_count = query->right()->stat().query_count();
DEBUG_ASSERT(left_count == right_count);
query->stat().set_query_count(left_count);
} // four-way
} // ComputeSumRecursion_
public:
void InitStats(ErrorTree* node) {
if (!(node->is_leaf())) {
InitStats(node->left());
InitStats(node->right());
}
node->stat().SetParams(module_);
} // InitStats()
void Init(struct datanode* mod, const Matrix& cent, double band) {
module_ = mod;
centers_.Copy(cent);
bandwidth_ = band;
DEBUG_ASSERT(bandwidth_ > 0.0);
num_points_ = centers_.n_cols();
dimension_ = centers_.n_rows();
results_.Init(num_points_);
results_.SetZero();
tree_ = tree::MakeKdTreeMidpoint<ErrorTree>(centers_,
fx_param_int(mod, "leaf_size", 20), &old_from_new_, NULL);
tree_->stat().set_query_count(num_points_);
InitStats(tree_);
} // Init()
void ComputeTotalSum(Vector* results_vec) {
fx_timer_start(module_, "timer");
ComputeSumRecursion_(tree_, tree_);
fx_timer_stop(module_, "timer");
results_vec->Copy(results_);
} // ComputeTotalSum()
}; // class GaussianKernelErrorTester
#endif
@@ -0,0 +1,122 @@
#ifndef HYBRID_ERROR_ANALYSIS_H
#define HYBRID_ERROR_ANALYSIS_H
#include "fastlib/fastlib.h"
class ErrorAnalysis {
private:
Vector abs_vec;
Vector rel_vec;
Vector exp_vec;
Vector gauss_vec;
Vector hybrid_vec;
Vector naive_vec;
struct datanode* abs_mod_;
struct datanode* rel_mod_;
struct datanode* exp_mod_;
struct datanode* gauss_mod_;
struct datanode* hybrid_mod_;
struct datanode* naive_mod_;
index_t num_points_;
void TotalError_() {
double abs_error = 0.0;
double rel_error = 0.0;
double exp_error = 0.0;
double gauss_error = 0.0;
double hybrid_error = 0.0;
double abs_rel = 0.0;
double rel_rel = 0.0;
double exp_rel = 0.0;
double gauss_rel = 0.0;
double hybrid_rel = 0.0;
for (index_t i = 0; i < num_points_; i++) {
double naive_val = naive_vec[i];
double this_abs = fabs(naive_val - abs_vec[i]);
double this_rel = fabs(naive_val - rel_vec[i]);
double this_exp = fabs(naive_val - exp_vec[i]);
double this_gauss = fabs(naive_val - gauss_vec[i]);
double this_hybrid = fabs(naive_val - hybrid_vec[i]);
abs_error = abs_error + this_abs;
rel_error = rel_error + this_rel;
exp_error = exp_error + this_exp;
gauss_error = gauss_error + this_gauss;
hybrid_error = hybrid_error + this_hybrid;
abs_rel = abs_rel + (this_abs/naive_val);
rel_rel = rel_rel + (this_rel/naive_val);
exp_rel = exp_rel + (this_exp/naive_val);
gauss_rel = gauss_rel + (this_gauss/naive_val);
hybrid_rel = hybrid_rel + (this_gauss/naive_val);
} // i
fx_format_result(abs_mod_, "total_absolute_error", "%g", abs_error);
fx_format_result(rel_mod_, "total_absolute_error", "%g", rel_error);
fx_format_result(exp_mod_, "total_absolute_error", "%g", exp_error);
fx_format_result(gauss_mod_, "total_absolute_error", "%g", gauss_error);
fx_format_result(hybrid_mod_, "total_absolute_error", "%g", hybrid_error);
fx_format_result(abs_mod_, "total_relative_error", "%g", abs_rel);
fx_format_result(rel_mod_, "total_relative_error", "%g", rel_rel);
fx_format_result(exp_mod_, "total_relative_error", "%g", exp_rel);
fx_format_result(gauss_mod_, "total_relative_error", "%g", gauss_rel);
fx_format_result(hybrid_mod_, "total_relative_error", "%g", hybrid_rel);
} // TotalAbsoluteError_()
public:
ErrorAnalysis() {}
~ErrorAnalysis() {}
void Init(const Vector& abs, const Vector& rel, const Vector& exp,
const Vector& gauss, const Vector& hybrid, const Vector& naive,
struct datanode* abs_m, struct datanode* rel_m,
struct datanode* exp_m, struct datanode* gauss_m,
struct datanode* hybrid_m, struct datanode* naive_m) {
abs_vec.Copy(abs);
rel_vec.Copy(rel);
exp_vec.Copy(exp);
gauss_vec.Copy(gauss);
hybrid_vec.Copy(hybrid);
naive_vec.Copy(naive);
num_points_ = abs_vec.length();
abs_mod_ = abs_m;
rel_mod_ = rel_m;
exp_mod_ = exp_m;
gauss_mod_ = gauss_m;
hybrid_mod_ = hybrid_m;
naive_mod_ = naive_m;
} // Init()
void ComputeResults() {
TotalError_();
} // ComputeResults()
}; // class ErrorAnalysis
#endif
@@ -0,0 +1,302 @@
/**
* @file hybrid_error_stat.h
*
* @author Bill March (march@gatech.edu)
*
* Defines the stat classes for the different kinds of error
*/
#ifndef HYBRID_ERROR_STAT_H
#define HYBRID_ERROR_STAT_H
// I should make all of these inherit the basic stat stuff
// The classes should be able to overlap entirely except for the Epsilon_
// function and some private variables for the complicated functions
class GenericErrorStat {
protected:
index_t query_count_;
double epsilon_;
virtual double Epsilon_(double upper_bound, double lower_bound) = 0;
public:
GenericErrorStat() {}
virtual ~GenericErrorStat() {}
void Init(const Matrix& matrix, index_t start, index_t count) {
query_count_ = count;
} // Init() (leaves)
void Init(const Matrix& matrix, index_t start, index_t count,
const GenericErrorStat& left, const GenericErrorStat& right) {
query_count_ = count;
} // Init() (non-leaves)
bool CanPrune(double q_upper_bound, double q_lower_bound,
index_t reference_count) {
bool prune = false;
double max_error_incurred = 0.5 * (q_upper_bound - q_lower_bound);
DEBUG_ASSERT(max_error_incurred >= 0.0);
double allowed_error = q_lower_bound *
Epsilon_(q_upper_bound, q_lower_bound) * reference_count / query_count_;
DEBUG_ASSERT(allowed_error >= 0.0);
if (max_error_incurred < allowed_error) {
prune = true;
epsilon_ = epsilon_ - max_error_incurred;
query_count_ = query_count_ - reference_count;
DEBUG_ASSERT(query_count_ >= 0.0);
}
return prune;
} // CanPrune()
void set_query_count(index_t new_count) {
query_count_ = new_count;
DEBUG_ASSERT(query_count_ >= 0);
} // set_query_count()
index_t query_count() {
return query_count_;
} // query_count()
}; // GenericErrorStat
/**
* Prunes with an absolute error criterion
*/
class AbsoluteErrorStat : public GenericErrorStat {
protected:
/**
* Returns the error tolerance as a function of the bounds on Q. In this case
* we divide by the lower bound to get absolute error.
*/
double Epsilon_(double upper_bound, double lower_bound) {
double eps = epsilon_ / lower_bound;
DEBUG_ASSERT(eps >= 0.0);
return (eps);
} // Epsilon_
public:
AbsoluteErrorStat() {}
~AbsoluteErrorStat() {}
void SetParams(struct datanode* mod) {
epsilon_ = fx_param_double_req(mod, "epsilon");
} // SetParams()
}; // class AbsoluteErrorStat
/**
* Prunes with a relative error criterion
*/
class RelativeErrorStat : public GenericErrorStat {
private:
index_t query_count_;
protected:
/**
* Relative error just depends on epsilon_
*/
double Epsilon_(double upper_bound, double lower_bound) {
DEBUG_ASSERT(epsilon_ >= 0.0);
return epsilon_;
} // Epsilon_
public:
RelativeErrorStat() {}
~RelativeErrorStat() {}
void SetParams(struct datanode* mod) {
epsilon_ = fx_param_double_req(mod, "epsilon");
DEBUG_ASSERT(epsilon_ >= 0.0);
} // SetParams
}; // class RelativeErrorStat
/**
* Prunes with the hybrid exponential error criterion
*/
class ExponentialErrorStat : public GenericErrorStat {
private:
double max_error_;
double steepness_;
double min_error_;
protected:
/**
* Hybrid error using the exponential criterion
*/
double Epsilon_(double upper_bound, double lower_bound) {
double eps = (max_error_ * exp(-steepness_ * upper_bound)) + min_error_ +
epsilon_;
DEBUG_ASSERT(eps >= 0.0);
return (eps);
} // Epsilon_()
public:
ExponentialErrorStat() {}
~ExponentialErrorStat() {}
void SetParams(struct datanode* mod) {
max_error_ = fx_param_double_req(mod, "max_error");
steepness_ = fx_param_double_req(mod, "steepness");
min_error_ = fx_param_double_req(mod, "min_error");
epsilon_ = 0.0;
} // SetParams()
}; // class ExponentialErrorStat
/**
* Uses a Gaussian hybrid error criterion
*/
class GaussianErrorStat : public GenericErrorStat {
private:
double max_error_;
double steepness_;
double min_error_;
protected:
/**
* Hybrid error using the gaussian criterion
*/
double Epsilon_(double upper_bound, double lower_bound) {
double eps = (max_error_ * exp(-steepness_ * upper_bound * upper_bound))
+ min_error_ + epsilon_;
DEBUG_ASSERT(eps >= 0.0);
return (eps);
} // Epsilon_()
public:
GaussianErrorStat() {}
~GaussianErrorStat() {}
void SetParams(struct datanode* mod) {
max_error_ = fx_param_double_req(mod, "max_error");
steepness_ = fx_param_double_req(mod, "steepness");
min_error_ = fx_param_double_req(mod, "min_error");
epsilon_ = 0.0;
} // SetParams
}; // class GaussianErrorStat
class HybridErrorStat : public GenericErrorStat {
private:
double steepness_;
protected:
double Epsilon_(double upper_bound, double lower_bound) {
double eps = (1 - exp(-steepness_ * lower_bound)) * epsilon_;
eps = eps + (exp(-steepness_ * upper_bound) * epsilon_ / lower_bound);
DEBUG_ASSERT(eps >= 0.0);
return eps;
} // Epsilon_()
public:
HybridErrorStat() {}
~HybridErrorStat() {}
void SetParams(struct datanode* mod) {
steepness_ = fx_param_double_req(mod, "steepness");
epsilon_ = fx_param_double_req(mod, "epsilon");
} // SetParams()
}; // class HybridErrorStat
#endif
@@ -0,0 +1,92 @@
#ifndef NAIVE_KERNEL_SUM_H
#define NAIVE_KERNEL_SUM_H
#include "fastlib/fastlib.h"
class NaiveKernelSum {
private:
Vector results_;
Matrix centers_;
double bandwidth_;
index_t num_points_;
index_t dimension_;
struct datanode* module_;
double ComputeGaussian_(index_t i, index_t j) {
Vector i_vec;
centers_.MakeColumnVector(i, &i_vec);
Vector j_vec;
centers_.MakeColumnVector(j, &j_vec);
double dist_sq = la::DistanceSqEuclidean(i_vec, j_vec);
return (exp(-bandwidth_ * dist_sq));
} // ComputeGaussian_()
public:
NaiveKernelSum() {}
~NaiveKernelSum() {}
void Init(struct datanode* mod, const Matrix& cent, double band) {
centers_.Copy(cent);
bandwidth_ = band;
DEBUG_ASSERT(bandwidth_ > 0.0);
num_points_ = centers_.n_cols();
dimension_ = centers_.n_rows();
results_.Init(num_points_);
results_.SetZero();
module_ = mod;
} // Init()
void ComputeTotalSum(Vector* return_results) {
fx_timer_start(module_, "timing");
for (index_t i = 0; i < num_points_; i++) {
double this_result = results_[i];
for (index_t j = i; j < num_points_; j++) { // for symmetry
double this_kernel = ComputeGaussian_(i, j);
this_result = this_result + this_kernel;
results_[j] = results_[j] + this_kernel; // take advantage of symmetry
} // j
results_[i] = this_result;
} // i
fx_timer_stop(module_, "timing");
return_results->Copy(results_);
} // ComputeTotalSum()
}; // NaiveKernelSum
#endif