📄 test_data.qbk
字号:
[section:test_data Graphing, Profiling, and Generating Test Data for Special Functions]The class `test_data` and associated helper functions are designed so that in justa few lines of code you should be able to:* Profile a continued fraction, or infinite series for convergence and accuracy.* Generate csv data from a special function that can be imported into your favoritegraphing program (or spreadsheet) for further analysis.* Generate high precision test data.[h4 Synopsis] namespace boost{ namespace math{ namespace tools{ enum parameter_type { random_in_range = 0, periodic_in_range = 1, power_series = 2, dummy_param = 0x80, }; template <class T> struct parameter_info; template <class T> parameter_info<T> make_random_param(T start_range, T end_range, int n_points); template <class T> parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points); template <class T> parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent); template <class T> bool get_user_parameter_info(parameter_info<T>& info, const char* param_name); template <class T> class test_data { public: typedef std::vector<T> row_type; typedef row_type value_type; private: typedef std::set<row_type> container_type; public: typedef typename container_type::reference reference; typedef typename container_type::const_reference const_reference; typedef typename container_type::iterator iterator; typedef typename container_type::const_iterator const_iterator; typedef typename container_type::difference_type difference_type; typedef typename container_type::size_type size_type; // creation: test_data(){} template <class F> test_data(F func, const parameter_info<T>& arg1); // insertion: template <class F> test_data& insert(F func, const parameter_info<T>& arg1); template <class F> test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2); template <class F> test_data& insert(F func, const parameter_info<T>& arg1, const parameter_info<T>& arg2, const parameter_info<T>& arg3); void clear(); // access: iterator begin(); iterator end(); const_iterator begin()const; const_iterator end()const; bool operator==(const test_data& d)const; bool operator!=(const test_data& d)const; void swap(test_data& other); size_type size()const; size_type max_size()const; bool empty()const; bool operator < (const test_data& dat)const; bool operator <= (const test_data& dat)const; bool operator > (const test_data& dat)const; bool operator >= (const test_data& dat)const; }; template <class charT, class traits, class T> std::basic_ostream<charT, traits>& write_csv( std::basic_ostream<charT, traits>& os, const test_data<T>& data); template <class charT, class traits, class T> std::basic_ostream<charT, traits>& write_csv( std::basic_ostream<charT, traits>& os, const test_data<T>& data, const charT* separator); template <class T> std::ostream& write_code(std::ostream& os, const test_data<T>& data, const char* name); }}} // namespaces [h4 Description]This tool is best illustrated with the following series of examples.The functionality of test_data is split into the following parts:* A functor that implements the function for which data is being generated:this is the bit you have to write.* One of more parameters that are to be passed to the functor, these aredescribed in fairly abstract terms: give me N points distributed like /this/ etc.* The class test_data, that takes the functor and descriptions of the parametersand computes how ever many output points have been requested, these are storedin a sorted container.* Routines to iterate over the test_data container and output the data in eithercsv format, or as C++ source code (as a table using Boost.Array).[h5 Example 1: Output Data for Graph Plotting]For example, lets say we want to graph the lgamma function between -3 and 100,one could do this like so: #include <boost/math/tools/test_data.hpp> #include <boost/math/special_functions/gamma.hpp> int main() { using namespace boost::math::tools; // create an object to hold the data: test_data<double> data; // insert 500 points at uniform intervals between just after -3 and 100: double (*pf)(double) = boost::math::lgamma; data.insert(pf, make_periodic_param(-3.0 + 0.00001, 100.0, 500)); // print out in csv format: write_csv(std::cout, data, ", "); return 0; } Which, when plotted, results in:[graph lgamma][h5 Example 2: Creating Test Data]As a second example, let's suppose we want to create highly accurate testdata for a special function. Since many special functions have two ormore independent parameters, it's very hard to effectively cover all ofthe possible parameter space without generating gigabytes of data atgreat computational expense. A second best approach is to provide the toolsby which a user (or the library maintainer) can quickly generate more dataon demand to probe the function over a particular domain of interest.In this example we'll generate test data for the beta function using[@http://shoup.net/ntl/doc/RR.txt NTL::RR] at 1000 bit precision.Rather than call our genericversion of the beta function, we'll implement a deliberately naive versionof the beta function using lgamma, and rely on the high precision of the data type used to get results accurate to at least 128-bit precision. In thisway our test data is independent of whatever clever tricks we may wish touse inside the our beta function.To start with then, here's the function object that creates the test data: #include <boost/math/tools/ntl.hpp> #include <boost/math/special_functions/gamma.hpp> #include <boost/math/tools/test_data.hpp> #include <fstream> #include <boost/math/tools/test_data.hpp> using namespace boost::math::tools; struct beta_data_generator { NTL::RR operator()(NTL::RR a, NTL::RR b) { // // If we throw a domain error then test_data will // ignore this input point. We'll use this to filter // out all cases where a < b since the beta function // is symmetrical in a and b: // if(a < b) throw std::domain_error(""); // very naively calculate spots with lgamma: NTL::RR g1, g2, g3; int s1, s2, s3; g1 = boost::math::lgamma(a, &s1); g2 = boost::math::lgamma(b, &s2); g3 = boost::math::lgamma(a+b, &s3); g1 += g2 - g3; g1 = exp(g1); g1 *= s1 * s2 * s3; return g1; } };To create the data, we'll need to input the domains for a and bfor which the function will be tested: the function `get_user_parameter_info`is designed for just that purpose. The start of main will look something like: // Set the precision on RR: NTL::RR::SetPrecision(1000); // bits. NTL::RR::SetOutputPrecision(40); // decimal digits. parameter_info<NTL::RR> arg1, arg2; test_data<NTL::RR> data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -