1 // -*- C++ -*-
2 /**
3 * @brief Efficient implementation of the computation of the Student T
4 * CDF and quantile
5 *
6 * Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
7 *
8 * This library is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this library. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22 #include <cmath>
23
24 #include "openturns/StudentFunctions.hxx"
25 #include "openturns/Exception.hxx"
26 #include "openturns/SpecFunc.hxx"
27 #include "openturns/DistFunc.hxx"
28 #include "openturns/GaussKronrodRule.hxx"
29 #include "openturns/Log.hxx"
30 #include "openturns/OTconfig.hxx"
31 #ifdef OPENTURNS_HAVE_BOOST
32 #define BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
33 #include <boost/math/distributions/students_t.hpp>
34 #include <boost/math/distributions/non_central_t.hpp>
35 #endif
36
37 BEGIN_NAMESPACE_OPENTURNS
38
39 namespace StudentFunctions
40 {
41
42 /********************************************************************************************************************************/
43 /* Normalized Student distribution, i.e. with a PDF equals to (1 + x^2 / nu)^(-(1 + nu) / 2) / (sqrt(nu) . Beta(1 / 2, nu / 2)) */
44 /********************************************************************************************************************************/
45 /* CDF */
StudentCDF(const Scalar nu,const Scalar x,const Bool tail)46 Scalar StudentCDF(const Scalar nu,
47 const Scalar x,
48 const Bool tail)
49 {
50 if (!(nu > 0.0)) throw InvalidArgumentException(HERE) << "Error: nu must be positive, here nu=" << nu;
51 if (x == 0.0) return 0.5;
52 if (nu == 1.0) return (tail ? 0.5 - (std::atan(x) * M_1_PI) : 0.5 + (std::atan(x) * M_1_PI));
53 const Scalar x2 = x * x;
54 if (nu == 2.0) return (tail ? 0.5 - 0.5 * (x / std::sqrt(2.0 + x2)) : 0.5 + 0.5 * (x / std::sqrt(2.0 + x2)));
55 if (nu == 3.0) return (tail ? 0.5 - (std::atan(x / std::sqrt(3.0)) * M_1_PI + x * std::sqrt(3.0) / (M_PI * (3.0 + x2))) : 0.5 + (std::atan(x / std::sqrt(3.0)) * M_1_PI + x * std::sqrt(3.0) / (M_PI * (3.0 + x2))));
56 if (nu == 4.0) return (tail ? 0.5 - (0.5 * x * (x2 + 6.0) * std::pow(4.0 + x2, -1.5)) : 0.5 + (0.5 * x * (x2 + 6.0) * std::pow(4.0 + x2, -1.5)));
57 if (nu == 5.0) return (tail ? 0.5 - (std::atan(x / std::sqrt(5.0)) * M_1_PI + x * std::sqrt(5.0) * (3.0 * x2 + 25.0) / (3.0 * M_PI * std::pow(5.0 + x2, 2))) : 0.5 + (std::atan(x / std::sqrt(5.0)) * M_1_PI + x * std::sqrt(5.0) * (3.0 * x2 + 25.0) / (3.0 * M_PI * std::pow(5.0 + x2, 2))));
58 if (nu == 6.0) return (tail ? 0.5 - (0.25 * x * (135.0 + x2 * (30.0 + 2.0 * x2)) * std::pow(6.0 + x2, -2.5)) : 0.5 + (0.25 * x * (135.0 + x2 * (30.0 + 2.0 * x2)) * std::pow(6.0 + x2, -2.5)));
59 if (nu == 7.0) return (tail ? 0.5 - (std::atan(x / std::sqrt(7.0)) * M_1_PI + x * std::sqrt(7.0) * (1617.0 + x2 * (280.0 + 15.0 * x2)) / (15.0 * M_PI * std::pow(7.0 + x2, 3))) : 0.5 + (std::atan(x / std::sqrt(7.0)) * M_1_PI + x * std::sqrt(7.0) * (1617.0 + x2 * (280.0 + 15.0 * x2)) / (15.0 * M_PI * std::pow(7.0 + x2, 3))));
60 #ifdef OPENTURNS_HAVE_BOOST
61 return (tail ? boost::math::cdf(boost::math::complement(boost::math::students_t(nu), x)) : boost::math::cdf(boost::math::students_t(nu), x)) ;
62 #else
63 #ifdef USE_NEW_ALGO
64 // First, try to use a Cornish-Fisher expansion
65 if (nu > 1e3)
66 {
67 const Scalar inu = 1.0 / nu;
68 // Compute the last corrective term first in order to check if the Cornish-Fisher approximation is good enough
69 // ~ \phi(x)/x+x\phi(x)[c1/nu + ... + c4/nu^4]
70 // -> \epsilon/x = x * c4 /nu^4 -> nu=(x^2*c4/\epsilon)^{1/4}
71 const Scalar c4 = (21.0 / 2048.0 + (61.0 / 6144.0 + (-71.0 / 30720.0 + (-313.0 / 30720.0 + (-2141.0 / 92160.0 + (445.0 / 18432.0 + (-25.0 / 6144.0 + x2 / 6144.0) * x2) * x2) * x2) * x2) * x2) * x2) * x2;
72 const Scalar lastContribution = std::abs(c4 * inu * inu * inu * inu * x);
73 Scalar normalPDF = DistFunc::dNormal(x);
74 Scalar normalCCDF = DistFunc::pNormal(std::abs(x), true);
75 if (normalCCDF > SpecFunc::ScalarEpsilon * normalPDF * lastContribution)
76 {
77 const Scalar c1 = 0.25 * (1.0 + x2);
78 const Scalar c2 = (-3.0 + x2 * (-5.0 + x2 * (-7.0 + 3.0 * x2))) / 96.0;
79 const Scalar c3 = (-15.0 + x2 * (-3.0 + x2 * (6.0 + x2 * (14.0 + x2 * (-11.0 + x2))))) / 384.0;
80 const Scalar correction = std::abs(x) * (inu * (c1 + inu * (c2 + inu * (c3 + inu * c4))));
81 const Scalar value = normalCCDF + normalPDF * correction;
82 return (((x >= 0.0) == tail) ? value : 0.5 + (0.5 - value));
83 }
84 }
85 #endif
86 Scalar value = 0.0;
87 if (2.0 * x2 > nu) value = 0.5 * SpecFunc::RegularizedIncompleteBeta(0.5, 0.5 * nu, x2 / (x2 + nu), true);
88 else value = 0.5 * SpecFunc::RegularizedIncompleteBeta(0.5 * nu, 0.5, nu / (x2 + nu), false);
89 return (((x < 0.0) == tail) ? 0.5 + (0.5 - value) : value);
90 #endif // OPENTURNS_HAVE_BOOST
91 }
92
93 /* The algorithm is based on the following article:
94 William T. Shaw, "New methods for simulating the Student T-distribution - direct use of the inverse cumulative distribution"
95 eprint http://eprints.maths.ox.ac.uk/184/1/tdist.pdf
96 */
StudentQuantile(const Scalar nu,const Scalar p,const Bool tail)97 Scalar StudentQuantile(const Scalar nu,
98 const Scalar p,
99 const Bool tail)
100 {
101 if (!(nu > 0.0)) throw InvalidArgumentException(HERE) << "Error: nu must be positive, here nu=" << nu;
102 if (p == 0.5) return 0.0;
103 const Scalar u = std::min(p, 0.5 + (0.5 - p));
104 if (nu == 1.0)
105 {
106 Scalar value = 0.0;
107 if (std::abs(u) < 0.025373628595705897178)
108 {
109 const Scalar u2 = u * u;
110 value = (-0.31830988618379067153 + (1.0471975511965977462 + (0.68902837067332933726 + (0.64766070854027820799 + 0.63921549794217821540 * u2) * u2) * u2) * u2) / u;
111 }
112 else value = std::tan((u - 0.5) * M_PI);
113 return (tail == (p < 0.5) ? -value : value);
114 }
115 if (nu == 2.0)
116 {
117 const Scalar alpha = 2.0 * u - 1.0;
118 const Scalar value = alpha * std::sqrt(2.0 / (0.5 + (0.5 - alpha * alpha)));
119 return (tail == (p < 0.5) ? -value : value);
120 }
121 if (nu == 4.0)
122 {
123 const Scalar alphaSqrt = 2.0 * std::sqrt(p * (0.5 + (0.5 - p)));
124 const Scalar value = 2.0 * std::sqrt((std::cos(std::acos(alphaSqrt) / 3.0) / alphaSqrt - 0.5) - 0.5);
125 // Warning! Here the test is different from the other ones
126 return (tail == (p > 0.5) ? -value : value);
127 }
128 #ifdef OPENTURNS_HAVE_BOOST
129 return tail ? boost::math::quantile(boost::math::complement(boost::math::students_t(nu), p)) : boost::math::quantile(boost::math::students_t(nu), p);
130 #else
131 #ifdef USE_NEW_ALGO
132 // Central part
133 const Scalar delta = 1.0 / nu;
134 const Scalar normalizationFactor = std::sqrt(nu * M_PI) * std::exp(SpecFunc::LogGamma(0.5 * nu) - SpecFunc::LogGamma(0.5 * (nu + 1.0)));
135 const Scalar v = (p - 0.5) * normalizationFactor;
136 const Scalar c30 = 0.11362104808202311779e-7 + (-0.10994648871905821641e-6 + (0.52754948010031397619e-6 + (-0.16579190541298212282e-5 + (0.37966880029665235514e-5 + (-0.66596982230496113818e-5 + (0.91156850995515265275e-5 + (-0.96136067004897554437e-5 + (0.72052743951206692720e-5 + (-0.23729574256549482204e-5 + (-0.31083841717988836362e-5 + (0.72241563770271714564e-5 + (-0.88503346702785761842e-5 + (0.81494033025679967378e-5 + (-0.61299787607422707781e-5 + (0.38918328140793891542e-5 + (-0.21177061849789713922e-5 + (0.99481889094179105397e-6 + (-0.40449311472598426761e-6 + (0.14225421139331034806e-6 + (-0.43122948806183507233e-7 + (0.11198634674338365791e-7 + (-0.24684919226898448232e-8 + (0.45586959658322983050e-9 + (-0.69253251498584479106e-10 + (0.84307519236004815165e-11 + (-0.79095838183517833726e-12 + (0.53696817752523318081e-13 + (-0.23480084614199964747e-14 + (0.49659938970935851773e-16 + 0.19701319568021683118e-83 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
137 if (c30 * std::pow(std::abs(v), 30) < SpecFunc::Precision)
138 {
139 const Scalar v2 = v * v;
140 const Scalar c1 = 0.16666666666666666667e0 + 0.16666666666666666667e0 * delta;
141 const Scalar c2 = 0.58333333333333333333e-1 + (0.66666666666666666667e-1 + 0.83333333333333333333e-2 * delta) * delta;
142 const Scalar c3 = 0.25198412698412698413e-1 + (0.26785714285714285714e-1 + (0.17857142857142857143e-2 + 0.19841269841269841270e-3 * delta) * delta) * delta;
143 const Scalar c4 = 0.12039792768959435626e-1 + (0.10559964726631393298e-1 + (-0.11078042328042328042e-2 + (0.37477954144620811287e-3 + 0.27557319223985890653e-5 * delta) * delta) * delta) * delta;
144 const Scalar c5 = 0.61039211560044893378e-2 + (0.38370059724226390893e-2 + (-0.16095979637646304313e-2 + (0.59458674042007375341e-3 + (-0.62705427288760622094e-4 + 0.25052108385441718775e-7 * delta) * delta) * delta) * delta) * delta;
145 const Scalar c6 = 0.32177478835464946576e-2 + (0.10898206731540064873e-2 + (-0.12579159844784844785e-2 + (0.69084207973096861986e-3 + (-0.16376804137220803887e-3 + (0.15401265401265401265e-4 + 0.16059043836821614599e-9 * delta) * delta) * delta) * delta) * delta) * delta;
146 const Scalar c7 = 0.17438262298340009980e-2 + (0.33530976880017885309e-4 + (-0.76245135440323932387e-3 + (0.64513046951456342991e-3 + (-0.24947258047043099953e-3 + (0.49255746366361445727e-4 + (-0.39851014346715404916e-5 + 0.76471637318198164759e-12 * delta) * delta) * delta) * delta) * delta) * delta) * delta;
147 const Scalar c8 = 0.96472747321388644237e-3 + (-0.31101086326318780412e-3 + (-0.36307660358786885787e-3 + (0.51406605788341121363e-3 + (-0.29133414466938067350e-3 + (0.90867107935219902229e-4 + (-0.15303004486655377567e-4 + (0.10914179173496789432e-5 + 0.28114572543455207632e-14 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
148 const Scalar c9 = 0.54229262813129686486e-3 + (-0.36942667800009661203e-3 + (-0.10230378073700412687e-3 + (0.35764655430568632777e-3 + (-0.28690924218514613987e-3 + (0.12645437628698076975e-3 + (-0.33202652391372058698e-4 + (0.48903045291975346210e-5 + (-0.31239569599829868045e-6 + 0.82206352466243297170e-17 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
149 const Scalar c10 = 0.30873303081359101129e-3 + (-0.32537004938571011330e-3 + (0.43550551405434728655e-4 + (0.21464548012307279066e-3 + (-0.24866783037387793908e-3 + (0.14689614712949377285e-3 + (-0.53558768075354021202e-4 + (0.12193465978325997301e-4 + (-0.15992939851465476095e-5 + (0.92645939464804105906e-7 + 0.19572941063391261231e-19 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
150 const Scalar c11 = 0.17759647804672470704e-3 + (-0.25535863863970254416e-3 + (0.11096883532369592643e-3 + (0.10245143385318167742e-3 + (-0.19299737813074419180e-3 + (0.14967238384542144940e-3 + (-0.71397916945779067573e-4 + (0.22340804256056967439e-4 + (-0.45025708360340229558e-5 + (0.53317862670086688269e-6 + (-0.28285516204934114990e-7 + 0.38681701706306840377e-22 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
151 const Scalar c12 = 0.10304494161207094302e-3 + (-0.18870337947507879042e-3 + (0.13111867117178691920e-3 + (0.24142361947767894831e-4 + (-0.13381350551156149560e-3 + (0.13730787745727972950e-3 + (-0.82983259907885451298e-4 + (0.33532084099933722245e-4 + (-0.92267370235976158080e-5 + (0.16716613200640343655e-5 + (-0.18065780614881715429e-6 + (0.88471846978918480156e-8 + 0.64469502843844733962e-25 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
152 const Scalar c13 = 0.60225008003409982305e-4 + (-0.13446378338878729098e-3 + (0.12598693847856027229e-3 + (-0.24590085473754607964e-4 + (-0.80299081016436116016e-4 + (0.11482249262723200489e-3 + (-0.86686905022801866130e-4 + (0.43609942443795689198e-4 + (-0.15373457330466647584e-4 + (0.37852139693751540035e-5 + (-0.62383416384412893096e-6 + (0.62064429966766230519e-7 + (-0.28243405937805525220e-8 + 0.91836898637955461484e-28 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
153 const Scalar c14 = 0.35418325565277953269e-4 + (-0.93539064666727070094e-4 + (0.10944539484512486371e-3 + (-0.50697922600088918528e-4 + (-0.37072450319611740043e-4 + (0.87596232666609754365e-4 + (-0.82848204898007245194e-4 + (0.50788700804564126157e-4 + (-0.22068398246898720913e-4 + (0.69246032720547932818e-5 + (-0.15457039388956089765e-5 + (0.23391857848945430160e-6 + (-0.21577431344124575639e-7 + (0.91752074323779275911e-9 + 0.11309962886447716932e-30 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
154 const Scalar c15 = 0.20941876127554053895e-4 + (-0.63982320984468180920e-4 + (0.89476217782852851948e-4 + (-0.61139401127641540572e-4 + (-0.53533915472133463276e-5 + (0.60092507103170186035e-4 + (-0.73116739352278641607e-4 + (0.54064352860717542135e-4 + (-0.28252385872037662336e-4 + (0.10858001403563361875e-4 + (-0.30773127066866958235e-5 + (0.62914443600476829315e-6 + (-0.88098412226584032081e-7 + (0.75797246590958718870e-8 + (-0.30260395874299918486e-9 + 0.12161250415535179496e-33 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
155 const Scalar c16 = 0.12440676964611940273e-4 + (-0.43227811212828780986e-4 + (0.70220395983921690289e-4 + (-0.61710480704301234682e-4 + (0.15725654644255476988e-4 + (0.35359168269646105688e-4 + (-0.59705715039685462092e-4 + (0.53283122436065111649e-4 + (-0.32980546454767555846e-4 + (0.15124084472974760560e-4 + (-0.52275481468058844855e-5 + (0.13532478852492074758e-5 + (-0.25548550443536494162e-6 + (0.33313281803022382511e-7 + (-0.26868931811498915393e-8 + (0.10112530549820428339e-9 + 0.11516335620771950281e-36 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
156 const Scalar c17 = 0.74211365422405435880e-5 + (-0.28933653345888529746e-4 + (0.53504983616749018458e-4 + (-0.56733447346013552297e-4 + (0.28046995125144914249e-4 + (0.15046953874432646849e-4 + (-0.44797205572871768076e-4 + (0.48974195262422243935e-4 + (-0.35626672354897940259e-4 + (0.19154758965665053933e-4 + (-0.78594245320243336015e-5 + (0.24736906403032496435e-5 + (-0.59010219823058050328e-6 + (0.10357491081664883410e-6 + (-0.12643342283016852693e-7 + (0.96013308009708694978e-9 + (-0.34189008978763638042e-10 + 0.96775929586318909921e-40 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
157 const Scalar c18 = 0.44431685013520881494e-5 + (-0.19225728937686126470e-4 + (0.39866342691238713685e-4 + (-0.49168720718451249294e-4 + (0.33775289421222619037e-4 + (-0.31087617911211617812e-6 + (-0.30179218224879386155e-4 + (0.42079421138111979198e-4 + (-0.35960405573411444377e-4 + (0.22412174312718158972e-4 + (-0.10713400205607912977e-4 + (0.39886149460845449983e-5 + (-0.11542192605244155692e-5 + (0.25556230486781413954e-6 + (-0.41938614038099184801e-7 + (0.48146397593234635691e-8 + (-0.34555062015776742157e-9 + (0.11678439676013086698e-10 + 0.72654601791530713154e-43 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
158 const Scalar c19 = 0.26689550078709988424e-5 + (-0.12701371273794086192e-4 + (0.29187128312258641044e-4 + (-0.40887625651814964910e-4 + (0.34931364376163753961e-4 + (-0.10941760706981209265e-4 + (-0.17095949246117664777e-4 + (0.33684354049709423542e-4 + (-0.34115982472168278269e-4 + (0.24496276291828666349e-4 + (-0.13463974509890800010e-4 + (0.58150160113439943742e-5 + (-0.19853509812636133521e-5 + (0.53231546936576302638e-6 + (-0.11005170539457305057e-6 + (0.16966333616170516023e-7 + (-0.18390721265526858957e-8 + (0.12516003776882922472e-9 + (-0.40260203854332223970e-11 + 0.49024697565135433977e-46 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
159 const Scalar c20 = 0.16079468293566755077e-5 + (-0.83519181950909056223e-5 + (0.21068264047704844251e-4 + (-0.32967663694395873249e-4 + (0.33192227255609240600e-4 + (-0.17505725315921772768e-4 + (-0.62507158539472975653e-5 + (0.24809896176762296083e-4 + (-0.30494472688029092428e-4 + (0.25201640916265454033e-4 + (-0.15787641075728493217e-4 + (0.77993169714226206621e-5 + (-0.30798093966460254524e-5 + (0.97237595059774941332e-6 + (-0.24310038260091801130e-6 + (0.47165155130938945170e-7 + (-0.68593186343403724861e-8 + (0.70445800732998961076e-9 + (-0.45594868193886099691e-10 + (0.13994385619395025871e-11 + 0.29893108271424045108e-49 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
160 const Scalar c21 = 0.97131634673789398878e-6 + (-0.54708225462996645305e-5 + (0.15031612868346010381e-4 + (-0.25948684686355881128e-4 + (0.29836299490113887428e-4 + (-0.20841746475330982938e-4 + (0.21041249868451872146e-5 + (0.16284262391941984951e-4 + (-0.25642616763698460772e-4 + (0.24523094611425921414e-4 + (-0.17422194113333550583e-4 + (0.97452558818057754024e-5 + (-0.43860223502643760704e-5 + (0.15981068447331646635e-5 + (-0.46976961660947401631e-6 + (0.11009357544923694910e-6 + (-0.20131669532027190606e-7 + (0.27718538806172187051e-8 + (-0.27054082137873267281e-9 + (0.16696448943448797325e-10 + (-0.49008459619564683563e-12 + 0.16552108677421951887e-52 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
161 const Scalar c22 = 0.58816866045106563105e-6 + (-0.35721527099333012576e-5 + (0.10620496275112834190e-4 + (-0.20030890513892794267e-4 + (0.25769570856134526479e-4 + (-0.21796848545887314032e-4 + (0.80432398190338473790e-5 + (0.86890833863663663181e-5 + (-0.20140605182206560763e-4 + (0.22622743926783082774e-4 + (-0.18204948546547989395e-4 + (0.11449675542311518867e-4 + (-0.58093764821307956918e-5 + (0.24063727883622600125e-5 + (-0.81496902854646487096e-6 + (0.22430060731696070044e-6 + (-0.49498828597482276521e-7 + (0.85628682699988764342e-8 + (-0.11197312381501881491e-8 + (0.10414592635951676503e-9 + (-0.61430478648550010010e-11 + (0.17279382812933012608e-12 + 0.83596508471828039833e-56 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
162 const Scalar c23 = 0.35694623785962092945e-6 + (-0.23261540519033892935e-5 + (0.74419178472078609044e-5 + (-0.15216318398635566250e-4 + (0.21588373630493424187e-4 + (-0.21126970163845042742e-4 + (0.11845455304496076792e-4 + (0.23609880626344786979e-5 + (-0.14516984266273695216e-4 + (0.19775888656621845116e-4 + (-0.18086107912040198044e-4 + (0.12736537507049307512e-4 + (-0.72266519718919009453e-5 + (0.33644119808366374648e-5 + (-0.12928827313050406050e-5 + (0.40943264368765462217e-6 + (-0.10600909150611481864e-6 + (0.22114965040891172594e-7 + (-0.36311096679932346748e-8 + (0.45222524763612305957e-9 + (-0.40179389558336936254e-10 + (0.22699473155292863497e-11 + (-0.61300713659856468819e-13 + 0.38666285139605938868e-59 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
163 const Scalar c24 = 0.21706145346345576459e-6 + (-0.15113095478470218871e-5 + (0.51776320957662205422e-5 + (-0.11403661131246235169e-4 + (0.17651639712419593863e-4 + (-0.19451656683614343464e-4 + (0.13890838328443818024e-4 + (-0.25727313272785381811e-5 + (-0.91965090082447991628e-5 + (0.16312044822824170784e-4 + (-0.17120331841076803075e-4 + (0.13482036950914841652e-4 + (-0.85057705076024007563e-5 + (0.44120031517730367803e-5 + (-0.19013595509454534312e-5 + (0.68222313948188219820e-6 + (-0.20303517093341515889e-6 + (0.49655697374466441223e-7 + (-0.98257414937286547754e-8 + (0.15356901309348980984e-8 + (-0.18261072140700804068e-9 + (0.15532593734902944723e-10 + (-0.84209778634328029841e-12 + (0.21870365650245415074e-13 + 0.16439747083165790335e-62 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
164 const Scalar c25 = 0.13224235211744936908e-6 + (-0.97997851350371097919e-6 + (0.35800640027359557107e-5 + (-0.84480240475059725402e-5 + (0.14148068359250587228e-4 + (-0.17244580119470605765e-4 + (0.14584645223195765026e-4 + (-0.61429327229030144895e-5 + (-0.44783230402391903591e-5 + (0.12562837136392852172e-4 + (-0.15443365608705532966e-4 + (0.13627399430067013885e-4 + (-0.95259580946524691274e-5 + (0.54692132382223148275e-5 + (-0.26191959740227063354e-5 + (0.10521721543189930798e-5 + (-0.35438400651509059576e-6 + (0.99538596530834004426e-7 + (-0.23075877907941536799e-7 + (0.43441556712522510653e-8 + (-0.64795864328105212055e-9 + (0.73731529915903144880e-10 + (-0.60158855648069050720e-11 + (0.31353477917777829365e-12 + (-0.78433290281573252839e-14 + 0.64469596404571726805e-66 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
165 const Scalar c26 = 0.80705592796482722655e-7 + (-0.63437141448789667973e-6 + (0.24620468370828364674e-5 + (-0.61960523928876340181e-5 + (0.11151856241067755755e-4 + (-0.14845004900719220437e-4 + (0.14307620482977119539e-4 + (-0.84853793010789473256e-5 + (-0.53744517576841498778e-6 + (0.88232406830407420128e-5 + (-0.13241570978518585723e-4 + (0.13179408729958335575e-4 + (-0.10194226086350057747e-4 + (0.64476996796559746850e-5 + (-0.34070253128267544011e-5 + (0.15180006154095517750e-5 + (-0.57168713761449777375e-6 + (0.18156038533459937192e-6 + (-0.48306691833643123879e-7 + (0.10648430194032302901e-7 + (-0.19122095699195631812e-8 + (0.27282465206711428022e-9 + (-0.29768356697676522564e-10 + (0.23340591071190063562e-11 + (-0.11712858328988494492e-12 + (0.28263287020243359521e-14 + 0.23392451525606577215e-69 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
166 const Scalar c27 = 0.49331711088852888976e-7 + (-0.41004368291703239281e-6 + (0.16850897961703399574e-5 + (-0.45047105587757996876e-5 + (0.86654922165692884461e-5 + (-0.12479329094478464909e-4 + (0.13388324258361694718e-4 + (-0.97936498514572374158e-5 + (0.25587045260909124072e-5 + (0.53280747221444300349e-5 + (-0.10721181802601462187e-4 + (0.12201152453443121452e-4 + (-0.10455670828281603489e-4 + (0.72629981428834119043e-5 + (-0.42115834110197720771e-5 + (0.20657788598905200051e-5 + (-0.86161321416614910843e-6 + (0.30565678902998530755e-6 + (-0.91884499221128689685e-7 + (0.23232167295343259066e-7 + (-0.48827798799360556781e-8 + (0.83839674785068315730e-9 + (-0.11465897079417236211e-9 + (0.12018375327114657495e-10 + (-0.90704383023344657506e-12 + (0.43891978494050177196e-13 + (-0.10229785550287397172e-14 + 0.78762463049180394663e-73 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
167 const Scalar c28 = 0.30198717233369596938e-7 + (-0.26470108300951418744e-6 + (0.11484217461677342633e-5 + (-0.32497913950022215997e-5 + (0.66506386195966268716e-5 + (-0.10285555161052384677e-4 + (0.12091874722611959811e-4 + (-0.10282092538544810074e-4 + (0.48262555421988436065e-5 + (0.22424493524723868069e-5 + (-0.80820796846961632989e-5 + (0.10796653917026920819e-4 + (-0.10296848856548453781e-4 + (0.78454106322971590499e-5 + (-0.49723665063048755405e-5 + (0.26692025174386628160e-5 + (-0.12235353625461559265e-5 + (0.48015929004768634100e-6 + (-0.16110110259511009705e-6 + (0.45993987610984026749e-7 + (-0.11082468980342148503e-7 + (0.22262384615144625788e-8 + (-0.36628003675664709291e-9 + (0.48106282932046855346e-10 + (-0.48521828528371470052e-11 + (0.35302160795771468331e-12 + (-0.16495114325484165433e-13 + (0.37178575828506531682e-15 + 0.24674957095607893065e-76 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
168 const Scalar c29 = 0.18511689126510260351e-7 + (-0.17068181783223545647e-6 + (0.77969745348337966326e-6 + (-0.23283626828310709094e-5 + (0.50491740099013299826e-5 + (-0.83365039291660230244e-5 + (0.10619684437634706088e-4 + (-0.10159658374625481776e-4 + (0.63391094420835344202e-5 + (-0.33689109880222793626e-6 + (-0.54986527825374819902e-5 + (0.90931526908756587315e-5 + (-0.97429399092296529333e-5 + (0.81477898036860525587e-5 + (-0.56292955614970245589e-5 + (0.32919777940121169458e-5 + (-0.16481984300967700802e-5 + (0.70993867832354502130e-6 + (-0.26326178871252171607e-6 + (0.83831263419606275090e-7 + (-0.22796773476702555683e-7 + (0.52479139309798472548e-8 + (-0.10097869367487702027e-8 + (0.15950303615432153340e-9 + (-0.20152759590530488405e-10 + (0.19590143225618303329e-11 + (-0.13759028734951606136e-12 + (0.62156462450904809840e-14 + (-0.13563652403372194113e-15 + 0.72106829618959360213e-80 * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta) * delta;
169 return (tail ? -1.0 : 1.0) * v * (1.0 + v2 * (c1 + v2 * (c2 + v2 * (c3 + v2 * (c4 + v2 * (c5 + v2 * (c6 + v2 * (c7 + v2 * (c8 + v2 * (c9 + v2 * (c10 + v2 * (c11 + v2 * (c12 + v2 * (c13 + v2 * (c14 + v2 * (c15 + v2 * (c16 + v2 * (c17 + v2 * (c18 + v2 * (c19 + v2 * (c20 + v2 * (c21 + v2 * (c22 + v2 * (c23 + v2 * (c24 + v2 * (c25 + v2 * (c26 + v2 * (c27 + v2 * (c28 + v2 * (c29 + v2 * c30))))))))))))))))))))))))))))));
170 }
171 // Here we try to use an asymptotic tail expansion. We limit the expansion to the fifth term as the evaluation of the d coefficients becomes unstable for moderate to large nu
172 // const Scalar d5(-nu / (nu + 2.0) * (nu + 1.0) / (nu + 2.0) * (nu + 3.0) / (nu + 2.0) * (nu + 9.0) / (nu + 2.0) * (-0.1 + (0.725 + (-1.578125 + (0.46875 + (1.22890625 + (0.353125 + 0.02734375 * nu) * nu) * nu) * nu) * nu) * nu) / ((nu + 2.0) * (nu + 4.0) * (nu + 4.0) * (nu + 6.0) * (nu + 8.0) * (nu + 10.0)));
173 const Scalar d4 = -nu / (nu + 2.0) * (nu + 1.0) / (nu + 2.0) * (nu + 7.0) / (nu + 2.0) * (0.16666666666666666667 + (-0.875 + (0.74479166666666666667 + (1.2109375 + (0.40104166666666666667 + 0.0390625 * nu) * nu) * nu) * nu) * nu) / ((nu + 2.0) * (nu + 4.0) * (nu + 4.0) * (nu + 6.0) * (nu + 8.0));
174 const Scalar z = std::pow(std::sqrt(nu) * u * normalizationFactor, 1.0 / nu);
175 const Scalar z2 = z * z;
176 // if (std::abs(d5 * std::pow(z2, 5)) < SpecFunc::Precision)
177 if (std::abs(d4 * std::pow(z2, 4)) < SpecFunc::Precision)
178 {
179 const Scalar d1 = -0.5 * (nu + 1.0) / (nu + 2.0);
180 const Scalar d2 = -0.125 * (nu / (nu + 2.0) * (nu + 1.0) / (nu + 2.0) * (nu + 3.0) / (nu + 4.0));
181 const Scalar d3 = -nu / (nu + 2.0) * (nu + 1.0) / (nu + 2.0) * (nu + 5.0) / (nu + 2.0) * (-0.041666666666666666667 + (0.14583333333333333333 + 0.0625 * nu) * nu) / ((nu + 2.0) * (nu + 6.0));
182 // const Scalar d4(-nu / (nu + 2.0) * (nu + 1.0) / (nu + 2.0) * (nu + 7.0) / (nu + 2.0) * (0.16666666666666666667 + (-0.875 + (0.74479166666666666667 + (1.2109375 + (0.40104166666666666667 + 0.0390625 * nu) * nu) * nu) * nu) * nu) / ((nu + 2.0) * (nu + 4.0) * (nu + 4.0) * (nu + 6.0) * (nu + 8.0)));
183 // const Scalar value(std::sqrt(nu) / z * (1.0 + (d1 + (d2 + (d3 + (d4 + d5 * z2) * z2) * z2) * z2) * z2));
184 const Scalar value = std::sqrt(nu) / z * (1.0 + (d1 + (d2 + (d3 + d4 * z2) * z2) * z2) * z2);
185 return (tail == (p < 0.5) ? value : -value);
186 }
187 #endif // USE_NEW_ALGO
188 // Finally, if neither the central series nor the tail series apply, use the incomplete beta inverse function
189 const Scalar omega = std::sqrt(nu * (1.0 / SpecFunc::RegularizedIncompleteBetaInverse(0.5 * nu, 0.5, 2.0 * u) - 1.0));
190 return ((p > 0.5) == tail ? -omega : omega);
191 #endif // OPENTURNS_HAVE_BOOST
192 }
193
194 /* Random number generation
195 We use a transformation method based on Gamma and Normal transformation:
196 If N is Normal(0, 1) distributed and G is Gamma(nu / 2) distributed,
197 sqrt(2 * nu) * N / sqrt(G) is distributed according to Student(nu)
198 */
StudentRealization(const Scalar nu)199 Scalar StudentRealization(const Scalar nu)
200 {
201 if (!(nu > 0.0)) throw InvalidArgumentException(HERE) << "Error: nu must be positive, here nu=" << nu;
202 const Scalar n = DistFunc::rNormal();
203 Scalar g = -1.0;
204 do
205 {
206 g = DistFunc::rGamma(0.5 * nu);
207 }
208 while (g == 0.0);
209 return std::sqrt(0.5 * nu / g) * n;
210 }
211
212 /************************************************************************************************************/
213 /* Normalized NonCentralStudent distribution, i.e. with a PDF equals to (eq. 31.15 p.516 of the reference): */
214 /* exp(-delta^2 / 2) * (nu / (nu + x^2)) ^ ((nu + 1) / 2) / (sqrt(nu * Pi) * Gamma(nu / 2)) * SUM */
215 /* where SUM = sum_0^inf Gamma((nu + k + 1) / 2) * omega^k / Gamma(k + 1) */
216 /* and omega = x * delta * sqrt(2 / (nu + x^2)) */
217 /* Reference: */
218 /* Norman L. Johnson, Samuel Kotz, N. Balakrishnan, "Continuous univariate distributions volume 2", second */
219 /* edition, 1995, Wiley Inter-Science */
220 /************************************************************************************************************/
221 /* CDF
222 We use the algorithm described in:
223 Viktor Witkovsky, "A Note on Computing Extreme Tail Probabilities of the Noncentral T Distribution with Large Noncentrality Parameter"
224 Computational Statistics & Data Analysis, 43 (2003) pp 249-267
225 */
NonCentralStudentCDF(const Scalar nu,const Scalar delta,const Scalar x,const Bool tail)226 Scalar NonCentralStudentCDF(const Scalar nu,
227 const Scalar delta,
228 const Scalar x,
229 const Bool tail)
230 {
231 // Check nu
232 if (!(nu > 0.0)) throw InvalidArgumentException(HERE) << "Error: the number of degrees of freedom nu=" << nu << " should be strictly positive.";
233 // Special case when |delta| << 1
234 if (std::abs(delta) < 4.0 * SpecFunc::Precision * nu) return DistFunc::pStudent(nu, x, tail);
235 // Very large nu
236 if (nu > 1.0 / SpecFunc::Precision) return DistFunc::pNormal(x - nu);
237 // Special case when |x| << 1
238 if (std::abs(x) < SpecFunc::Precision) return DistFunc::pNormal(-delta, tail);
239 // Small nu
240 if (nu < 20.0)
241 {
242 #ifdef OPENTURNS_HAVE_BOOST
243 return (tail ? boost::math::cdf(boost::math::complement(boost::math::non_central_t(nu, delta), x)) : boost::math::cdf(boost::math::non_central_t(nu, delta), x));
244 #else
245 return NonCentralStudentCDFAlt0(nu, delta, x, tail, SpecFunc::Precision, SpecFunc::MaximumIteration);
246 #endif
247 }
248 if (x < 0.0) return NonCentralStudentCDF(nu, -delta, -x, !tail);
249 Bool computeTail = true;
250 Bool useChiSquareTail = false;
251 if (x < delta)
252 {
253 computeTail = false;
254 useChiSquareTail = true;
255 }
256
257 /*******************************/
258 /* Integration bounds and mode */
259 /*******************************/
260 Scalar lowerBound = 0.0;
261 Scalar upperBound = 0.0;
262 Scalar mode = 0.0;
263 // const = -(log(2)+log(2*pi)/2)
264 const Scalar const1 = -1.6120857137646180667900353000732139;
265 // logRelTolBound = log(eps) eps = 2.220446049250313e-16
266 const Scalar logRelTolBound = -3.604365338911715e+01;
267 // zUpperBound > -norminv(eps(0)) = 3.847234634276879e+01
268 const Scalar zUpperBound = 38.5;
269 // tLower = log(1/(1-eps)) ~ eps
270 const Scalar tUpper(7.208730677823431e+01); // tUpper = log(1/eps^2)
271 const Scalar tLower(4.930380657631324e-32); // tLower = log(1/(1-eps^2)) ~ eps^2
272 const Scalar nuMinus2 = std::max(1.0, (nu - 2.0));
273 const Scalar xSquare = x * x;
274 const Scalar halfNu = 0.5 * nu;
275
276 // Estimate the position of the modus MOD of the FUNC
277 mode = (x * std::sqrt(4 * nu * nuMinus2 + xSquare * (delta * delta + 4 * nuMinus2)) - delta * (xSquare + 2 * nu)) / (2 * (xSquare + nu));
278 const Scalar dZ = std::min(0.5 * std::abs(mode + delta), 0.01);
279 Point dMode(3);
280 dMode[0] = mode - dZ;
281 dMode[1] = mode;
282 dMode[2] = mode + dZ;
283 const Scalar theta = nu / xSquare;
284 Point q(3);
285 q[0] = theta * std::pow(dMode[0] + delta, 2);
286 q[1] = theta * std::pow(dMode[1] + delta, 2);
287 q[2] = theta * std::pow(dMode[2] + delta, 2);
288 // Estimate the value of log(FUNC) around the mode point
289 Point logFMode(3);
290 logFMode[0] = const1 + 0.5 * (nuMinus2 * std::log(q[0] / nu) + nu - q[0] - dMode[0] * dMode[0]);
291 logFMode[1] = const1 + 0.5 * (nuMinus2 * std::log(q[1] / nu) + nu - q[1] - dMode[1] * dMode[1]);
292 logFMode[2] = const1 + 0.5 * (nuMinus2 * std::log(q[2] / nu) + nu - q[2] - dMode[2] * dMode[2]);
293 // For given logRelTolBound estimate the logAbsoluteToleranceBound
294 const Scalar logAbsoluteToleranceBound = logFMode[1] + logRelTolBound;
295 // Estimate the integration limits by quadratic approximation
296 const Scalar a = 0.5 * ((logFMode[0] - logFMode[1]) + (logFMode[2] - logFMode[1])) / (dZ * dZ);
297 const Scalar b = 0.5 * (logFMode[0] - logFMode[2]) / dZ;
298 const Scalar discriminantSqrt = std::sqrt(b * b + 4 * a * logRelTolBound);
299 const Scalar denominator = 2 * a;
300 Scalar lowerBound0 = std::max(-zUpperBound, (discriminantSqrt - b) / denominator);
301 Scalar upperBound0 = std::max(-zUpperBound, std::min(zUpperBound, -(discriminantSqrt + b) / denominator));
302 // Find zAbsoluteToleranceBound by solving: logAbsoluteToleranceBound = log(normpdf(z))
303 const Scalar zAbsoluteToleranceBound = std::min(zUpperBound, std::sqrt(-1.8378770664093454835606594728112352 - 2 * logAbsoluteToleranceBound));
304 if (!useChiSquareTail)
305 {
306 Scalar quantileUpper = 0.0;
307 // Estimate quantile of chi^2 distribution
308 // with nu degrees of freedom, see INGLOT (2010, Eqn. A.3)
309 if (nu > 1) quantileUpper = std::max(0.0, nu + 2 * tUpper + 1.62 * std::sqrt(nu * tUpper) + 0.63012 * std::sqrt(nu) * std::log(tUpper) - 1.12032 * std::sqrt(nu) - 2.48 * std::sqrt(tUpper) - 0.65381 * std::log(tUpper) - 0.22872);
310 else quantileUpper = 6.739648382445014e+01;
311 const Scalar zQuantileUpper = std::sqrt((quantileUpper / nu) * xSquare) - delta;
312 // Conservative estimate of the upper integration limit upperBound:
313 // NORMPDF is sufficiently small OR CHI2CDF is close to 1.
314 upperBound = std::min(zAbsoluteToleranceBound, zQuantileUpper);
315 // For large nu we assume approximate symmetry of FUNC
316 if ((nu > 1e4) && (mode > -zUpperBound) && (mode < zUpperBound)) lowerBound0 = mode - (upperBound - mode);
317 lowerBound = std::max(-delta, lowerBound0);
318 }
319 else
320 {
321 Scalar quantileLower = 0.0;
322 if (nu > 1) quantileLower = std::max(0.0, nu + 2 * tLower + 1.62 * std::sqrt(nu * tLower) + 0.63012 * std::sqrt(nu) * std::log(tLower) - 1.12032 * std::sqrt(nu) - 2.48 * std::sqrt(tLower) - 0.65381 * std::log(tLower) - 0.22872);
323 else quantileLower = 0.0;
324 const Scalar zQuantileLower = std::sqrt((quantileLower / nu) * xSquare) - delta;
325 const Scalar lowerBound1 = std::max(-zAbsoluteToleranceBound, zQuantileLower);
326 lowerBound = std::max(-delta, lowerBound1);
327 if ((nu > 1e4) && (mode > -zUpperBound) && (mode < zUpperBound)) upperBound0 = mode + (mode - lowerBound);
328 upperBound = upperBound0;
329 }
330 Scalar value = 0.0;
331 // First, compute the Normal contribution
332 if (useChiSquareTail) value = 0.5 * SpecFunc::ErfC(-lowerBound * 0.7071067811865475244);
333 else value = 0.5 * SpecFunc::ErfC(upperBound * 0.7071067811865475244);
334 // Second, compute the contribution of each subinterval by Gauss-Legendre integration with 15 nodes on 16 intervals
335 const GaussKronrodRule rule(GaussKronrodRule::G7K15);
336 const Scalar wg0 = rule.getZeroGaussWeight();
337 const Point wg(rule.getOtherGaussWeights());
338 const Point xg(rule.getOtherKronrodNodes());
339 const UnsignedInteger iMax = 8;
340 const Scalar dLowerBound = (mode - lowerBound) / iMax;
341 const Scalar wLowerBound = 0.5 * dLowerBound;
342 const Scalar dUpperBound = (mode - upperBound) / iMax;
343 const Scalar wUpperBound = 0.5 * dUpperBound;
344 const Scalar omega = nu / (2.0 * xSquare);
345 for (UnsignedInteger i = 0; i < iMax; ++i)
346 {
347 // Ith interval at the left of the mode
348 const Scalar ci = lowerBound + (i + 0.5) * dLowerBound;
349 Scalar contributionLeft = wg0 * DistFunc::pGamma(halfNu, omega * std::pow(ci + delta, 2), useChiSquareTail) * DistFunc::dNormal(ci);
350 // Ith interval at the right of the mode
351 const Scalar xii = upperBound + (i + 0.5) * dUpperBound;
352 Scalar contributionRight = wg0 * DistFunc::pGamma(halfNu, omega * std::pow(xii + delta, 2), useChiSquareTail) * DistFunc::dNormal(xii);
353 for (UnsignedInteger j = 0; j < xg.getSize() / 2; ++j)
354 {
355 const Scalar zetaj = xg[2 * j + 1];
356 // Contribution of the left interval
357 const Scalar zj_m = ci - wLowerBound * zetaj;
358 const Scalar zj_p = ci + wLowerBound * zetaj;
359 contributionLeft += wg[j] * (DistFunc::pGamma(halfNu, omega * std::pow(zj_m + delta, 2), useChiSquareTail) * DistFunc::dNormal(zj_m) + DistFunc::pGamma(halfNu, omega * std::pow(zj_p + delta, 2), useChiSquareTail) * DistFunc::dNormal(zj_p));
360 // Contribution of the right interval
361 const Scalar zetaj_m = xii - wUpperBound * zetaj;
362 const Scalar zetaj_p = xii + wUpperBound * zetaj;
363 contributionRight += wg[j] * (DistFunc::pGamma(halfNu, omega * std::pow(zetaj_m + delta, 2), useChiSquareTail) * DistFunc::dNormal(zetaj_m) + DistFunc::pGamma(halfNu, omega * std::pow(zetaj_p + delta, 2), useChiSquareTail) * DistFunc::dNormal(zetaj_p));
364 } // Loop over j, the integration points
365 value += contributionLeft * wLowerBound - contributionRight * wUpperBound;
366 } // Loop over i, the integration subintervals
367 // Set the values of the CDF and CCDF
368 return (computeTail == tail ? value : 0.5 + (0.5 - value));
369 }
370
371 /* CDF Alt0
372 We use the algorithm described in:
373 Denise Benton, K. Krishnamoorthy, "Computing discrete mixtures of continuous distributions: noncentral chisquare, noncentral t
374 and the distribution of the square of the sample multiple correlation coefficient",
375 Computational Statistics & Data Analysis, 43 (2003) pp 249-267
376 */
NonCentralStudentCDFAlt0(const Scalar nu,const Scalar delta,const Scalar x,const Bool tail,const Scalar precision,const UnsignedInteger maximumIteration)377 Scalar NonCentralStudentCDFAlt0(const Scalar nu,
378 const Scalar delta,
379 const Scalar x,
380 const Bool tail,
381 const Scalar precision,
382 const UnsignedInteger maximumIteration)
383 {
384 // Check nu
385 if (!(nu > 0.0)) throw InvalidArgumentException(HERE) << "Error: the number of degrees of freedom nu=" << nu << " should be strictly positive.";
386 // Special case when |delta| << 1
387 if (std::abs(delta / (4.0 * nu)) < precision) return DistFunc::pStudent(nu, x, tail);
388 // Very large nu
389 if (nu > 1.0 / precision) return DistFunc::pNormal(x - nu);
390 // Special case when |x| << 1
391 if (std::abs(x) < precision) return DistFunc::pNormal(-delta, tail);
392 Scalar t = x;
393 Scalar del = delta;
394 // Must use the complementary function for negative arguments
395 if (x < 0.0)
396 {
397 t = -t;
398 del = -del;
399 }
400 // Some useful quantities
401 const Scalar x2 = t * t;
402 const Scalar xi = x2 / (nu + x2);
403 const Scalar logXi = std::log(xi);
404 const Scalar halfNu = 0.5 * nu;
405 const Scalar halfDelta2 = 0.5 * del * del;
406 const Scalar logHalfDelta2 = std::log(halfDelta2);
407 // Starting index in the sum: integral part of halfDelta2 and insure that it is at least 1
408 const UnsignedInteger k = std::max(1UL, static_cast<UnsignedInteger>(floor(halfDelta2)));
409 // Index of the forward iterations
410 UnsignedInteger kForward = k;
411 // Index of the backward iterations
412 UnsignedInteger kBackward = k;
413 LOGDEBUG(OSS() << "kForward=" << kForward << "kBackward=" << kBackward);
414 // Terms and factors of the summation.
415 // The initialization corresponds to the terme of index k.
416 const Scalar commonExponent = -halfDelta2 + k * logHalfDelta2;
417 LOGDEBUG(OSS() << "commonExponent=" << commonExponent);
418 Scalar pForward = 0.5 * std::exp(commonExponent - SpecFunc::LnGamma(k + 1));
419 Scalar qForward = 0.5 * del / M_SQRT2 * std::exp(commonExponent - SpecFunc::LnGamma(k + 1.5));
420 LOGDEBUG(OSS() << "pForward=" << pForward << ", qForward=" << qForward);
421 Scalar betaPForward = DistFunc::pBeta(k + 0.5, halfNu, xi);
422 Scalar betaQForward = DistFunc::pBeta(k + 1, halfNu, xi);
423 LOGDEBUG(OSS() << "betaPForward=" << betaPForward << ", betaQForward=" << betaQForward);
424 // The correction factors will be updated at the beginning of each iteration: it is the quantity to add to
425 // the corresponding betaP and betaQ factors to go to the value associated with the current iteration.
426 // They are thus initialized such that after one update, they will change the betaP and betaQ factors
427 // to their values associated with the iteration (k-1):
428 const Scalar commonFactor = (k - 0.5) * logXi + halfNu * std::log(nu / (nu + x2)) - SpecFunc::LnGamma(halfNu);
429 LOGDEBUG(OSS() << "commonFactor=" << commonFactor);
430 // correctionBetaPForward = Gamma(k - 1/2 + nu/2) / Gamma(k + 1/2) / Gamma(nu/2) * xi^(k - 1/2) * (1 - xi)^(nu/2)
431 Scalar correctionBetaPForward = -std::exp(SpecFunc::LnGamma(k - 0.5 + halfNu) - SpecFunc::LnGamma(k + 0.5) + commonFactor);
432 // correctionBetaPForward = Gamma(k + nu/2) / Gamma(k + 1) / Gamma(nu/2) * xi^k * (1 - xi)^(nu/2)
433 Scalar correctionBetaQForward = -std::exp(SpecFunc::LnGamma(k + halfNu) - SpecFunc::LnGamma(k + 1) + commonFactor + 0.5 * logXi);
434 LOGDEBUG(OSS() << "correctionBetaPForward=" << correctionBetaPForward << ", correctionBetaQForward=" << correctionBetaQForward);
435 Scalar pBackward = pForward;
436 Scalar qBackward = qForward;
437 Scalar betaPBackward = betaPForward;
438 Scalar betaQBackward = betaQForward;
439 // The correction factors will be updated at the beginning of each iteration: it is the quantity to add to
440 // the corresponding betaP and betaQ factors to go to the value associated with the current iteration.
441 // They are thus initialized such that after one update, they will change the betaP and betaQ factors
442 // to their values associated with the iteration (k+1):
443 // correctionBetaPBackward = Gamma(k + 1/2 + nu/2) / Gamma(k + 3/2 + nu/2) / Gamma(nu/2) * xi^(k + 1/2) * (1 - xi)^(nu/2)
444 Scalar correctionBetaPBackward = -correctionBetaPForward * xi * (k - 0.5 + halfNu) / (k + 0.5);
445 Scalar correctionBetaQBackward = -correctionBetaQForward * xi * (k + halfNu) / (k + 1);
446 LOGDEBUG(OSS() << "correctionBetaPBackward=" << correctionBetaPBackward << ", correctionBetaQBackward=" << correctionBetaQBackward);
447 Scalar value = DistFunc::pNormal(-del) + pForward * betaPForward + qForward * betaQForward;
448 Scalar contributionForward = 0.0;
449 Scalar contributionBackward = 0.0;
450 Scalar error = SpecFunc::MaxScalar;
451 // At the beginning of the iteration, kForward and kBackward store the index of the last terms
452 // that have already been accumulated. Each iteration must update the P and Q factors, as well
453 // as the betaP and betaQ factors. For this last update, one must update the corresponding
454 // correction factors. Thus, we proceed as follows:
455 // First, update the correction factors for the betaP and betaQ factors
456 // Second, update the betaP and betaQ factors
457 // Third, upate the P and Q factors
458 // Fourth, accumulate the current contribution
459 // It is the responsibility of the main iteration to update kForward and kBackward
460 #define FORWARD_ITERATION \
461 correctionBetaPForward *= xi * (kForward - 0.5 + halfNu) / (kForward + 0.5); \
462 correctionBetaQForward *= xi * (kForward + halfNu) / (kForward + 1); \
463 pForward *= halfDelta2 / (kForward + 1); \
464 betaPForward += correctionBetaPForward; \
465 qForward *= halfDelta2 / (kForward + 1.5); \
466 betaQForward += correctionBetaQForward; \
467 contributionForward = pForward * betaPForward + qForward * betaQForward; \
468 value += contributionForward;
469
470 #define BACKWARD_ITERATION \
471 correctionBetaPBackward *= (kBackward + 0.5) / (xi * (kBackward - 0.5 + halfNu)); \
472 correctionBetaQBackward *= (kBackward + 1) / (xi * (kBackward + halfNu)); \
473 pBackward *= kBackward / halfDelta2; \
474 betaPBackward += correctionBetaPBackward; \
475 qBackward *= (kBackward + 0.5) / halfDelta2; \
476 betaQBackward += correctionBetaQBackward; \
477 contributionBackward = pBackward * betaPBackward + qBackward * betaQBackward; \
478 value += contributionBackward;
479
480 // Here, i is an UnsignedInteger as it is only a loop counter
481 UnsignedInteger i = 1;
482
483 const UnsignedInteger imax = std::min(k, maximumIteration);
484 while((error > 0.0) && (i <= imax))
485 {
486 FORWARD_ITERATION;
487 BACKWARD_ITERATION;
488 error = contributionForward + contributionBackward;
489 ++kForward;
490 --kBackward;
491 ++i;
492 }
493 // Do we have to perform further forward iterations?
494 while ((error > 0.0) && (i <= maximumIteration))
495 {
496 FORWARD_ITERATION;
497 error = contributionForward;
498 ++kForward;
499 ++i;
500 }
501 #undef FORWARD_ITERATION
502 #undef BACKWARD_ITERATION
503 if (error > precision * (std::abs(value) + precision)) LOGWARN(OSS() << "Warning: in NonCentralStudentAlt0(nu, delta, x), no convergence after " << i << " iterations. Error is " << error * value << " value is " << value << " for nu=" << nu << ", delta=" << delta << " and x=" << x);
504 // Clip to [0,1] in order to get rid of small rounding error
505 value = (value < 0.0 ? 0.0 : (value > 1.0 ? 1.0 : value));
506 // Check if we had to change the sign of the argument or if we are asked for the tail CDF
507 if ((tail && (x > 0.0)) || (!tail && (x < 0.0))) value = 0.5 + (0.5 - value);
508 return value;
509 }
510
511 /* PDF
512 We use the relation between the PDF and the CDF in order to reduce the computation of the PDF to two computations of the CDF */
NonCentralStudentPDF(const Scalar nu,const Scalar delta,const Scalar x)513 Scalar NonCentralStudentPDF(const Scalar nu,
514 const Scalar delta,
515 const Scalar x)
516 {
517 // Check nu
518 if (!(nu > 0.0)) throw InvalidArgumentException(HERE) << "Error: the number of degrees of freedom nu=" << nu << " should be strictly positive.";
519 // Early exit for delta == 0, central Student PDF
520 if (std::abs(delta / (4.0 * nu)) < SpecFunc::Precision) return std::exp(SpecFunc::LnGamma(0.5 * nu + 0.5) - SpecFunc::LnGamma(0.5 * nu) - 0.5 * std::log(M_PI * nu) + (0.5 * nu + 0.5) * std::log(nu / (nu + x * x)));
521 if (std::abs(x) < SpecFunc::Precision) return std::exp(SpecFunc::LnGamma(0.5 * nu + 0.5) - SpecFunc::LnGamma(0.5 * nu) - 0.5 * std::log(M_PI * nu) - 0.5 * delta * delta);
522 return std::max(0.0, nu / x * (NonCentralStudentCDF(nu + 2, delta, x * std::sqrt(1.0 + 2.0 / nu)) - NonCentralStudentCDF(nu, delta, x)));
523 }
524
525 /************************************************************************************************************/
526 /* Normalized NonCentralStudent distribution, i.e. with a PDF equals to (eq. 31.15 p.516 of the reference): */
527 /* exp(-delta^2 / 2) * (nu / (nu + x^2)) ^ ((nu + 1) / 2) / (sqrt(nu * Pi) * Gamma(nu / 2)) * SUM */
528 /* where SUM = sum_0^inf Gamma((nu + k + 1) / 2) * omega^k / Gamma(k + 1) */
529 /* and omega = x * delta * sqrt(2 / (nu + x^2)) */
530 /* In order to derive simple update formulas for the terms in the sum, we separate the odd indices from the */
531 /* even ones: */
532 /* SUM = SUM_ODD + SUM_EVEN, where: */
533 /* SUM_ODD = sum_0^inf Gamma(nu / 2 + k + 1) * omega * z^k / Gamma(2 * k + 2) */
534 /* SUM_EVEN = sum_O^inf Gamma((nu + 1) / 2 + k) * z^k / Gamma(2 * k + 1) */
535 /* and z = omega^2 */
536 /* The summation is done starting at the kM chosen as for the NonCentralStudentCDFAlt0 method. */
537 /* Reference: */
538 /* Norman L. Johnson, Samuel Kotz, N. Balakrishnan, "Continuous univariate distributions volume 2", second */
539 /* edition, 1995, Wiley Inter-Science */
540 /************************************************************************************************************/
NonCentralStudentPDFAlt0(const Scalar nu,const Scalar delta,const Scalar x,const Scalar precision,const UnsignedInteger maximumIteration)541 Scalar NonCentralStudentPDFAlt0(const Scalar nu,
542 const Scalar delta,
543 const Scalar x,
544 const Scalar precision,
545 const UnsignedInteger maximumIteration)
546 {
547 // Check nu
548 if (!(nu > 0.0)) throw InvalidArgumentException(HERE) << "Error: the number of degrees of freedom nu=" << nu << " should be strictly positive.";
549 // Early exit for delta == 0, central Student PDF
550 if (std::abs(delta / (4.0 * nu)) < precision) return std::exp(SpecFunc::LnGamma(0.5 * nu + 0.5) - SpecFunc::LnGamma(0.5 * nu) - 0.5 * std::log(M_PI * nu) + (0.5 * nu + 0.5) * std::log(nu / (nu + x * x)));
551 // Case delta <> 0
552 #ifdef OPENTURNS_HAVE_BOOST
553 (void) maximumIteration;
554 return boost::math::pdf(boost::math::non_central_t(nu, delta), x);
555 #else
556 const Scalar halfNu = 0.5 * nu;
557 const Scalar halfNup1_2 = halfNu + 0.5;
558 const Scalar logConstant = -0.5 * delta * delta - SpecFunc::LnGamma(halfNu) - 0.5 * std::log(M_PI * nu);
559 // Early exit for x == 0
560 if (std::abs(x) < precision) return std::exp(logConstant + SpecFunc::LnGamma(halfNup1_2));
561 // For x <> 0
562 const Scalar x2 = x * x;
563 const Scalar w = 1.0 / (nu + x2);
564 Scalar logFactor = logConstant + halfNup1_2 * std::log(nu * w);
565 // Special treatment for very low value to avoid NaNs due to 0.Inf
566 static const Scalar logPrecision(std::log(precision));
567 if (logFactor < logPrecision)
568 {
569 Scalar value = 0.0;
570 if (x < 0.0) value = nu / x * (NonCentralStudentCDF(nu + 2.0, delta, x * std::sqrt(1.0 + 2.0 / nu), false) - NonCentralStudentCDF(nu, delta, x, false));
571 else value = -nu / x * (NonCentralStudentCDF(nu + 2.0, -delta, -x * std::sqrt(1.0 + 2.0 / nu), false) - NonCentralStudentCDF(nu, -delta, -x));
572 return std::max(0.0, value);
573 }
574
575 const Scalar omega = delta * x * std::sqrt(2 * w);
576 const Scalar z = omega * omega;
577 // Start at even index that maximize the coefficient in the sum
578 const Scalar halfDelta2 = 0.5 * delta * delta;
579 // Starting index in the sum: integral part of halfDelta2 and insure that it is at least 1
580 const UnsignedInteger k = std::max(1UL, static_cast<UnsignedInteger>(floor(halfDelta2)));
581 // Loop forward and backward starting from k
582 // Initialization
583 const Scalar kLogZ = k * std::log(z);
584 Scalar pForwardEven = std::exp(logFactor + SpecFunc::LnGamma(halfNup1_2 + k) - SpecFunc::LnGamma(2 * k + 1) + kLogZ);
585 Scalar pForwardOdd = omega * std::exp(logFactor + SpecFunc::LnGamma(halfNu + k + 1) - SpecFunc::LnGamma(2 * k + 2) + kLogZ);
586 Scalar pBackwardEven = pForwardEven;
587 Scalar pBackwardOdd = pForwardOdd;
588 Scalar value = pForwardOdd + pForwardEven;
589 Scalar error = SpecFunc::MaxScalar;
590 UnsignedInteger kForward = k;
591 UnsignedInteger kBackward = k;
592 #define FORWARD_ITERATION \
593 pForwardOdd *= (halfNu + kForward + 1) * z / (2 * (kForward + 1) * (2 * kForward + 3)); \
594 pForwardEven *= (halfNup1_2 + kForward) * z / (2 * (kForward + 1) * (2 * kForward + 1)); \
595 value += pForwardOdd + pForwardEven;
596 #define BACKWARD_ITERATION \
597 pBackwardOdd *= 2 * kBackward * (2 * kBackward + 1) / (z * (halfNu + kBackward)); \
598 pBackwardEven *= 2 * kBackward * (2 * kBackward - 1) / (z * (halfNup1_2 + kBackward - 1)); \
599 value += pBackwardOdd + pBackwardEven;
600
601 // Here, i is an UnsignedInteger as it is only a loop counter
602 UnsignedInteger i = 1;
603 const UnsignedInteger imax = std::min(k, maximumIteration);
604 // while((error > precision * (std::abs(value) + precision)) && (i <= imax))
605 while((error > 0.0) && (i <= imax))
606 {
607 FORWARD_ITERATION;
608 BACKWARD_ITERATION;
609 error = pForwardOdd + pBackwardOdd + pForwardEven + pBackwardEven;
610 ++kForward;
611 --kBackward;
612 ++i;
613 }
614 // Do we have to perform further forward iterations?
615 // while ((error > precision * (std::abs(value) + precision)) && (i <= MaximumIteration))
616 while ((error > 0.0) && (i <= maximumIteration))
617 {
618 FORWARD_ITERATION;
619 error = pForwardOdd + pForwardEven;
620 ++kForward;
621 ++i;
622 }
623 #undef FORWARD_ITERATION
624 #undef BACKWARD_ITERATION
625 if (error > precision * (std::abs(value) + precision)) LOGWARN(OSS() << "Warning: in NonCentralStudentPDFAlt0(nu, delta, x), no convergence after " << i << " iterations. Error is " << error * value << " value is " << value << " for nu=" << nu << ", delta=" << delta << " and x=" << x);
626 // Clip to [0,+inf[ in order to get rid of small rounding error
627 return (value <= 0.0 ? 0.0 : value);
628 #endif
629 }
630
631 /* Random number generation
632 We use a transformation method based on Gamma and Normal transformation:
633 If N is Normal(delta, 1) distributed and G is Gamma(nu / 2) distributed,
634 sqrt(2 * nu) * N / sqrt(G) is distributed according to NonCentralStudent(nu, delta)
635 */
NonCentralStudentRealization(const Scalar nu,const Scalar delta)636 Scalar NonCentralStudentRealization(const Scalar nu,
637 const Scalar delta)
638 {
639 const Scalar n = DistFunc::rNormal() + delta;
640 Scalar g = -1.0;
641 do
642 {
643 g = DistFunc::rGamma(0.5 * nu);
644 }
645 while (g == 0.0);
646 return std::sqrt(0.5 * nu / g) * n;
647 }
648
649 } // StudentFunctions
650
651 END_NAMESPACE_OPENTURNS
652