1 // Copyright John Maddock 2006, 2007. 2 // Copyright Paul A. Bristow 2006, 2007. 3 // Use, modification and distribution are subject to the 4 // Boost Software License, Version 1.0. (See accompanying file 5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 7 #ifndef BOOST_STATS_TRIANGULAR_HPP 8 #define BOOST_STATS_TRIANGULAR_HPP 9 10 // http://mathworld.wolfram.com/TriangularDistribution.html 11 // Note that the 'constructors' defined by Wolfram are difference from those here, 12 // for example 13 // N[variance[triangulardistribution{1, +2}, 1.5], 50] computes 14 // 0.041666666666666666666666666666666666666666666666667 15 // TriangularDistribution{1, +2}, 1.5 is the analog of triangular_distribution(1, 1.5, 2) 16 17 // http://en.wikipedia.org/wiki/Triangular_distribution 18 19 #include <boost/math/distributions/fwd.hpp> 20 #include <boost/math/special_functions/expm1.hpp> 21 #include <boost/math/distributions/detail/common_error_handling.hpp> 22 #include <boost/math/distributions/complement.hpp> 23 #include <boost/math/constants/constants.hpp> 24 25 #include <utility> 26 27 namespace boost{ namespace math 28 { 29 namespace detail 30 { 31 template <class RealType, class Policy> check_triangular_lower(const char * function,RealType lower,RealType * result,const Policy & pol)32 inline bool check_triangular_lower( 33 const char* function, 34 RealType lower, 35 RealType* result, const Policy& pol) 36 { 37 if((boost::math::isfinite)(lower)) 38 { // Any finite value is OK. 39 return true; 40 } 41 else 42 { // Not finite: infinity or NaN. 43 *result = policies::raise_domain_error<RealType>( 44 function, 45 "Lower parameter is %1%, but must be finite!", lower, pol); 46 return false; 47 } 48 } // bool check_triangular_lower( 49 50 template <class RealType, class Policy> check_triangular_mode(const char * function,RealType mode,RealType * result,const Policy & pol)51 inline bool check_triangular_mode( 52 const char* function, 53 RealType mode, 54 RealType* result, const Policy& pol) 55 { 56 if((boost::math::isfinite)(mode)) 57 { // any finite value is OK. 58 return true; 59 } 60 else 61 { // Not finite: infinity or NaN. 62 *result = policies::raise_domain_error<RealType>( 63 function, 64 "Mode parameter is %1%, but must be finite!", mode, pol); 65 return false; 66 } 67 } // bool check_triangular_mode( 68 69 template <class RealType, class Policy> check_triangular_upper(const char * function,RealType upper,RealType * result,const Policy & pol)70 inline bool check_triangular_upper( 71 const char* function, 72 RealType upper, 73 RealType* result, const Policy& pol) 74 { 75 if((boost::math::isfinite)(upper)) 76 { // any finite value is OK. 77 return true; 78 } 79 else 80 { // Not finite: infinity or NaN. 81 *result = policies::raise_domain_error<RealType>( 82 function, 83 "Upper parameter is %1%, but must be finite!", upper, pol); 84 return false; 85 } 86 } // bool check_triangular_upper( 87 88 template <class RealType, class Policy> check_triangular_x(const char * function,RealType const & x,RealType * result,const Policy & pol)89 inline bool check_triangular_x( 90 const char* function, 91 RealType const& x, 92 RealType* result, const Policy& pol) 93 { 94 if((boost::math::isfinite)(x)) 95 { // Any finite value is OK 96 return true; 97 } 98 else 99 { // Not finite: infinity or NaN. 100 *result = policies::raise_domain_error<RealType>( 101 function, 102 "x parameter is %1%, but must be finite!", x, pol); 103 return false; 104 } 105 } // bool check_triangular_x 106 107 template <class RealType, class Policy> check_triangular(const char * function,RealType lower,RealType mode,RealType upper,RealType * result,const Policy & pol)108 inline bool check_triangular( 109 const char* function, 110 RealType lower, 111 RealType mode, 112 RealType upper, 113 RealType* result, const Policy& pol) 114 { 115 if ((check_triangular_lower(function, lower, result, pol) == false) 116 || (check_triangular_mode(function, mode, result, pol) == false) 117 || (check_triangular_upper(function, upper, result, pol) == false)) 118 { // Some parameter not finite. 119 return false; 120 } 121 else if (lower >= upper) // lower == upper NOT useful. 122 { // lower >= upper. 123 *result = policies::raise_domain_error<RealType>( 124 function, 125 "lower parameter is %1%, but must be less than upper!", lower, pol); 126 return false; 127 } 128 else 129 { // Check lower <= mode <= upper. 130 if (mode < lower) 131 { 132 *result = policies::raise_domain_error<RealType>( 133 function, 134 "mode parameter is %1%, but must be >= than lower!", lower, pol); 135 return false; 136 } 137 if (mode > upper) 138 { 139 *result = policies::raise_domain_error<RealType>( 140 function, 141 "mode parameter is %1%, but must be <= than upper!", upper, pol); 142 return false; 143 } 144 return true; // All OK. 145 } 146 } // bool check_triangular 147 } // namespace detail 148 149 template <class RealType = double, class Policy = policies::policy<> > 150 class triangular_distribution 151 { 152 public: 153 typedef RealType value_type; 154 typedef Policy policy_type; 155 triangular_distribution(RealType l_lower=-1,RealType l_mode=0,RealType l_upper=1)156 triangular_distribution(RealType l_lower = -1, RealType l_mode = 0, RealType l_upper = 1) 157 : m_lower(l_lower), m_mode(l_mode), m_upper(l_upper) // Constructor. 158 { // Evans says 'standard triangular' is lower 0, mode 1/2, upper 1, 159 // has median sqrt(c/2) for c <=1/2 and 1 - sqrt(1-c)/2 for c >= 1/2 160 // But this -1, 0, 1 is more useful in most applications to approximate normal distribution, 161 // where the central value is the most likely and deviations either side equally likely. 162 RealType result; 163 detail::check_triangular("boost::math::triangular_distribution<%1%>::triangular_distribution",l_lower, l_mode, l_upper, &result, Policy()); 164 } 165 // Accessor functions. lower() const166 RealType lower()const 167 { 168 return m_lower; 169 } mode() const170 RealType mode()const 171 { 172 return m_mode; 173 } upper() const174 RealType upper()const 175 { 176 return m_upper; 177 } 178 private: 179 // Data members: 180 RealType m_lower; // distribution lower aka a 181 RealType m_mode; // distribution mode aka c 182 RealType m_upper; // distribution upper aka b 183 }; // class triangular_distribution 184 185 typedef triangular_distribution<double> triangular; 186 187 template <class RealType, class Policy> range(const triangular_distribution<RealType,Policy> &)188 inline const std::pair<RealType, RealType> range(const triangular_distribution<RealType, Policy>& /* dist */) 189 { // Range of permissible values for random variable x. 190 using boost::math::tools::max_value; 191 return std::pair<RealType, RealType>(-max_value<RealType>(), max_value<RealType>()); 192 } 193 194 template <class RealType, class Policy> support(const triangular_distribution<RealType,Policy> & dist)195 inline const std::pair<RealType, RealType> support(const triangular_distribution<RealType, Policy>& dist) 196 { // Range of supported values for random variable x. 197 // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero. 198 return std::pair<RealType, RealType>(dist.lower(), dist.upper()); 199 } 200 201 template <class RealType, class Policy> pdf(const triangular_distribution<RealType,Policy> & dist,const RealType & x)202 RealType pdf(const triangular_distribution<RealType, Policy>& dist, const RealType& x) 203 { 204 static const char* function = "boost::math::pdf(const triangular_distribution<%1%>&, %1%)"; 205 RealType lower = dist.lower(); 206 RealType mode = dist.mode(); 207 RealType upper = dist.upper(); 208 RealType result = 0; // of checks. 209 if(false == detail::check_triangular(function, lower, mode, upper, &result, Policy())) 210 { 211 return result; 212 } 213 if(false == detail::check_triangular_x(function, x, &result, Policy())) 214 { 215 return result; 216 } 217 if((x < lower) || (x > upper)) 218 { 219 return 0; 220 } 221 if (x == lower) 222 { // (mode - lower) == 0 which would lead to divide by zero! 223 return (mode == lower) ? 2 / (upper - lower) : RealType(0); 224 } 225 else if (x == upper) 226 { 227 return (mode == upper) ? 2 / (upper - lower) : RealType(0); 228 } 229 else if (x <= mode) 230 { 231 return 2 * (x - lower) / ((upper - lower) * (mode - lower)); 232 } 233 else 234 { // (x > mode) 235 return 2 * (upper - x) / ((upper - lower) * (upper - mode)); 236 } 237 } // RealType pdf(const triangular_distribution<RealType, Policy>& dist, const RealType& x) 238 239 template <class RealType, class Policy> cdf(const triangular_distribution<RealType,Policy> & dist,const RealType & x)240 inline RealType cdf(const triangular_distribution<RealType, Policy>& dist, const RealType& x) 241 { 242 static const char* function = "boost::math::cdf(const triangular_distribution<%1%>&, %1%)"; 243 RealType lower = dist.lower(); 244 RealType mode = dist.mode(); 245 RealType upper = dist.upper(); 246 RealType result = 0; // of checks. 247 if(false == detail::check_triangular(function, lower, mode, upper, &result, Policy())) 248 { 249 return result; 250 } 251 if(false == detail::check_triangular_x(function, x, &result, Policy())) 252 { 253 return result; 254 } 255 if((x <= lower)) 256 { 257 return 0; 258 } 259 if (x >= upper) 260 { 261 return 1; 262 } 263 // else lower < x < upper 264 if (x <= mode) 265 { 266 return ((x - lower) * (x - lower)) / ((upper - lower) * (mode - lower)); 267 } 268 else 269 { 270 return 1 - (upper - x) * (upper - x) / ((upper - lower) * (upper - mode)); 271 } 272 } // RealType cdf(const triangular_distribution<RealType, Policy>& dist, const RealType& x) 273 274 template <class RealType, class Policy> quantile(const triangular_distribution<RealType,Policy> & dist,const RealType & p)275 RealType quantile(const triangular_distribution<RealType, Policy>& dist, const RealType& p) 276 { 277 BOOST_MATH_STD_USING // for ADL of std functions (sqrt). 278 static const char* function = "boost::math::quantile(const triangular_distribution<%1%>&, %1%)"; 279 RealType lower = dist.lower(); 280 RealType mode = dist.mode(); 281 RealType upper = dist.upper(); 282 RealType result = 0; // of checks 283 if(false == detail::check_triangular(function,lower, mode, upper, &result, Policy())) 284 { 285 return result; 286 } 287 if(false == detail::check_probability(function, p, &result, Policy())) 288 { 289 return result; 290 } 291 if(p == 0) 292 { 293 return lower; 294 } 295 if(p == 1) 296 { 297 return upper; 298 } 299 RealType p0 = (mode - lower) / (upper - lower); 300 RealType q = 1 - p; 301 if (p < p0) 302 { 303 result = sqrt((upper - lower) * (mode - lower) * p) + lower; 304 } 305 else if (p == p0) 306 { 307 result = mode; 308 } 309 else // p > p0 310 { 311 result = upper - sqrt((upper - lower) * (upper - mode) * q); 312 } 313 return result; 314 315 } // RealType quantile(const triangular_distribution<RealType, Policy>& dist, const RealType& q) 316 317 template <class RealType, class Policy> cdf(const complemented2_type<triangular_distribution<RealType,Policy>,RealType> & c)318 RealType cdf(const complemented2_type<triangular_distribution<RealType, Policy>, RealType>& c) 319 { 320 static const char* function = "boost::math::cdf(const triangular_distribution<%1%>&, %1%)"; 321 RealType lower = c.dist.lower(); 322 RealType mode = c.dist.mode(); 323 RealType upper = c.dist.upper(); 324 RealType x = c.param; 325 RealType result = 0; // of checks. 326 if(false == detail::check_triangular(function, lower, mode, upper, &result, Policy())) 327 { 328 return result; 329 } 330 if(false == detail::check_triangular_x(function, x, &result, Policy())) 331 { 332 return result; 333 } 334 if (x <= lower) 335 { 336 return 1; 337 } 338 if (x >= upper) 339 { 340 return 0; 341 } 342 if (x <= mode) 343 { 344 return 1 - ((x - lower) * (x - lower)) / ((upper - lower) * (mode - lower)); 345 } 346 else 347 { 348 return (upper - x) * (upper - x) / ((upper - lower) * (upper - mode)); 349 } 350 } // RealType cdf(const complemented2_type<triangular_distribution<RealType, Policy>, RealType>& c) 351 352 template <class RealType, class Policy> quantile(const complemented2_type<triangular_distribution<RealType,Policy>,RealType> & c)353 RealType quantile(const complemented2_type<triangular_distribution<RealType, Policy>, RealType>& c) 354 { 355 BOOST_MATH_STD_USING // Aid ADL for sqrt. 356 static const char* function = "boost::math::quantile(const triangular_distribution<%1%>&, %1%)"; 357 RealType l = c.dist.lower(); 358 RealType m = c.dist.mode(); 359 RealType u = c.dist.upper(); 360 RealType q = c.param; // probability 0 to 1. 361 RealType result = 0; // of checks. 362 if(false == detail::check_triangular(function, l, m, u, &result, Policy())) 363 { 364 return result; 365 } 366 if(false == detail::check_probability(function, q, &result, Policy())) 367 { 368 return result; 369 } 370 if(q == 0) 371 { 372 return u; 373 } 374 if(q == 1) 375 { 376 return l; 377 } 378 RealType lower = c.dist.lower(); 379 RealType mode = c.dist.mode(); 380 RealType upper = c.dist.upper(); 381 382 RealType p = 1 - q; 383 RealType p0 = (mode - lower) / (upper - lower); 384 if(p < p0) 385 { 386 RealType s = (upper - lower) * (mode - lower); 387 s *= p; 388 result = sqrt((upper - lower) * (mode - lower) * p) + lower; 389 } 390 else if (p == p0) 391 { 392 result = mode; 393 } 394 else // p > p0 395 { 396 result = upper - sqrt((upper - lower) * (upper - mode) * q); 397 } 398 return result; 399 } // RealType quantile(const complemented2_type<triangular_distribution<RealType, Policy>, RealType>& c) 400 401 template <class RealType, class Policy> mean(const triangular_distribution<RealType,Policy> & dist)402 inline RealType mean(const triangular_distribution<RealType, Policy>& dist) 403 { 404 static const char* function = "boost::math::mean(const triangular_distribution<%1%>&)"; 405 RealType lower = dist.lower(); 406 RealType mode = dist.mode(); 407 RealType upper = dist.upper(); 408 RealType result = 0; // of checks. 409 if(false == detail::check_triangular(function, lower, mode, upper, &result, Policy())) 410 { 411 return result; 412 } 413 return (lower + upper + mode) / 3; 414 } // RealType mean(const triangular_distribution<RealType, Policy>& dist) 415 416 417 template <class RealType, class Policy> variance(const triangular_distribution<RealType,Policy> & dist)418 inline RealType variance(const triangular_distribution<RealType, Policy>& dist) 419 { 420 static const char* function = "boost::math::mean(const triangular_distribution<%1%>&)"; 421 RealType lower = dist.lower(); 422 RealType mode = dist.mode(); 423 RealType upper = dist.upper(); 424 RealType result = 0; // of checks. 425 if(false == detail::check_triangular(function, lower, mode, upper, &result, Policy())) 426 { 427 return result; 428 } 429 return (lower * lower + upper * upper + mode * mode - lower * upper - lower * mode - upper * mode) / 18; 430 } // RealType variance(const triangular_distribution<RealType, Policy>& dist) 431 432 template <class RealType, class Policy> mode(const triangular_distribution<RealType,Policy> & dist)433 inline RealType mode(const triangular_distribution<RealType, Policy>& dist) 434 { 435 static const char* function = "boost::math::mode(const triangular_distribution<%1%>&)"; 436 RealType mode = dist.mode(); 437 RealType result = 0; // of checks. 438 if(false == detail::check_triangular_mode(function, mode, &result, Policy())) 439 { // This should never happen! 440 return result; 441 } 442 return mode; 443 } // RealType mode 444 445 template <class RealType, class Policy> median(const triangular_distribution<RealType,Policy> & dist)446 inline RealType median(const triangular_distribution<RealType, Policy>& dist) 447 { 448 BOOST_MATH_STD_USING // ADL of std functions. 449 static const char* function = "boost::math::median(const triangular_distribution<%1%>&)"; 450 RealType mode = dist.mode(); 451 RealType result = 0; // of checks. 452 if(false == detail::check_triangular_mode(function, mode, &result, Policy())) 453 { // This should never happen! 454 return result; 455 } 456 RealType lower = dist.lower(); 457 RealType upper = dist.upper(); 458 if (mode >= (upper + lower) / 2) 459 { 460 return lower + sqrt((upper - lower) * (mode - lower)) / constants::root_two<RealType>(); 461 } 462 else 463 { 464 return upper - sqrt((upper - lower) * (upper - mode)) / constants::root_two<RealType>(); 465 } 466 } // RealType mode 467 468 template <class RealType, class Policy> skewness(const triangular_distribution<RealType,Policy> & dist)469 inline RealType skewness(const triangular_distribution<RealType, Policy>& dist) 470 { 471 BOOST_MATH_STD_USING // for ADL of std functions 472 using namespace boost::math::constants; // for root_two 473 static const char* function = "boost::math::skewness(const triangular_distribution<%1%>&)"; 474 475 RealType lower = dist.lower(); 476 RealType mode = dist.mode(); 477 RealType upper = dist.upper(); 478 RealType result = 0; // of checks. 479 if(false == boost::math::detail::check_triangular(function,lower, mode, upper, &result, Policy())) 480 { 481 return result; 482 } 483 return root_two<RealType>() * (lower + upper - 2 * mode) * (2 * lower - upper - mode) * (lower - 2 * upper + mode) / 484 (5 * pow((lower * lower + upper * upper + mode * mode 485 - lower * upper - lower * mode - upper * mode), RealType(3)/RealType(2))); 486 // #11768: Skewness formula for triangular distribution is incorrect - corrected 29 Oct 2015 for release 1.61. 487 } // RealType skewness(const triangular_distribution<RealType, Policy>& dist) 488 489 template <class RealType, class Policy> kurtosis(const triangular_distribution<RealType,Policy> & dist)490 inline RealType kurtosis(const triangular_distribution<RealType, Policy>& dist) 491 { // These checks may be belt and braces as should have been checked on construction? 492 static const char* function = "boost::math::kurtosis(const triangular_distribution<%1%>&)"; 493 RealType lower = dist.lower(); 494 RealType upper = dist.upper(); 495 RealType mode = dist.mode(); 496 RealType result = 0; // of checks. 497 if(false == detail::check_triangular(function,lower, mode, upper, &result, Policy())) 498 { 499 return result; 500 } 501 return static_cast<RealType>(12)/5; // 12/5 = 2.4; 502 } // RealType kurtosis_excess(const triangular_distribution<RealType, Policy>& dist) 503 504 template <class RealType, class Policy> kurtosis_excess(const triangular_distribution<RealType,Policy> & dist)505 inline RealType kurtosis_excess(const triangular_distribution<RealType, Policy>& dist) 506 { // These checks may be belt and braces as should have been checked on construction? 507 static const char* function = "boost::math::kurtosis_excess(const triangular_distribution<%1%>&)"; 508 RealType lower = dist.lower(); 509 RealType upper = dist.upper(); 510 RealType mode = dist.mode(); 511 RealType result = 0; // of checks. 512 if(false == detail::check_triangular(function,lower, mode, upper, &result, Policy())) 513 { 514 return result; 515 } 516 return static_cast<RealType>(-3)/5; // - 3/5 = -0.6 517 // Assuming mathworld really means kurtosis excess? Wikipedia now corrected to match this. 518 } 519 520 template <class RealType, class Policy> entropy(const triangular_distribution<RealType,Policy> & dist)521 inline RealType entropy(const triangular_distribution<RealType, Policy>& dist) 522 { 523 using std::log; 524 return constants::half<RealType>() + log((dist.upper() - dist.lower())/2); 525 } 526 527 } // namespace math 528 } // namespace boost 529 530 // This include must be at the end, *after* the accessors 531 // for this distribution have been defined, in order to 532 // keep compilers that support two-phase lookup happy. 533 #include <boost/math/distributions/detail/derived_accessors.hpp> 534 535 #endif // BOOST_STATS_TRIANGULAR_HPP 536 537 538 539