📄 test_data.qbk
字号:
std::cout << "Welcome.\n" "This program will generate spot tests for the beta function:\n" " beta(a, b)\n\n"; bool cont; std::string line; do{ // prompt the user for the domain of a and b to test: get_user_parameter_info(arg1, "a"); get_user_parameter_info(arg2, "b"); // create the data: data.insert(beta_data_generator(), arg1, arg2); // see if the user want's any more domains tested: std::cout << "Any more data [y/n]?"; std::getline(std::cin, line); boost::algorithm::trim(line); cont = (line == "y"); }while(cont);[caution At this point one potential stumbling block should be mentioned:test_data<>::insert will create a matrix of test data when there are twoor more parameters, so if we have two parameters and we're asked fora thousand points on each, that's a ['million test points in total].Don't say you weren't warned!]There's just one final step now, and that's to write the test data to file: std::cout << "Enter name of test data file [default=beta_data.ipp]"; std::getline(std::cin, line); boost::algorithm::trim(line); if(line == "") line = "beta_data.ipp"; std::ofstream ofs(line.c_str()); write_code(ofs, data, "beta_data");The format of the test data looks something like: #define SC_(x) static_cast<T>(BOOST_JOIN(x, L)) static const boost::array<boost::array<T, 3>, 1830> beta_med_data = { SC_(0.4883005917072296142578125), SC_(0.4883005917072296142578125), SC_(3.245912809500479157065104747353807392371), SC_(3.5808107852935791015625), SC_(0.4883005917072296142578125), SC_(1.007653173802923954909901438393379243537), /* ... lots of rows skipped */ };The first two values in each row are the input parameters that were passedto our functor and the last value is the return value from the functor.Had our functor returned a tuple rather than a value, then we would have hadone entry for each element in the tuple in addition to the input parameters.The first #define serves two purposes:* It reduces the file sizes considerably: all those `static_cast`'s add up to a lotof bytes otherwise (they are needed to suppress compiler warnings when `T` is narrower than a `long double`).* It provides a useful customisation point: for example if we were testinga user-defined type that has more precision than a `long double` we could changeit to:[^#define SC_(x) lexical_cast<T>(BOOST_STRINGIZE(x))] in order to ensure that no truncation of the values occurs prior to conversionto `T`. Note that this isn't used by default as it's rather hard on the compilerwhen the table is large.[h5 Example 3: Profiling a Continued Fraction for Convergence and Accuracy]Alternatively, lets say we want to profile a continued fraction for convergence and error. As an example, we'll use the continued fractionfor the upper incomplete gamma function, the following function objectreturns the next a[sub N ] and b[sub N ] of the continued fractioneach time it's invoked: template <class T> struct upper_incomplete_gamma_fract { private: T z, a; int k; public: typedef std::pair<T,T> result_type; upper_incomplete_gamma_fract(T a1, T z1) : z(z1-a1+1), a(a1), k(0) { } result_type operator()() { ++k; z += 2; return result_type(k * (a - k), z); } };We want to measure both the relative error, and the rate of convergenceof this fraction, so we'll write a functor that returns both as a tuple:class test_data will unpack the tuple for us, and create one column of datafor each element in the tuple (in addition to the input parameters): #include <boost/math/tools/test_data.hpp> #include <boost/math/tools/test.hpp> #include <boost/math/special_functions/gamma.hpp> #include <boost/math/tools/ntl.hpp> #include <boost/tr1/tuple.hpp> template <class T> struct profile_gamma_fraction { typedef std::tr1::tuple<T, T> result_type; result_type operator()(T val) { using namespace boost::math::tools; // estimate the true value, using arbitary precision // arithmetic and NTL::RR: NTL::RR rval(val); upper_incomplete_gamma_fract<NTL::RR> f1(rval, rval); NTL::RR true_val = continued_fraction_a(f1, 1000); // // Now get the aproximation at double precision, along with the number of // iterations required: boost::uintmax_t iters = std::numeric_limits<boost::uintmax_t>::max(); upper_incomplete_gamma_fract<T> f2(val, val); T found_val = continued_fraction_a(f2, std::numeric_limits<T>::digits, iters); // // Work out the relative error, as measured in units of epsilon: T err = real_cast<T>(relative_error(true_val, NTL::RR(found_val)) / std::numeric_limits<T>::epsilon()); // // now just return the results as a tuple: return std::tr1::make_tuple(err, iters); } };Feeding that functor into test_data allows rapid output of csv data,for whatever type `T` we may be interested in: 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 0 and 100: data.insert(profile_gamma_fraction<double>(), make_periodic_param(0.01, 100.0, 100)); // print out in csv format: write_csv(std::cout, data, ", "); return 0; }This time there's no need to plot a graph, the first few rows are: a and z, Error/epsilon, Iterations required 0.01, 9723.14, 4726 1.0099, 9.54818, 87 2.0098, 3.84777, 40 3.0097, 0.728358, 25 4.0096, 2.39712, 21 5.0095, 0.233263, 16So it's pretty clear that this fraction shouldn't be used for small valuesof a and z.[h4 reference][#test_data_reference]Most of this tool has been described already in the examples above, we'll just add the following notes on the non-member functions: template <class T> parameter_info<T> make_random_param(T start_range, T end_range, int n_points); Tells class test_data to test /n_points/ random values in the range [start_range,end_range]. template <class T> parameter_info<T> make_periodic_param(T start_range, T end_range, int n_points); Tells class test_data to test /n_points/ evenly spaced values in the range [start_range,end_range]. template <class T> parameter_info<T> make_power_param(T basis, int start_exponent, int end_exponent);Tells class test_data to test points of the form ['basis + R * 2[super expon]] for each/expon/ in the range [start_exponent, end_exponent], and /R/ a random number in \[0.5, 1\]. template <class T> bool get_user_parameter_info(parameter_info<T>& info, const char* param_name); Prompts the user for the parameter range and form to use.Finally, if we don't want the parameter to be included in the output, we can telltest_data by setting it a "dummy parameter": parameter_info<double> p = make_random_param(2.0, 5.0, 10); p.type |= dummy_param; This is useful when the functor used transforms the parameter in some waybefore passing it to the function under test, usually the functor will thenreturn both the transformed input and the result in a tuple, so there's noneed for the original pseudo-parameter to be included in program output.[endsect][/section:test_data Graphing, Profiling, and Generating Test Data for Special Functions][/ Copyright 2006 John Maddock and Paul A. Bristow. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt).]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -