Files
mlpack/fastlib/trunk/contrib/march/error/hybrid_error.h
T

311 lines
8.2 KiB
C++

/**
* @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_;
double max_error_;
double min_error_;
double steepness_;
index_t num_prunes_;
/**
* 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) {
double min_query_val = DBL_MAX;
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;
if (query_value < min_query_val) {
min_query_val = query_value;
}
}
query->stat().set_query_lower_bound(min_query_val);
/*index_t remainder = query->stat().remaining_references();
index_t reference_count = reference->count();
DEBUG_ASSERT(remainder - reference_count >= 0);
query->stat().set_remaining_references(remainder - 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->stat().CanPrune(
q_upper_bound, q_lower_bound, reference->count())) {
num_prunes_++;
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);
la::AddTo(approx, &subvec);
query->stat().set_query_lower_bound(
query->stat().query_lower_bound() + approximate_result);
} // Pruning case
else if (query->is_leaf() && reference->is_leaf()) {
ComputeSumBaseCase_(query, reference);
} // Base case
else if(query->is_leaf()) {
ComputeSumRecursion_(query, reference->left());
ComputeSumRecursion_(query, reference->right());
} // only split references
else if(reference->is_leaf()) {
// How to propagate lower bound down the tree?
// Will try parent's bound
// it's loose on one (both?) children, but I don't have a better idea
query->left()->stat().set_query_lower_bound(
query->stat().query_lower_bound());
query->right()->stat().set_query_lower_bound(
query->stat().query_lower_bound());
ComputeSumRecursion_(query->left(), reference);
ComputeSumRecursion_(query->right(), reference);
query->stat().set_query_lower_bound(
min(query->left()->stat().query_lower_bound(),
query->right()->stat().query_lower_bound()));
DEBUG_ASSERT(query->stat().query_lower_bound() >= 0.0);
} // only split queries
else {
query->left()->stat().set_query_lower_bound(
query->stat().query_lower_bound());
query->right()->stat().set_query_lower_bound(
query->stat().query_lower_bound());
ComputeSumRecursion_(query->left(), reference->left());
ComputeSumRecursion_(query->left(), reference->right());
ComputeSumRecursion_(query->right(), reference->left());
ComputeSumRecursion_(query->right(), reference->right());
query->stat().set_query_lower_bound(
min(query->left()->stat().query_lower_bound(),
query->right()->stat().query_lower_bound()));
DEBUG_ASSERT(query->stat().query_lower_bound() >= 0.0);
} // four-way
} // ComputeSumRecursion_
public:
void InitStats(ErrorTree* node) {
if (!(node->is_leaf())) {
InitStats(node->left());
InitStats(node->right());
}
node->stat().SetParams(max_error_, min_error_, steepness_);
node->stat().set_remaining_references(num_points_);
// This is only true for these unit coefficient Gaussians
//node->stat().set_query_upper_bound(num_points_);
node->stat().set_query_lower_bound(0.0);
} // InitStats()
void Init(struct datanode* mod, const Matrix& cent, double band,
double max_err, double min_err, double steep) {
module_ = mod;
centers_.Copy(cent);
bandwidth_ = band;
DEBUG_ASSERT(bandwidth_ > 0.0);
max_error_ = max_err;
DEBUG_ASSERT(max_error_ > 0.0);
min_error_ = min_err;
DEBUG_ASSERT(min_error_ > 0.0);
steepness_ = steep;
DEBUG_ASSERT(steepness_ > 0.0);
num_points_ = centers_.n_cols();
dimension_ = centers_.n_rows();
num_prunes_ = 0;
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_, "summation");
ComputeSumRecursion_(tree_, tree_);
fx_timer_stop(module_, "summation");
results_vec->Init(num_points_);
// unpermute the results for analysis
for (index_t i = 0; i < num_points_; i++) {
(*results_vec)[old_from_new_[i]] = results_[i];
} // i
// ot::Print(*results_vec);
fx_format_result(module_, "num_prunes", "%d", num_prunes_);
fx_format_result(module_, "num_points", "%d", num_points_);
} // ComputeTotalSum()
}; // class GaussianKernelErrorTester
#endif