mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-30 12:30:23 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			512 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			512 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| 
 | |
| [section:pol_tutorial Policy Tutorial]
 | |
| 
 | |
| [section:what_is_a_policy So Just What is a Policy Anyway?]
 | |
| 
 | |
| A policy is a compile-time mechanism for customising the behaviour of a
 | |
| special function, or a statistical distribution.  With Policies you can
 | |
| control:
 | |
| 
 | |
| * What action to take when an error occurs.
 | |
| * What happens when you call a function that is mathematically undefined
 | |
| (for example, if you ask for the mean of a Cauchy distribution).
 | |
| * What happens when you ask for a quantile of a discrete distribution.
 | |
| * Whether the library is allowed to internally promote `float` to `double`
 | |
| and `double` to `long double` in order to improve precision.
 | |
| * What precision to use when calculating the result.
 | |
| 
 | |
| Some of these policies could arguably be runtime variables, but then we couldn't
 | |
| use compile-time dispatch internally to select the best evaluation method
 | |
| for the given policies.
 | |
| 
 | |
| For this reason a Policy is a /type/: in fact it's an instance of the
 | |
| class template `boost::math::policies::policy<>`.  This class is just a
 | |
| compile-time-container of user-selected policies (sometimes called a type-list):
 | |
| 
 | |
|    using namespace boost::math::policies;
 | |
|    //
 | |
|    // Define a policy that sets ::errno on overflow, and does
 | |
|    // not promote double to long double internally:
 | |
|    //
 | |
|    typedef policy<domain_error<errno_on_error>, promote_double<false> > mypolicy;
 | |
| 
 | |
| [endsect] [/section:what_is_a_policy So Just What is a Policy Anyway?]
 | |
| 
 | |
| [section:policy_tut_defaults Policies Have Sensible Defaults]
 | |
| 
 | |
| Most of the time you can just ignore the policy framework.
 | |
| 
 | |
| ['*The defaults for the various policies are as follows,
 | |
| if these work OK for you then you can stop reading now!]
 | |
| 
 | |
| [variablelist
 | |
| [[Domain Error][Throws a `std::domain_error` exception.]]
 | |
| [[Pole Error][Occurs when a function is evaluated at a pole: throws a `std::domain_error` exception.]]
 | |
| [[Overflow Error][Throws a `std::overflow_error` exception.]]
 | |
| [[Underflow][Ignores the underflow, and returns zero.]]
 | |
| [[Denormalised Result][Ignores the fact that the result is denormalised, and returns it.]]
 | |
| [[Rounding Error][Throws a `boost::math::rounding_error` exception.]]
 | |
| [[Internal Evaluation Error][Throws a `boost::math::evaluation_error` exception.]]
 | |
| [[Indeterminate Result Error][Returns a result that depends on the function where the error occurred.]]
 | |
| [[Promotion of float to double][Does occur by default - gives full float precision results.]]
 | |
| [[Promotion of double to long double][Does occur by default if long double offers
 | |
|    more precision than double.]]
 | |
| [[Precision of Approximation Used][By default uses an approximation that
 | |
|    will result in the lowest level of error for the type of the result.]]
 | |
| [[Behaviour of Discrete Quantiles]
 | |
|    [
 | |
|    The quantile function will by default return an integer result that has been
 | |
|    /rounded outwards/.  That is to say lower quantiles (where the probability is
 | |
|    less than 0.5) are rounded downward, and upper quantiles (where the probability
 | |
|    is greater than 0.5) are rounded upwards.  This behaviour
 | |
|    ensures that if an X% quantile is requested, then /at least/ the requested
 | |
|    coverage will be present in the central region, and /no more than/
 | |
|    the requested coverage will be present in the tails.
 | |
| 
 | |
| This behaviour can be changed so that the quantile functions are rounded
 | |
|    differently, or even return a real-valued result using
 | |
|    [link math_toolkit.pol_overview Policies].  It is strongly
 | |
|    recommended that you read the tutorial
 | |
|    [link math_toolkit.pol_tutorial.understand_dis_quant
 | |
|    Understanding Quantiles of Discrete Distributions] before
 | |
|    using the quantile function on a discrete distribution.  The
 | |
|    [link math_toolkit.pol_ref.discrete_quant_ref reference docs]
 | |
|    describe how to change the rounding policy
 | |
|    for these distributions.
 | |
| ]]
 | |
| ]
 | |
