Re: [boost] math/statistics policy-interface feedback requested.

Paris (U.E.), le 19/06/2007 Bonjour Sorry for the delay, I had yet another emergency these last few days. The proposed interface is certainly nice and could definitely be worked with. For the global choices it is perfectly adequate, IMO. For finer control however, it does appear very fine-grained (per-function), which, in addition to making error message harder to read, is perhaps not the most frequent level of interaction needed. For instance, while I certainly see the need sometimes for per-function disabling of internal double promotion, I believe that more frequently users may want to set these preferences at the translation unit level, when not at the global level. While (mostly) global objects may be evil, something along the lines of standard streams and their interaction with locales and facets may perhaps be easier to deal with. For instance, functions may requests their choices from some external "policy object", which may be locally altered, much as streams will enquire from a locale how to typeset dates. Granted, this would make multithreading all the hairier (which is not really a good idea). On the other hand, not all functions necessarily require information about every configuration choice possible, so passing in all the info when only some is needed may perhaps be excessive. Merci Hubert Holin On 12 juin 2007, at 11:37, John Maddock wrote:
Following the successful review of the math-toolkit of special functions and statistical distributions the main feature request was for a better way of customising the library: both for choosing between precision vs speed tradeoffs, and for determining how errors are best handled. I've been experimenting with various policy-based interfaced based on Boost.Parameter, and I think I now have something useable, so I'd like to know what people think:
Policy Defaults ~~~~~~~~~~~~~~~
The library will use a sensible set of defaults (throw on domain errors and internal evaluation errors, favour accuracy over speed etc), which can be changed via the usual macro mechanism, so adding:
#define BOOST_MATH_DOMAIN_ERROR_POLICY errno_on_error #define BOOST_MATH_OVERFLOW_ERROR_POLICY ignor_error
to a user-config header would do what they say to the default policies.
Add Hock Changes: ~~~~~~~~~~~~~~~~~
We can create an ad-hock policy change at the call site by using the make_policy function, for example:
using namespace boost::math::policy; using namespace boost::math;
quantile( poisson(100), 0.05, make_policy( // 5 dicimal digits precision only digits10<10>(), // don't internally promote double->long double for extra precision: promote_double<false>(), // return integer result, immediately below the "real" value: discrete_quantile<integer_below>() ));
Which returns the lower 95% quantile (ie critical value) of a poisson distribution with rate parameter of 100 event's per unit time. The result is calculated using only 10 decimal digits internal precision, truncated to the largest integer that gives a CDF less than 0.05.
Predefined Policies ~~~~~~~~~~~~~~~~~~~
Although ad-hock policies are useful for testing, I imagine most sites would want a few carefully controlled (and tested) policies. To achieve that you define a typedef of the policy class:
using namespace boost::math::policy; typedef policy< // Set error handling: domain_error<throw_on_error>, pole_error<throw_on_error>, overflow_error<throw_on_error>, evaluation_error<throw_on_error>, denorm_error<ignor_error>, underflow_error<ignor_error>, // calculate to 8 decimal digits internally digits10<8>, // don't promote double->long double for accuracy promote_double<false>, // Integer quantiles return the "outside edge": // below the real value for lower critical values, // above it for upper critical values, so that the // area inside contains *at least* the requested coverage: discrete_quantile<integer_outside_edge>
fast_quantile_policy;
static fast_quantile_policy fast_quantile;
Then we can just use:
quantile( poisson(100), 0.05, fast_quantile);
In our actual code.
Currently this policy interface is vapourware: I have enough of a prototype implemented to know that it's possible to achieve this syntax (this is revision #3 already !), but there's a lot of hairy meta-programming to convert that into something that the library's internals can make use of... so I'd like to know what folks think before I invest too much time messing about with MPL :-)
The main disadvantage I've noticed at present, is that the mangled names of the policy class - and therefore all the special functions etc - are *very* long. This has an impact on error messages: in particular we currently use BOOST_CURRENT_FUNCTION to get a nice formatted name of a function that's about to throw, but with function names a couple of pages long I don't think that will be possible with this interface any more :-(
Thanks in advance, John Maddock.

Hubert Holin wrote:
The proposed interface is certainly nice and could definitely be worked with.
OK good.
For the global choices it is perfectly adequate, IMO. For finer control however, it does appear very fine-grained (per-function), which, in addition to making error message harder to read, is perhaps not the most frequent level of interaction needed. For instance, while I certainly see the need sometimes for per-function disabling of internal double promotion, I believe that more frequently users may want to set these preferences at the translation unit level, when not at the global level.
Whilst I think you're correct that per-TU control is the most often needed requirement: it's actually the hardest of all to provide without violating the dreaded "one definition rule". The only solution I can think of involves making the special function macros - which also means we can't overload them - and then internally mangling the function names according to the policies in effect (in other words the same as the current proposal). Perhaps more interesting, and based on the current proposal is per-namespace policies: #include <boost/math/special_functions.hpp> typedef something my_policy_type; namespace mynamespace{ BOOST_MATH_DECL_POLICY_SPECIAL_FUNCTIONS(my_policy_type); } Then "mynamespace::tgamma(x);" would use my_policy_type to determine it's behaviour. If you an unnamed namespace then this could be used in place of a using declaration, to bring a specific set of functions into the scope of the current TU only.
While (mostly) global objects may be evil, something along the lines of standard streams and their interaction with locales and facets may perhaps be easier to deal with. For instance, functions may requests their choices from some external "policy object", which may be locally altered, much as streams will enquire from a locale how to typeset dates. Granted, this would make multithreading all the hairier (which is not really a good idea).
Oh, no global objects please, they're evil, no really they are! :-)
On the other hand, not all functions necessarily require information about every configuration choice possible, so passing in all the info when only some is needed may perhaps be excessive.
That's true. However, I now have the meta-programming code that allows a function to reset policies that it knows aren't relevent to it to their defaults. Policy objects also get "normalised" internally to reduce code bloat, so: tgamma(x, policy1); and tgamma(x, policy2); are one-line inline forwarders that will call the same template instantiations internally, if there are no relevent differences between policy1 and policy2. Thanks for the feedback, John.
participants (2)
-
Hubert Holin
-
John Maddock