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