| 
 | |
| What's more, if you define your own policy type, then it automatically
 | |
| inherits the defaults for any policies not explicitly set, so given:
 | |
| 
 | |
|    using namespace boost::math::policies;
 | |
|    //
 | |
|    // Define a policy that sets ::errno on overflow, and does
 | |
|    // not promote double to long double internally:
 | |
|    //
 | |
|    typedef policy<domain_error<errno_on_error>, promote_double<false> > mypolicy;
 | |
| 
 | |
| then `mypolicy` defines a policy where only the overflow error handling and
 | |
| `double`-promotion policies differ from the defaults.
 | |
| 
 | |
| [endsect][/section:policy_tut_defaults Policies Have Sensible Defaults]
 | |
| 
 | |
| [section:policy_usage So How are Policies Used Anyway?]
 | |
| 
 | |
| The details follow later, but basically policies can be set by either:
 | |
| 
 | |
| * Defining some macros that change the default behaviour: [*this is the
 | |
|    recommended method for setting installation-wide policies].
 | |
| * By instantiating a distribution object with an explicit policy:
 | |
|    this is mainly reserved for ad hoc policy changes.
 | |
| * By passing a policy to a special function as an optional final argument:
 | |
|    this is mainly reserved for ad hoc policy changes.
 | |
| * By using some helper macros to define a set of functions or distributions
 | |
| in the current namespace that use a specific policy: [*this is the
 | |
| recommended method for setting policies on a project- or translation-unit-wide
 | |
| basis].
 | |
| 
 | |
| The following sections introduce these methods in more detail.
 | |
| 
 | |
| [endsect] [/section:policy_usage So How are Policies Used Anyway?]
 | |
| 
 | |
| [section:changing_policy_defaults Changing the Policy Defaults]
 | |
| 
 | |
| The default policies used by the library are changed by the usual
 | |
| configuration macro method.
 | |
| 
 | |
| For example, passing `-DBOOST_MATH_DOMAIN_ERROR_POLICY=errno_on_error` to
 | |
| your compiler will cause domain errors to set `::errno` and return a __NaN
 | |
| rather than the usual default behaviour of throwing a `std::domain_error`
 | |
| exception.
 | |
| 
 | |
| [tip For Microsoft Visual Studio,you can add to the Project Property Page,
 | |
| C/C++, Preprocessor, Preprocessor definitions like:
 | |
| 
 | |
| ``BOOST_MATH_ASSERT_UNDEFINED_POLICY=0
 | |
| BOOST_MATH_OVERFLOW_ERROR_POLICY=errno_on_error``
 | |
| 
 | |
| This may be helpful to avoid complications with pre-compiled headers
 | |
| that may mean that the equivalent definitions in source code:
 | |
| 
 | |
| ``#define BOOST_MATH_ASSERT_UNDEFINED_POLICY false
 | |
| #define BOOST_MATH_OVERFLOW_ERROR_POLICY errno_on_error``
 | |
| 
 | |
| *may be ignored*.
 | |
| 
 | |
| The compiler command line shows:
 | |
| 
 | |
| ``/D "BOOST_MATH_ASSERT_UNDEFINED_POLICY=0"
 | |
| /D "BOOST_MATH_OVERFLOW_ERROR_POLICY=errno_on_error"``
 | |
| ] [/MSVC tip]
 | |
| 
 | |
| There is however a very important caveat to this:
 | |
| 
 | |
