Boost C++ Libraries: Ticket #11395: Beta Binonial Distriction/ log beta / boost::math::beta not usable when the result would be very small https://svn.boost.org/trac10/ticket/11395 <p> Because the computation inside beta_imp is not done in the logarithm scale it returns zero instead of the correct value. Proposed corrected implementation: </p> <p> <em> Lanczos calculation: T agh = a + Lanczos::g() - T(0.5); T bgh = b + Lanczos::g() - T(0.5); T cgh = c + Lanczos::g() - T(0.5); result = Lanczos::lanczos_sum_expG_scaled(a) * Lanczos::lanczos_sum_expG_scaled(b) / Lanczos::lanczos_sum_expG_scaled(c); </em> If a and b were originally less than 1 we need to scale the result: result *= prefix; result = log(result); result += (.5*(1-log(bgh))); </p> <p> T ambh = a - T(0.5) - b; if((fabs(b * ambh) &lt; (cgh * 100)) &amp;&amp; (a &gt; 100)) { </p> <blockquote> <p> <em> Special case where the base of the power term is close to 1 </em> compute (1+x)<sup>y instead: result += (ambh * boost::math::log1p(-b / cgh, pol)); result += (b*(log(agh)+log(bgh)-2*log(cgh))); </sup></p> </blockquote> <p> } else { </p> <blockquote> <p> result += (a-T(0.5))*(log(agh)-log(cgh))+(b*(log(bgh)-log(cgh))); </p> </blockquote> <p> } return exp(result); </p> <p> Also, it would be good to also propose a 'l_beta_imp' function returning the logarithm of beta_imp, i.e., not doing the 'exp(result)'. The same can be done for 'binomial_coefficient' by introducing a 'l_binomial_coefficient' function. </p> <p> The main interest is the combination of those functions without problems, e.g.: </p> <blockquote> <p> binomial_coefficient(...) * beta(...) / beta(...) can be converted to: exp(l_binomial_coefficient(...) + l_beta(...) - l_beta(...)) </p> </blockquote> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/11395 Trac 1.4.3 John Maddock Mon, 15 Jun 2015 07:53:36 GMT <link>https://svn.boost.org/trac10/ticket/11395#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11395#comment:1</guid> <description> <p> Thanks for the report. </p> <p> Can you give me a concrete example that underflows too soon? I'm having trouble finding one testing random values. </p> <p> Adding lbeta may well be useful: although in your example I would expect cancellation errors to dominate for large arguments. You could also break the expression down into lgamma calls - if the arguments are related you well get a lot of algebraic simplification that way too? </p> </description> <category>Ticket</category> </item> <item> <author>Oli Quinet <quinet.olivier@…></author> <pubDate>Mon, 15 Jun 2015 08:49:27 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/11395#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11395#comment:2</guid> <description> <p> Here are some mu/rho values that generate large values for the coefficient alpha/beta (alpha = mu*(1-rho)/rho and beta = (1-mu)*(1-rho)/rho)): </p> <p> mu = 0.959234 rho = 2.73072e-5 a (alpha) = 35126.55 b (beta) = 1492.826 </p> <p> =&gt; expected log(beta(a,b)): l_beta(a, b) = -6241.592 </p> <p> Performing the calculation in the logarithm scale reduces the cancellation errors as it involves adding/subtracting quantities and only taking the exp of the sum at the end instead of multiplying/dividing very large/small numbers. </p> <p> I'm using the binomial_coefficient and beta functions to compute the beta_binomial distribution (<a class="ext-link" href="https://en.wikipedia.org/wiki/Beta-binomial_distribution"><span class="icon">​</span>https://en.wikipedia.org/wiki/Beta-binomial_distribution</a>). I.e. in boost notation: pdf(k, beta_binomial_dist(n, a, b)) = exp(l_binomial_coefficient(n, k) + l_beta(a+k,b+n-k) - l_beta(a, b)). </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Mon, 15 Jun 2015 18:18:36 GMT</pubDate> <title>type, summary changed https://svn.boost.org/trac10/ticket/11395#comment:3 https://svn.boost.org/trac10/ticket/11395#comment:3 <ul> <li><strong>type</strong> <span class="trac-field-old">Patches</span> → <span class="trac-field-new">Feature Requests</span> </li> <li><strong>summary</strong> <span class="trac-field-old">boost::math::beta not working for large a,b values</span> → <span class="trac-field-new">Beta Binonial Distriction/ log beta / boost::math::beta not usable when the result would be very small</span> </li> </ul> <p> OK, I'm changing this to a feature request, since beta is returning the correct value (0) in the case you give, as the result is outside the range of a double. </p> <p> If you expect less error from using logs then you will be *very* disappointed: <code> exp(a-b) </code> is the classic example that *causes* cancellation error. In the extreme case where a and b are close in value, <em>you will get no significant digits correct in the result.</em> This is true, even when a and b are correct to 0.5ulp - and typically they will have much larger error than that. This is why we go to great lengths to avoid computing via logs everywhere in Boost.Math - the errors produced are typically several orders of magnitude lower. </p> <p> However, without investigating this much more in depth than I have time for at present, it does look like you're stuck with logs, suggest you use the alternate form listed here: <a class="ext-link" href="http://www.wolframalpha.com/input/?i=Binomial[n%2Ck]+*+Beta[k%2Ba%2C+n-k%2Bb]+%2F+Beta[a%2Cb"><span class="icon">​</span>http://www.wolframalpha.com/input/?i=Binomial[n%2Ck]+*+Beta[k%2Ba%2C+n-k%2Bb]+%2F+Beta[a%2Cb</a>] and use lgamma (check I typed the equations correctly though!!) You will also need to do a very careful error analysis to make sure the results aren't garbage. </p> Ticket Oli Quinet <quinet.olivier@…> Mon, 15 Jun 2015 18:35:30 GMT <link>https://svn.boost.org/trac10/ticket/11395#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/11395#comment:4</guid> <description> <p> Yes, it is true if a is very close to b. But in my case, it is not a problem, as it is taken care of before (limits check). </p> <p> Thank you for suggesting lgamma, I didn't see this function before. I will check it tomorrow. </p> <p> Thanks again. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>John Maddock</dc:creator> <pubDate>Tue, 31 Jul 2018 17:36:27 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/11395#comment:5 https://svn.boost.org/trac10/ticket/11395#comment:5 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">obsolete</span> </li> </ul> Ticket