File indexing completed on 2025-07-02 08:16:41
0001
0002
0003
0004
0005
0006
0007
0008 #ifndef BOOST_STATS_INVERSE_GAUSSIAN_HPP
0009 #define BOOST_STATS_INVERSE_GAUSSIAN_HPP
0010
0011 #ifdef _MSC_VER
0012 #pragma warning(disable: 4512)
0013 #endif
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053 #include <boost/math/special_functions/erf.hpp> // for erf/erfc.
0054 #include <boost/math/distributions/complement.hpp>
0055 #include <boost/math/distributions/detail/common_error_handling.hpp>
0056 #include <boost/math/distributions/normal.hpp>
0057 #include <boost/math/distributions/gamma.hpp> // for gamma function
0058
0059 #include <boost/math/tools/tuple.hpp>
0060 #include <boost/math/tools/roots.hpp>
0061
0062 #include <utility>
0063
0064 namespace boost{ namespace math{
0065
0066 template <class RealType = double, class Policy = policies::policy<> >
0067 class inverse_gaussian_distribution
0068 {
0069 public:
0070 using value_type = RealType;
0071 using policy_type = Policy;
0072
0073 explicit inverse_gaussian_distribution(RealType l_mean = 1, RealType l_scale = 1)
0074 : m_mean(l_mean), m_scale(l_scale)
0075 {
0076 static const char* function = "boost::math::inverse_gaussian_distribution<%1%>::inverse_gaussian_distribution";
0077
0078 RealType result;
0079 detail::check_scale(function, l_scale, &result, Policy());
0080 detail::check_location(function, l_mean, &result, Policy());
0081 detail::check_x_gt0(function, l_mean, &result, Policy());
0082 }
0083
0084 RealType mean()const
0085 {
0086 return m_mean;
0087 }
0088
0089
0090 RealType location()const
0091 {
0092 return m_mean;
0093 }
0094 RealType scale()const
0095 {
0096 return m_scale;
0097 }
0098
0099 RealType shape()const
0100 {
0101 return m_scale / m_mean;
0102 }
0103
0104 private:
0105
0106
0107
0108 RealType m_mean;
0109 RealType m_scale;
0110 };
0111
0112 using inverse_gaussian = inverse_gaussian_distribution<double>;
0113
0114 #ifdef __cpp_deduction_guides
0115 template <class RealType>
0116 inverse_gaussian_distribution(RealType)->inverse_gaussian_distribution<typename boost::math::tools::promote_args<RealType>::type>;
0117 template <class RealType>
0118 inverse_gaussian_distribution(RealType,RealType)->inverse_gaussian_distribution<typename boost::math::tools::promote_args<RealType>::type>;
0119 #endif
0120
0121 template <class RealType, class Policy>
0122 inline std::pair<RealType, RealType> range(const inverse_gaussian_distribution<RealType, Policy>& )
0123 {
0124 using boost::math::tools::max_value;
0125 return std::pair<RealType, RealType>(static_cast<RealType>(0.), max_value<RealType>());
0126 }
0127
0128 template <class RealType, class Policy>
0129 inline std::pair<RealType, RealType> support(const inverse_gaussian_distribution<RealType, Policy>& )
0130 {
0131
0132 using boost::math::tools::max_value;
0133 return std::pair<RealType, RealType>(static_cast<RealType>(0.), max_value<RealType>());
0134 }
0135
0136 template <class RealType, class Policy>
0137 inline RealType pdf(const inverse_gaussian_distribution<RealType, Policy>& dist, const RealType& x)
0138 {
0139 BOOST_MATH_STD_USING
0140
0141 RealType scale = dist.scale();
0142 RealType mean = dist.mean();
0143 RealType result = 0;
0144 static const char* function = "boost::math::pdf(const inverse_gaussian_distribution<%1%>&, %1%)";
0145 if(false == detail::check_scale(function, scale, &result, Policy()))
0146 {
0147 return result;
0148 }
0149 if(false == detail::check_location(function, mean, &result, Policy()))
0150 {
0151 return result;
0152 }
0153 if(false == detail::check_x_gt0(function, mean, &result, Policy()))
0154 {
0155 return result;
0156 }
0157 if(false == detail::check_positive_x(function, x, &result, Policy()))
0158 {
0159 return result;
0160 }
0161
0162 if (x == 0)
0163 {
0164 return 0;
0165 }
0166
0167 result =
0168 sqrt(scale / (constants::two_pi<RealType>() * x * x * x))
0169 * exp(-scale * (x - mean) * (x - mean) / (2 * x * mean * mean));
0170 return result;
0171 }
0172
0173 template <class RealType, class Policy>
0174 inline RealType logpdf(const inverse_gaussian_distribution<RealType, Policy>& dist, const RealType& x)
0175 {
0176 BOOST_MATH_STD_USING
0177
0178 RealType scale = dist.scale();
0179 RealType mean = dist.mean();
0180 RealType result = -std::numeric_limits<RealType>::infinity();
0181 static const char* function = "boost::math::logpdf(const inverse_gaussian_distribution<%1%>&, %1%)";
0182 if(false == detail::check_scale(function, scale, &result, Policy()))
0183 {
0184 return result;
0185 }
0186 if(false == detail::check_location(function, mean, &result, Policy()))
0187 {
0188 return result;
0189 }
0190 if(false == detail::check_x_gt0(function, mean, &result, Policy()))
0191 {
0192 return result;
0193 }
0194 if(false == detail::check_positive_x(function, x, &result, Policy()))
0195 {
0196 return result;
0197 }
0198
0199 if (x == 0)
0200 {
0201 return std::numeric_limits<RealType>::quiet_NaN();
0202 }
0203
0204 const RealType two_pi = boost::math::constants::two_pi<RealType>();
0205
0206 result = (-scale*pow(mean - x, RealType(2))/(mean*mean*x) + log(scale) - 3*log(x) - log(two_pi)) / 2;
0207 return result;
0208 }
0209
0210 template <class RealType, class Policy>
0211 inline RealType cdf(const inverse_gaussian_distribution<RealType, Policy>& dist, const RealType& x)
0212 {
0213 BOOST_MATH_STD_USING
0214
0215 RealType scale = dist.scale();
0216 RealType mean = dist.mean();
0217 static const char* function = "boost::math::cdf(const inverse_gaussian_distribution<%1%>&, %1%)";
0218 RealType result = 0;
0219 if(false == detail::check_scale(function, scale, &result, Policy()))
0220 {
0221 return result;
0222 }
0223 if(false == detail::check_location(function, mean, &result, Policy()))
0224 {
0225 return result;
0226 }
0227 if (false == detail::check_x_gt0(function, mean, &result, Policy()))
0228 {
0229 return result;
0230 }
0231 if(false == detail::check_positive_x(function, x, &result, Policy()))
0232 {
0233 return result;
0234 }
0235 if (x == 0)
0236 {
0237 return 0;
0238 }
0239
0240
0241
0242
0243 normal_distribution<RealType> n01;
0244
0245 RealType n0 = sqrt(scale / x);
0246 n0 *= ((x / mean) -1);
0247 RealType n1 = cdf(n01, n0);
0248 RealType expfactor = exp(2 * scale / mean);
0249 RealType n3 = - sqrt(scale / x);
0250 n3 *= (x / mean) + 1;
0251 RealType n4 = cdf(n01, n3);
0252 result = n1 + expfactor * n4;
0253 return result;
0254 }
0255
0256 template <class RealType, class Policy>
0257 struct inverse_gaussian_quantile_functor
0258 {
0259
0260 inverse_gaussian_quantile_functor(const boost::math::inverse_gaussian_distribution<RealType, Policy> dist, RealType const& p)
0261 : distribution(dist), prob(p)
0262 {
0263 }
0264 boost::math::tuple<RealType, RealType> operator()(RealType const& x)
0265 {
0266 RealType c = cdf(distribution, x);
0267 RealType fx = c - prob;
0268 RealType dx = pdf(distribution, x);
0269
0270 return boost::math::make_tuple(fx, dx);
0271 }
0272 private:
0273 const boost::math::inverse_gaussian_distribution<RealType, Policy> distribution;
0274 RealType prob;
0275 };
0276
0277 template <class RealType, class Policy>
0278 struct inverse_gaussian_quantile_complement_functor
0279 {
0280 inverse_gaussian_quantile_complement_functor(const boost::math::inverse_gaussian_distribution<RealType, Policy> dist, RealType const& p)
0281 : distribution(dist), prob(p)
0282 {
0283 }
0284 boost::math::tuple<RealType, RealType> operator()(RealType const& x)
0285 {
0286 RealType c = cdf(complement(distribution, x));
0287 RealType fx = c - prob;
0288 RealType dx = -pdf(distribution, x);
0289
0290
0291 return boost::math::make_tuple(fx, dx);
0292 }
0293 private:
0294 const boost::math::inverse_gaussian_distribution<RealType, Policy> distribution;
0295 RealType prob;
0296 };
0297
0298 namespace detail
0299 {
0300 template <class RealType>
0301 inline RealType guess_ig(RealType p, RealType mu = 1, RealType lambda = 1)
0302 {
0303 BOOST_MATH_STD_USING
0304 using boost::math::policies::policy;
0305
0306 using boost::math::policies::overflow_error;
0307
0308 using boost::math::policies::ignore_error;
0309
0310 using no_overthrow_policy = policy<overflow_error<ignore_error>>;
0311
0312 RealType x;
0313 RealType phi = lambda / mu;
0314 if (phi > 2.)
0315 {
0316
0317
0318
0319
0320
0321
0322 normal_distribution<RealType, no_overthrow_policy> n01;
0323 x = mu * exp(quantile(n01, p) / sqrt(phi) - 1/(2 * phi));
0324 }
0325 else
0326 {
0327
0328 using boost::math::gamma_distribution;
0329
0330
0331 using gamma_nooverflow = gamma_distribution<RealType, no_overthrow_policy>;
0332
0333 gamma_nooverflow g(static_cast<RealType>(0.5), static_cast<RealType>(1.));
0334
0335
0336 RealType qg = quantile(complement(g, p));
0337 x = lambda / (qg * 2);
0338
0339 if (x > mu/2)
0340 {
0341
0342 RealType q = quantile(g, p);
0343
0344
0345 x = mu * exp(q / sqrt(phi) - 1/(2 * phi));
0346 }
0347 }
0348 return x;
0349 }
0350 }
0351
0352 template <class RealType, class Policy>
0353 inline RealType quantile(const inverse_gaussian_distribution<RealType, Policy>& dist, const RealType& p)
0354 {
0355 BOOST_MATH_STD_USING
0356
0357
0358 RealType mean = dist.mean();
0359 RealType scale = dist.scale();
0360 static const char* function = "boost::math::quantile(const inverse_gaussian_distribution<%1%>&, %1%)";
0361
0362 RealType result = 0;
0363 if(false == detail::check_scale(function, scale, &result, Policy()))
0364 return result;
0365 if(false == detail::check_location(function, mean, &result, Policy()))
0366 return result;
0367 if (false == detail::check_x_gt0(function, mean, &result, Policy()))
0368 return result;
0369 if(false == detail::check_probability(function, p, &result, Policy()))
0370 return result;
0371 if (p == 0)
0372 {
0373 return 0;
0374 }
0375 if (p == 1)
0376 {
0377 result = policies::raise_overflow_error<RealType>(function,
0378 "probability parameter is 1, but must be < 1!", Policy());
0379 return result;
0380 }
0381
0382 RealType guess = detail::guess_ig(p, dist.mean(), dist.scale());
0383 using boost::math::tools::max_value;
0384
0385 RealType min = static_cast<RealType>(0);
0386 RealType max = max_value<RealType>();
0387
0388
0389
0390 int get_digits = policies::digits<RealType, Policy>();
0391 std::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
0392 using boost::math::tools::newton_raphson_iterate;
0393 result =
0394 newton_raphson_iterate(inverse_gaussian_quantile_functor<RealType, Policy>(dist, p), guess, min, max, get_digits, max_iter);
0395 if (max_iter >= policies::get_max_root_iterations<Policy>())
0396 {
0397 return policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:"
0398 " either there is no answer to quantile or the answer is infinite. Current best guess is %1%", result, Policy());
0399 }
0400 return result;
0401 }
0402
0403 template <class RealType, class Policy>
0404 inline RealType cdf(const complemented2_type<inverse_gaussian_distribution<RealType, Policy>, RealType>& c)
0405 {
0406 BOOST_MATH_STD_USING
0407
0408 RealType scale = c.dist.scale();
0409 RealType mean = c.dist.mean();
0410 RealType x = c.param;
0411 static const char* function = "boost::math::cdf(const complement(inverse_gaussian_distribution<%1%>&), %1%)";
0412
0413 RealType result = 0;
0414 if(false == detail::check_scale(function, scale, &result, Policy()))
0415 return result;
0416 if(false == detail::check_location(function, mean, &result, Policy()))
0417 return result;
0418 if (false == detail::check_x_gt0(function, mean, &result, Policy()))
0419 return result;
0420 if(false == detail::check_positive_x(function, x, &result, Policy()))
0421 return result;
0422
0423 normal_distribution<RealType> n01;
0424 RealType n0 = sqrt(scale / x);
0425 n0 *= ((x / mean) -1);
0426 RealType cdf_1 = cdf(complement(n01, n0));
0427
0428 RealType expfactor = exp(2 * scale / mean);
0429 RealType n3 = - sqrt(scale / x);
0430 n3 *= (x / mean) + 1;
0431
0432
0433 RealType n6 = cdf(complement(n01, +sqrt(scale/x) * ((x /mean) + 1)));
0434
0435 result = cdf_1 - expfactor * n6;
0436 return result;
0437 }
0438
0439 template <class RealType, class Policy>
0440 inline RealType quantile(const complemented2_type<inverse_gaussian_distribution<RealType, Policy>, RealType>& c)
0441 {
0442 BOOST_MATH_STD_USING
0443
0444 RealType scale = c.dist.scale();
0445 RealType mean = c.dist.mean();
0446 static const char* function = "boost::math::quantile(const complement(inverse_gaussian_distribution<%1%>&), %1%)";
0447 RealType result = 0;
0448 if(false == detail::check_scale(function, scale, &result, Policy()))
0449 return result;
0450 if(false == detail::check_location(function, mean, &result, Policy()))
0451 return result;
0452 if (false == detail::check_x_gt0(function, mean, &result, Policy()))
0453 return result;
0454 RealType q = c.param;
0455 if(false == detail::check_probability(function, q, &result, Policy()))
0456 return result;
0457
0458 RealType guess = detail::guess_ig(q, mean, scale);
0459
0460 using boost::math::tools::max_value;
0461
0462 RealType min = static_cast<RealType>(0);
0463 RealType max = max_value<RealType>();
0464
0465
0466 int get_digits = policies::digits<RealType, Policy>();
0467 std::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
0468 using boost::math::tools::newton_raphson_iterate;
0469 result = newton_raphson_iterate(inverse_gaussian_quantile_complement_functor<RealType, Policy>(c.dist, q), guess, min, max, get_digits, max_iter);
0470 if (max_iter >= policies::get_max_root_iterations<Policy>())
0471 {
0472 return policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:"
0473 " either there is no answer to quantile or the answer is infinite. Current best guess is %1%", result, Policy());
0474 }
0475 return result;
0476 }
0477
0478 template <class RealType, class Policy>
0479 inline RealType mean(const inverse_gaussian_distribution<RealType, Policy>& dist)
0480 {
0481 return dist.mean();
0482 }
0483
0484 template <class RealType, class Policy>
0485 inline RealType scale(const inverse_gaussian_distribution<RealType, Policy>& dist)
0486 {
0487 return dist.scale();
0488 }
0489
0490 template <class RealType, class Policy>
0491 inline RealType shape(const inverse_gaussian_distribution<RealType, Policy>& dist)
0492 {
0493 return dist.shape();
0494 }
0495
0496 template <class RealType, class Policy>
0497 inline RealType standard_deviation(const inverse_gaussian_distribution<RealType, Policy>& dist)
0498 {
0499 BOOST_MATH_STD_USING
0500 RealType scale = dist.scale();
0501 RealType mean = dist.mean();
0502 RealType result = sqrt(mean * mean * mean / scale);
0503 return result;
0504 }
0505
0506 template <class RealType, class Policy>
0507 inline RealType mode(const inverse_gaussian_distribution<RealType, Policy>& dist)
0508 {
0509 BOOST_MATH_STD_USING
0510 RealType scale = dist.scale();
0511 RealType mean = dist.mean();
0512 RealType result = mean * (sqrt(1 + (9 * mean * mean)/(4 * scale * scale))
0513 - 3 * mean / (2 * scale));
0514 return result;
0515 }
0516
0517 template <class RealType, class Policy>
0518 inline RealType skewness(const inverse_gaussian_distribution<RealType, Policy>& dist)
0519 {
0520 BOOST_MATH_STD_USING
0521 RealType scale = dist.scale();
0522 RealType mean = dist.mean();
0523 RealType result = 3 * sqrt(mean/scale);
0524 return result;
0525 }
0526
0527 template <class RealType, class Policy>
0528 inline RealType kurtosis(const inverse_gaussian_distribution<RealType, Policy>& dist)
0529 {
0530 RealType scale = dist.scale();
0531 RealType mean = dist.mean();
0532 RealType result = 15 * mean / scale -3;
0533 return result;
0534 }
0535
0536 template <class RealType, class Policy>
0537 inline RealType kurtosis_excess(const inverse_gaussian_distribution<RealType, Policy>& dist)
0538 {
0539 RealType scale = dist.scale();
0540 RealType mean = dist.mean();
0541 RealType result = 15 * mean / scale;
0542 return result;
0543 }
0544
0545 }
0546 }
0547
0548
0549
0550
0551 #include <boost/math/distributions/detail/derived_accessors.hpp>
0552
0553 #endif
0554
0555