| [important
 | |
| [*['Default policies changed by setting configuration macros must be changed
 | |
| uniformly in every translation unit in the program.]]
 | |
| 
 | |
| Failure to follow this rule may result in violations of the "One
 | |
| Definition Rule (ODR)" and result in unpredictable program behaviour.]
 | |
| 
 | |
| That means there are only two safe ways to use these macros:
 | |
| 
 | |
| * Edit them in [@../../../../boost/math/tools/user.hpp boost/math/tools/user.hpp],
 | |
| so that the defaults are set on an installation-wide basis.
 | |
| Unfortunately this may not be convenient if
 | |
| you are using a pre-installed Boost distribution (on Linux for example).
 | |
| * Set the defines in your project's Makefile or build environment, so that they
 | |
| are set uniformly across all translation units.
 | |
| 
 | |
| What you should *not* do is:
 | |
| 
 | |
| * Set the defines in the source file using `#define` as doing so
 | |
| almost certainly will break your program, unless you're absolutely
 | |
| certain that the program is restricted to a single translation unit.
 | |
| 
 | |
| And, yes, you will find examples in our test programs where we break this
 | |
| rule: but only because we know there will always be a single
 | |
| translation unit only: ['don't say that you weren't warned!]
 | |
| 
 | |
| [import ../../example/error_handling_example.cpp]
 | |
| 
 | |
| [error_handling_example]
 | |
| 
 | |
| [endsect] [/section:changing_policy_defaults Changing the Policy Defaults]
 | |
| 
 | |
| [section:ad_hoc_dist_policies Setting Policies for Distributions on an Ad Hoc Basis]
 | |
| 
 | |
| All of the statistical distributions in this library are class templates
 | |
| that accept two template parameters:
 | |
| real type (float, double ...) and policy (how to handle exceptional events),
 | |
| both with sensible defaults, for example:
 | |
| 
 | |
|    namespace boost{ namespace math{
 | |
| 
 | |
|    template <class RealType = double, class Policy = policies::policy<> >
 | |
|    class fisher_f_distribution;
 | |
| 
 | |
|    typedef fisher_f_distribution<> fisher_f;
 | |
| 
 | |
|    }}
 | |
| 
 | |
| This policy gets used by all the accessor functions that accept
 | |
| a distribution as an argument, and forwarded to all the functions called
 | |
| by these.  So if you use the shorthand-typedef for the distribution, then you get
 | |
| `double` precision arithmetic and all the default policies.
 | |
| 
 | |
| However, say for example we wanted to evaluate the quantile
 | |
| of the binomial distribution at float precision, without internal
 | |
| promotion to double, and with the result rounded to the /nearest/
 | |
| integer, then here's how it can be done:
 | |
| 
 | |
| [import ../../example/policy_eg_3.cpp]
 | |
| 
 | |
| [policy_eg_3]
 | |
| 
 | |
| Which outputs:
 | |
| 
 | |
| [pre quantile is: 40]
 | |
| 
 | |
| [endsect][/section:ad_hoc_dist_policies Setting Policies for Distributions on an Ad Hoc Basis]
 | |
| 
 | |
| [section:ad_hoc_sf_policies Changing the Policy on an Ad Hoc Basis for the Special Functions]
 | |
| 
 | |
| All of the special functions in this library come in two overloaded forms,
 | |
| one with a final "policy" parameter, and one without.  For example:
 | |
| 
 | |
|    namespace boost{ namespace math{
 | |
| 
 | |
|    template <class RealType, class Policy>
 | |
|    RealType tgamma(RealType, const Policy&);
 | |
| 
 | |
|    template <class RealType>
 | |
|    RealType tgamma(RealType);
 | |
| 
 | |
|    }} // namespaces
 | |
| 
 | |
| Normally, the second version is just a forwarding wrapper to the first
 | |
| like this:
 | |
| 
 | |
|    template <class RealType>
 | |
|    inline RealType tgamma(RealType x)
 | |
|    {
 | |
|       return tgamma(x, policies::policy<>());
 | |
|    }
 | |
| 
 | |
| So calling a special function with a specific policy
 | |
| is just a matter of defining the policy type to use
 | |
| and passing it as the final parameter.  For example,
 | |
| suppose we want `tgamma` to behave in a C-compatible
 | |
| fashion and set `::errno` when an error occurs, and never
 | |
| throw an exception:
 | |
| 
 | |
| [import ../../example/policy_eg_1.cpp]
 | |
| 
 | |
| [policy_eg_1]
 | |
| 
 | |
| which outputs:
 | |
| 
 | |
| [pre
 | |
| Result of tgamma(30000) is: 1.#INF
 | |
| errno = 34
 | |
| Result of tgamma(-10) is: 1.#QNAN
 | |
| errno = 33
 | |
| ]
 | |
| 
 | |
| Alternatively, for ad hoc use, we can use the `make_policy`
 | |
| helper function to create a policy for us: this usage is more
 | |
| verbose, so is probably only preferred when a policy is going
 | |
| to be used once only:
 | |
| 
 | |
| [import ../../example/policy_eg_2.cpp]
 | |
| 
 | |
| [policy_eg_2]
 | |
| 
 | |
| [endsect] [/section:ad_hoc_sf_policies Changing the Policy on an Ad Hoc Basis for the Special Functions]
 | |
| 
 | |
| [section:namespace_policies Setting Policies at Namespace or Translation Unit Scope]
 | |
| 
 | |
| Sometimes what you want to do is just change a set of policies within
 | |
| the current scope: *the one thing you should not do in this situation
 | |
| is use the configuration macros*, as this can lead to "One Definition
 | |
| Rule" violations.  Instead this library provides a pair of macros
 | |
| especially for this purpose.
 | |
| 
 | |
| Let's consider the special functions first: we can declare a set of
 | |
| forwarding functions that all use a specific policy using the
 | |
| macro BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(['Policy]).  This
 | |
| macro should be used either inside a unique namespace set aside for the
 | |
| purpose (for example, a C namespace for a C-style policy),
 | |
| or an unnamed namespace if you just want the functions
 | |
| visible in global scope for the current file only.
 | |
| 
 | |
| [import ../../example/policy_eg_4.cpp]
 | |
| 
 | |
| [policy_eg_4]
 | |
| 
 | |
| The same mechanism works well at file scope as well, by using an unnamed
 | |
| namespace, we can ensure that these declarations don't conflict with any
 | |
| alternate policies present in other translation units:
 | |
| 
 | |
| [import ../../example/policy_eg_5.cpp]
 | |
| 
 | |
| [policy_eg_5]
 | |
| 
 | |
| Handling policies for the statistical distributions is very similar except that now
 | |
| the macro BOOST_MATH_DECLARE_DISTRIBUTIONS accepts two parameters: the
 | |
| floating point type to use, and the policy type to apply.  For example:
 | |
| 
 | |
|    BOOST_MATH_DECLARE_DISTRIBUTIONS(double, mypolicy)
 | |
| 
 | |
| Results a set of typedefs being defined like this:
 | |
| 
 | |
|    typedef boost::math::normal_distribution<double, mypolicy> normal;
 | |
| 
 | |
| The name of each typedef is the same as the name of the distribution
 | |
| class template, but without the "_distribution" suffix.
 | |
| 
 | |
| [import ../../example/policy_eg_6.cpp]
 | |
| 
 | |
| [policy_eg_6]
 | |
| 
 | |
| [note
 | |
| There is an important limitation to note: you can *not use the macros
 | |
| BOOST_MATH_DECLARE_DISTRIBUTIONS and BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS
 | |
| ['in the same namespace]*,  as doing so creates ambiguities between functions
 | |
| and distributions of the same name.
 | |
| ]
 | |
| 
 | |
| As before, the same mechanism works well at file scope as well: by using an unnamed
 | |
| namespace, we can ensure that these declarations don't conflict with any
 | |
| alternate policies present in other translation units:
 | |
| 
 | |
| [import ../../example/policy_eg_7.cpp]
 | |
| 
 | |
| [policy_eg_7]
 | |
| 
 | |
| [endsect][/section:namespace_policies Setting Policies at Namespace or Translation Unit Scope]
 | |
| 
 | |
| [section:user_def_err_pol Calling User Defined Error Handlers]
 | |
| 
 | |
| [import ../../example/policy_eg_8.cpp]
 | |
| 
 | |
| [policy_eg_8]
 | |
| 
 | |
| [import ../../example/policy_eg_9.cpp]
 | |
| 
 | |
| [policy_eg_9]
 | |
| 
 | |
| [endsect] [/section:user_def_err_pol Calling User Defined Error Handlers]
 | |
| 
 | |
| [section:understand_dis_quant Understanding Quantiles of Discrete Distributions]
 | |
| 
 | |
| Discrete distributions present us with a problem when calculating the
 | |
| quantile: we are starting from a continuous real-valued variable - the
 | |
| probability - but the result (the value of the random variable)
 | |
| should really be discrete.
 | |
| 
 | |
| Consider for example a Binomial distribution, with a sample size of
 | |
| 50, and a success fraction of 0.5.  There are a variety of ways
 | |
| we can plot a discrete distribution, but if we plot the PDF
 | |
| as a step-function then it looks something like this:
 | |
| 
 | |
| [$../graphs/binomial_pdf.png]
 | |
| 
 | |
| Now lets suppose that the user asks for a the quantile that corresponds
 | |
| to a probability of 0.05, if we zoom in on the CDF for that region here's
 | |
| what we see:
 | |
| 
 | |
| [$../graphs/binomial_quantile_1.png]
 | |
| 
 | |
| As can be seen there is no random variable that corresponds to
 | |
| a probability of exactly 0.05, so we're left with two choices as
 | |
| shown in the figure:
 | |
| 
 | |
| * We could round the result down to 18.
 | |
| * We could round the result up to 19.
 | |
| 
 | |
| In fact there's actually a third choice as well: we could "pretend" that the
 | |
| distribution was continuous and return a real valued result: in this case we
 | |
| would calculate a result of approximately 18.701 (this accurately
 | |
| reflects the fact that the result is nearer to 19 than 18).
 | |
| 
 | |
| By using policies we can offer any of the above as options, but that
 | |
| still leaves the question: ['What is actually the right thing to do?]
 | |
| 
 | |
| And in particular: ['What policy should we use by default?]
 | |
| 
 | |
| In coming to an answer we should realise that:
 | |
| 
 | |
| * Calculating an integer result is often much faster than
 | |
| calculating a real-valued result: in fact in our tests it
 | |
| was up to 20 times faster.
 | |
| * Normally people calculate quantiles so that they can perform
 | |
| a test of some kind: ['"If the random variable is less than N
 | |
| then we can reject our null-hypothesis with 90% confidence."]
 | |
| 
 | |
| So there is a genuine benefit to calculating an integer result
 | |
| as well as it being "the right thing to do" from a philosophical
 | |
| point of view.  What's more if someone asks for a quantile at 0.05,
 | |
| then we can normally assume that they are asking for
 | |
| ['[*at least] 95% of the probability to the right of the value chosen,
 | |
| and [*no more than] 5% of the probability to the left of the value chosen.]
 | |
| 
 | |
| In the above binomial example we would therefore round the result down to 18.
 | |
| 
 | |
| The converse applies to upper-quantiles: If the probability is greater than
 | |
| 0.5 we would want to round the quantile up, ['so that [*at least] the requested
 | |
| probability is to the left of the value returned, and [*no more than] 1 - the
 | |
| requested probability is to the right of the value returned.]
 | |
| 
 | |
| Likewise for two-sided intervals, we would round lower quantiles down,
 | |
| and upper quantiles up.  This ensures that we have ['at least the requested
 | |
| probability in the central region] and ['no more than 1 minus the requested
 | |
| probability in the tail areas.]
 | |
| 
 | |
| For example, taking our 50 sample binomial distribution with a success fraction
 | |
| of 0.5, if we wanted a two sided 90% confidence interval, then we would ask
 | |
| for the 0.05 and 0.95 quantiles with the results ['rounded outwards] so that
 | |
| ['at least 90% of the probability] is in the central area:
 | |
| 
 | |
| [$../graphs/binomial_pdf_3.png]
 | |
| 
 | |
| So far so good, but there is in fact a trap waiting for the unwary here:
 | |
| 
 | |
|    quantile(binomial(50, 0.5), 0.05);
 | |
| 
 | |
| returns 18 as the result, which is what we would expect from the graph above,
 | |
| and indeed there is no x greater than 18 for which:
 | |
| 
 | |
|    cdf(binomial(50, 0.5), x) <= 0.05;
 | |
| 
 | |
| However:
 | |
| 
 | |
|    quantile(binomial(50, 0.5), 0.95);
 | |
| 
 | |
| returns 31, and indeed while there is no x less than 31 for which:
 | |
| 
 | |
|    cdf(binomial(50, 0.5), x) >= 0.95;
 | |
| 
 | |
| We might naively expect that for this symmetrical distribution the result
 | |
| would be 32 (since 32 = 50 - 18), but we need to remember that the cdf of
 | |
| the binomial is /inclusive/ of the random variable.  So while the left tail
 | |
| area /includes/ the quantile returned, the right tail area always excludes
 | |
| an upper quantile value: since that "belongs" to the central area.
 | |
| 
 | |
| Look at the graph above to see what's going on here: the lower quantile
 | |
| of 18 belongs to the left tail, so any value <= 18 is in the left tail.
 | |
| The upper quantile of 31 on the other hand belongs to the central area,
 | |
| so the tail area actually starts at 32, so any value > 31 is in the
 | |
| right tail.
 | |
| 
 | |
| Therefore if U and L are the upper and lower quantiles respectively, then
 | |
| a random variable X is in the tail area - where we would reject the null
 | |
| hypothesis if:
 | |
| 
 | |
|    X <= L || X > U
 | |
| 
 | |
| And the a variable X is inside the central region if:
 | |
| 
 | |
|    L < X <= U
 | |
| 
 | |
| The moral here is to ['always be very careful with your comparisons
 | |
| when dealing with a discrete distribution], and if in doubt,
 | |
| ['base your comparisons on CDF's instead].
 | |
| 
 | |
| [heading Other Rounding Policies are Available]
 | |
| 
 | |
| As you would expect from a section on policies, you won't be surprised
 | |
| to know that other rounding options are available:
 | |
| 
 | |
| [variablelist
 | |
| 
 | |
| [[integer_round_outwards]
 | |
|    [This is the default policy as described above: lower quantiles
 | |
|    are rounded down (probability < 0.5), and upper quantiles
 | |
|    (probability > 0.5) are rounded up.
 | |
| 
 | |
|    This gives /no more than/ the requested probability
 | |
|    in the tails, and /at least/ the requested probability
 | |
|    in the central area.]]
 | |
| [[integer_round_inwards]
 | |
|    [This is the exact opposite of the default policy:
 | |
|    lower quantiles
 | |
|    are rounded up (probability < 0.5),
 | |
|    and upper quantiles (probability > 0.5) are rounded down.
 | |
| 
 | |
|    This gives /at least/ the requested probability
 | |
|    in the tails, and /no more than/ the requested probability
 | |
|    in the central area.]]
 | |
| [[integer_round_down][This policy will always round the result down
 | |
|    no matter whether it is an upper or lower quantile]]
 | |
| [[integer_round_up][This policy will always round the result up
 | |
|    no matter whether it is an upper or lower quantile]]
 | |
| [[integer_round_nearest][This policy will always round the result
 | |
|    to the nearest integer
 | |
|    no matter whether it is an upper or lower quantile]]
 | |
| [[real][This policy will return a real valued result
 | |
|    for the quantile of a discrete distribution: this is
 | |
|    generally much slower than finding an integer result
 | |
|    but does allow for more sophisticated rounding policies.]]
 | |
| 
 | |
| ]
 | |
| 
 | |
| [import ../../example/policy_eg_10.cpp]
 | |
| 
 | |
| [policy_eg_10]
 | |
| 
 | |
| [endsect]
 | |
| 
 | |
| [endsect] [/section:pol_Tutorial Policy Tutorial]
 | |
| 
 | |
| 
 | |
| [/ math.qbk
 | |
|   Copyright 2007, 2013 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).
 | |
| ]
 | |
| 
 | |
| 
 |