1 /++ 2 Random number generators 3 +/ 4 /** 5 Authors: [Ilya Yaroshenko](http://9il.github.io) 6 7 Copyright: © 2014-2015 [Ilya Yaroshenko](http://9il.github.io) 8 9 License: MIT 10 */ 11 module atmosphere.random; 12 13 import core.stdc.tgmath; 14 15 import std.math : isNormal, isFinite; 16 import std.random; 17 import std.traits; 18 import atmosphere.params; 19 20 /++ 21 Interface for infinity input range of random numbers. 22 +/ 23 interface DistributionRNG(T) 24 { 25 ///always false 26 enum empty = false; 27 ///do nothing 28 static void popFront() @safe pure nothrow @nogc {} 29 /++ 30 Returns: new random number. 31 +/ 32 T front() @property; 33 } 34 35 /// 36 unittest 37 { 38 import std.algorithm : map; 39 class NormalRNG : DistributionRNG!double 40 { 41 double front() @property 42 { 43 return rNormal(); 44 } 45 } 46 47 import std.range; 48 auto rng = new NormalRNG; 49 auto sigma = 4.0; 50 auto mu = 2.0; 51 auto sample = rng.map!(x => x * sigma + mu).take(9).array; 52 } 53 54 55 /// 56 DistributionRNG!F toDistributionRNG 57 (Rng, F = ReturnType!(Rng.front)) 58 (Rng rng) 59 { 60 static assert(isFloatingPoint!F); 61 return new class (rng) DistributionRNG!F { 62 Rng rng; 63 this(Rng rng) { this.rng = rng; } 64 F front() @property { return rng.front; }; 65 }; 66 67 } 68 69 /// 70 unittest 71 { 72 DistributionRNG!double rng = GammaSRNG!double(rndGen, 1, 3).toDistributionRNG; 73 } 74 75 76 /++ 77 Class to create normal variance-mean mixture random number generators. 78 Assume `U` has mixing probability density, `Y ~ N(0, 1)`. 79 Class constructs `RNG` for `Z = Y*U^(1/2)+beta*U`. 80 +/ 81 class NormalVarianceMeanMixtureRNG(T, UniformRNG = Random) : DistributionRNG!T 82 if (isFloatingPoint!T) 83 { 84 private UniformRNG* rng; 85 86 private DistributionRNG!T components; 87 private T beta; 88 89 /++ 90 Constructor 91 Params: 92 rng = uniform random number generator (`Y`) 93 components = mixing random generator (`U`) 94 beta = mixture scale parameter: `Y*U^(1/2)+beta*U` 95 +/ 96 this(ref UniformRNG rng, DistributionRNG!T components, T beta) 97 in { 98 assert(beta.isFinite); 99 } 100 body { 101 this.rng = &rng; 102 this.components = components; 103 this.beta = beta; 104 } 105 106 final T front() @property 107 { 108 immutable y = rng.rNormal!T; 109 immutable u = components.front; 110 return y * u.sqrt + beta * u; 111 } 112 } 113 114 /// 115 unittest 116 { 117 import std.random; 118 import std.range; 119 class MyVarianceGammaRNG : NormalVarianceMeanMixtureRNG!double 120 { 121 this(double shape, double scale, double beta) 122 { 123 auto components = GammaSRNG!double(rndGen, shape, scale); 124 super(rndGen, components.toDistributionRNG, beta); 125 } 126 } 127 auto rng = new MyVarianceGammaRNG(1.2, 10, 3); 128 auto sample = rng.take(10).array; 129 } 130 131 132 /++ 133 Class to generate random observations from a 134 gamma 135 distribution. 136 +/ 137 struct GammaSRNG(T, UniformRNG = Random) 138 if (isFloatingPoint!T) 139 { 140 private UniformRNG* rng; 141 142 private T shape, scale; 143 144 /++ 145 Constructor 146 Params: 147 rng = uniform random number generator 148 shape = shape parameter 149 scale = scale parameter 150 +/ 151 this(ref UniformRNG rng, T shape, T scale) 152 { 153 this.rng = &rng; 154 this.shape = shape; 155 this.scale = scale; 156 } 157 158 /// 159 T front() @property 160 { 161 return rng.rGamma(shape) * scale; 162 } 163 /// 164 static void popFront() @safe pure nothrow @nogc {} 165 /// 166 enum empty = false; 167 168 } 169 170 /// 171 unittest 172 { 173 import std.algorithm : map; 174 import std.range; 175 auto rng = GammaSRNG!double(rndGen, 1.1, 1.1); 176 auto sample = rng.map!(x => x + 4).take(9).array; 177 } 178 179 180 /++ 181 Class to generate random observations from a 182 inverse-gamma 183 distribution. 184 +/ 185 struct InverseGammaSRNG(T, UniformRNG = Random) 186 if (isFloatingPoint!T) 187 { 188 private UniformRNG* rng; 189 190 private T shape, scale; 191 192 /++ 193 Constructor 194 Params: 195 rng = uniform random number generator 196 shape = shape parameter 197 scale = scale parameter 198 +/ 199 this(ref UniformRNG rng, T shape, T scale) 200 { 201 this.rng = &rng; 202 this.shape = shape; 203 this.scale = scale; 204 } 205 206 /// 207 T front() @property 208 { 209 return rng.rInverseGamma(shape) * scale; 210 } 211 /// 212 static void popFront() @safe pure nothrow @nogc {} 213 /// 214 enum empty = false; 215 216 } 217 218 /// 219 unittest 220 { 221 import std.algorithm : map; 222 import std.range; 223 auto rng = InverseGammaSRNG!double(rndGen, 1.1, 1.1); 224 auto sample = rng.map!(x => x + 4).take(9).array; 225 } 226 227 228 /++ 229 Class to generate random observations from a 230 generalized gamma 231 distribution. 232 +/ 233 struct GeneralizedGammaSRNG(T, UniformRNG = Random) 234 if (isFloatingPoint!T) 235 { 236 private UniformRNG* rng; 237 238 private T shape, power, scale; 239 240 /++ 241 Constructor 242 Params: 243 rng = uniform random number generator 244 shape = shape parameter 245 power = power parameter 246 scale = scale parameter 247 +/ 248 this(ref UniformRNG rng, T shape, T power, T scale) 249 { 250 this.rng = &rng; 251 this.shape = shape; 252 this.power = power; 253 this.scale = scale; 254 } 255 256 /// 257 T front() @property 258 { 259 return rng.rGeneralizedGamma(shape, power) * scale; 260 } 261 /// 262 static void popFront() @safe pure nothrow @nogc {} 263 /// 264 enum empty = false; 265 266 } 267 268 /// 269 unittest 270 { 271 import std.algorithm : map; 272 import std.range; 273 auto rng = GeneralizedGammaSRNG!double(rndGen, 1.1, 1.1, 1.1); 274 auto sample = rng.map!(x => x + 4).take(9).array; 275 } 276 277 278 /++ 279 Class to generate random observations from a 280 inverse Gaussian 281 distribution. 282 +/ 283 struct InverseGaussianSRNG(T, UniformRNG = Random) 284 if (isFloatingPoint!T) 285 { 286 private UniformRNG* rng; 287 288 private T mu, lambda; 289 290 /++ 291 Constructor 292 Params: 293 rng = uniform random number generator 294 lambda = parameter lambda 295 mu = parameter mu 296 +/ 297 this(ref UniformRNG rng, T lambda, T mu) 298 { 299 this.rng = &rng; 300 this.lambda = lambda; 301 this.mu = mu; 302 } 303 304 /// 305 T front() @property 306 { 307 return rng.rInverseGaussian(lambda, mu); 308 } 309 /// 310 static void popFront() @safe pure nothrow @nogc {} 311 /// 312 enum empty = false; 313 314 } 315 316 /// 317 unittest 318 { 319 import std.algorithm : map; 320 import std.range; 321 auto rng = InverseGaussianSRNG!double(rndGen, 1.1, 1.1); 322 auto sample = rng.map!(x => x + 4).take(9).array; 323 } 324 325 326 /++ 327 Class to generate random observations from a 328 proper (chi > 0, psi > 0) generalized inverse Gaussian distribution. 329 The algorithm is based on that given by Dagpunar (1989). 330 331 References: $(LINK2 https://www.stat.auckland.ac.nz/~dscott/, Original R source code). 332 +/ 333 struct ProperGeneralizedInverseGaussianSRNG(T, UniformRNG = Random) 334 if (isFloatingPoint!T) 335 { 336 private UniformRNG* rng; 337 338 private T lambdam1, eta, omega, a, b, c, m; 339 340 /++ 341 Constructor 342 Params: 343 rng = uniform random number generator 344 lambda = parameter lambda 345 eta = sqrt(chi / psi) 346 omega = sqrt(chi * psi) 347 +/ 348 this(ref UniformRNG rng, T lambda, T eta, T omega) 349 in { 350 assert(lambda.isFinite); 351 assert(eta.isNormal); 352 assert(eta > 0); 353 assert(omega.isNormal); 354 assert(omega > 0); 355 } 356 body { 357 import std.numeric : findRoot; 358 359 this.rng = &rng; 360 this.lambdam1 = lambda - 1; 361 this.omega= omega; 362 this.eta = eta; 363 this.m = (lambdam1 + hypot(lambdam1, omega)) / omega; 364 365 immutable a0 = 0.5f * omega; 366 immutable a1 = -1 - lambda - m * a0; 367 immutable a2 = lambdam1 * m - a0; 368 immutable a3 = m * a0; 369 //g = function(y) { 370 //0.5f*omega*y^3 - y^2*(0.5f*omega*m + lambda + 1) + 371 //y*(lambdam1*m - 0.5f*omega) + 0.5f*omega*m 372 //} 373 T g(T y) { return y * (y * (y * a0 + a1) + a2) + a3; } 374 T upper = m; 375 while (g(upper) <= 0) 376 upper *= 2; 377 immutable yM = findRoot(&g, 0, m); 378 immutable yP = findRoot(&g, m, upper); 379 immutable d = m + 1/m; 380 immutable e = -0.25f * omega; 381 382 this.a = (yP - m) * pow(yP/m, 0.5f * lambdam1) * exp(e * (yP + 1/yP - d)); 383 this.b = (yM - m) * pow(yM/m, 0.5f * lambdam1) * exp(e * (yM + 1/yM - d)); 384 this.c = e * d + 0.5f * lambdam1 * log(m); 385 } 386 387 /// 388 T front() @property 389 { 390 immutable c0 = -0.5f*lambdam1; 391 immutable c1 = 0.25f * omega; 392 for (;;) 393 { 394 immutable r1 = rng.uniform01!T; 395 immutable r2 = rng.uniform01!T; 396 immutable x = m + a * r2 / r1 + b * (1 - r2) / r1; 397 if (x > 0 && -log(r1) >= c0 * log(x) + c1 * (x + 1/x) + c) 398 return x * eta; 399 } 400 } 401 /// 402 static void popFront() @safe pure nothrow @nogc {} 403 /// 404 enum empty = false; 405 406 } 407 408 /// 409 unittest 410 { 411 import std.algorithm : map; 412 import std.range; 413 auto rng = ProperGeneralizedInverseGaussianSRNG!double(rndGen, -2, 5.0, 2); 414 auto sample = rng.map!(x => x + 4).take(9).array; 415 } 416 417 418 /++ 419 Class to generate random observations from a 420 generalized inverse Gaussian 421 distribution. 422 423 See_Also: [distribution.params](distribution/params.html) 424 +/ 425 final class GeneralizedInverseGaussianRNG(T, UniformRNG = Random) : DistributionRNG!T 426 if (isFloatingPoint!T) 427 { 428 private DistributionRNG!T rng; 429 430 /++ 431 Constructor 432 Params: 433 rng = uniform random number generator 434 lambda = parameter lambda 435 chi = chi parameter 436 psi = psi parameter 437 +/ 438 this(ref UniformRNG rng, T lambda, T chi, T psi) 439 in { 440 assert(lambda.isFinite); 441 assert(chi.isFinite); 442 assert(chi >= 0); 443 assert(psi.isFinite); 444 assert(psi >= 0); 445 } 446 body { 447 immutable params = GIGChiPsi!T(chi, psi); 448 if (chi < T.min_normal) 449 this.rng = GammaSRNG!(T, UniformRNG)(rng, lambda, 2 / psi).toDistributionRNG; 450 else if (psi < T.min_normal) 451 this.rng = InverseGammaSRNG!(T, UniformRNG)(rng, -lambda, chi / 2).toDistributionRNG; 452 else if (lambda == -0.5f) 453 this.rng = InverseGaussianSRNG!(T, UniformRNG)(rng, params.eta, chi).toDistributionRNG; 454 else 455 this.rng = ProperGeneralizedInverseGaussianSRNG!(T, UniformRNG)(rng, lambda, params.eta, params.omega).toDistributionRNG; 456 } 457 458 T front() @property 459 { 460 return rng.front; 461 } 462 } 463 464 /// 465 unittest 466 { 467 import std.algorithm : map; 468 import std.range; 469 auto rng = new GeneralizedInverseGaussianRNG!double(rndGen, -2, 5.0, 2); 470 auto sample = rng.map!(x => x + 4).take(9).array; 471 } 472 473 474 /++ 475 Class to generate random observations from a 476 variance-gamma 477 distribution using normal variance-mean mixture of 478 gamma 479 distribution. 480 +/ 481 final class VarianceGammaRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T 482 if (isFloatingPoint!T) 483 { 484 /++ 485 Constructor 486 Params: 487 rng = uniform random number generator 488 shape = gamma shape parameter 489 scale = gamma scale parameter 490 beta = mixture scale parameter: `Y*U^(1/2)+beta*U` 491 +/ 492 this(ref UniformRNG rng, T shape, T scale, T beta) 493 { 494 super(rng, GammaSRNG!(T, UniformRNG)(rng, shape, scale).toDistributionRNG, beta); 495 } 496 } 497 498 /// 499 unittest 500 { 501 import std.algorithm : map; 502 import std.range; 503 auto rng = new VarianceGammaRNG!double(rndGen, 1.1, 1.1, 1.1); 504 auto sample = rng.map!(x => x + 4).take(9).array; 505 } 506 507 508 /++ 509 Class to generate random observations from a 510 hyperbolic asymmetric 511 t-distribution using normal variance-mean mixture of 512 inverse-gamma 513 distribution. 514 +/ 515 final class HyperbolicAsymmetricTRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T 516 if (isFloatingPoint!T) 517 { 518 /++ 519 Constructor 520 Params: 521 rng = uniform random number generator 522 shape = inverse-gamma shape parameter 523 scale = inverse-gamma scale parameter 524 beta = mixture scale parameter: `Y*U^(1/2)+beta*U` 525 +/ 526 this(ref UniformRNG rng, T shape, T scale, T beta) 527 { 528 super(rng, InverseGammaSRNG!(T, UniformRNG)(rng, shape, scale).toDistributionRNG, beta); 529 } 530 } 531 532 /// 533 unittest 534 { 535 import std.algorithm : map; 536 import std.range; 537 auto rng = new HyperbolicAsymmetricTRNG!double(rndGen, 1.1, 1.1, 1.1); 538 auto sample = rng.map!(x => x + 4).take(9).array; 539 } 540 541 542 /++ 543 Class to generate random observations from a 544 generalized variance-gamma 545 distribution using normal variance-mean mixture of 546 generalized gamma 547 distribution. 548 +/ 549 final class GeneralizedVarianceGammaRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T 550 if (isFloatingPoint!T) 551 { 552 /++ 553 Constructor 554 Params: 555 rng = uniform random number generator 556 shape = generalized gamma shape parameter 557 power = generalized gamma power parameter 558 scale = generalized gamma scale parameter 559 beta = mixture scale parameter: `Y*U^(1/2)+beta*U` 560 +/ 561 this(ref UniformRNG rng, T shape, T power, T scale, T beta) 562 { 563 super(rng, GeneralizedGammaSRNG!(T, UniformRNG)(rng, shape, power, scale).toDistributionRNG, beta); 564 } 565 } 566 567 /// 568 unittest 569 { 570 import std.algorithm : map; 571 import std.range; 572 auto rng = new GeneralizedVarianceGammaRNG!double(rndGen, 1.1, 1.1, 1.1, 1.1); 573 auto sample = rng.map!(x => x + 4).take(9).array; 574 } 575 576 577 /++ 578 Class to generate random observations from a 579 normal inverse Gaussian 580 distribution using normal variance-mean mixture of 581 inverse Gaussian 582 distribution. 583 +/ 584 final class NormalInverseGaussianRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T 585 if (isFloatingPoint!T) 586 { 587 /++ 588 Constructor 589 Params: 590 rng = uniform random number generator 591 mu = inverse Gaussian parameter mu 592 lambda = inverse Gaussian parameter lambda 593 beta = mixture scale parameter: `Y*U^(1/2) + beta*U` 594 +/ 595 this(ref UniformRNG rng, T lambda, T mu, T beta) 596 { 597 super(rng, InverseGaussianSRNG!(T, UniformRNG)(rng, mu, lambda).toDistributionRNG, beta); 598 } 599 } 600 601 /// 602 unittest 603 { 604 import std.algorithm : map; 605 import std.range; 606 auto rng = new NormalInverseGaussianRNG!double(rndGen, 1.1, 1.1, 1.1); 607 auto sample = rng.map!(x => x + 4).take(9).array; 608 } 609 610 611 /++ 612 Class to generate random observations from a 613 proper generalized hyperbolic 614 distribution using normal variance-mean mixture of 615 proper generalized inverse Gaussian 616 distribution. 617 618 See_Also: [distribution.params](distribution/params.html) 619 +/ 620 final class ProperGeneralizedHyperbolicRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T 621 if (isFloatingPoint!T) 622 { 623 /++ 624 Constructor 625 Params: 626 rng = uniform random number generator 627 lambda = proper generalized inverse Gaussian parameter lambda 628 eta = proper generalized inverse Gaussian scale parameter 629 omega = proper generalized inverse Gaussian concetration parameter 630 beta = mixture scale parameter: `Y*U^(1/2)+beta*U` 631 +/ 632 this(ref UniformRNG rng, T lambda, T eta, T omega, T beta) 633 { 634 super(rng, ProperGeneralizedInverseGaussianSRNG!(T, UniformRNG)(rng, lambda, eta, omega).toDistributionRNG, beta); 635 } 636 } 637 638 /// 639 unittest 640 { 641 import std.algorithm : map; 642 import std.range; 643 auto rng = new ProperGeneralizedHyperbolicRNG!double(rndGen, 1.1, 1.1, 1.1, 1.1); 644 auto sample = rng.map!(x => x + 4).take(9).array; 645 } 646 647 648 /++ 649 Class to generate random observations from a 650 generalized hyperbolic 651 distribution using normal variance-mean mixture of 652 generalized inverse Gaussian 653 distribution. 654 655 See_Also: [distribution.params](distribution/params.html) 656 +/ 657 final class GeneralizedHyperbolicRNG(T, UniformRNG = Random) : NormalVarianceMeanMixtureRNG!T 658 if (isFloatingPoint!T) 659 { 660 /++ 661 Constructor 662 Params: 663 rng = uniform random number generator 664 lambda = generalized inverse Gaussian parameter lambda 665 chi = generalized inverse Gaussian chi parameter 666 psi = generalized inverse Gaussian psi parameter 667 beta = mixture scale parameter: `Y*U^(1/2)+beta*U` 668 +/ 669 this(ref UniformRNG rng, T lambda, T chi, T psi, T beta) 670 { 671 super(rng, new GeneralizedInverseGaussianRNG!(T, UniformRNG)(rng, lambda, chi, psi), beta); 672 } 673 } 674 675 /// 676 unittest 677 { 678 import std.algorithm : map; 679 import std.range; 680 auto rng = new GeneralizedHyperbolicRNG!double(rndGen, 1.1, 1.1, 1.1, 1.1); 681 auto sample = rng.map!(x => x + 4).take(9).array; 682 } 683 684 685 /++ 686 Function to generate random observationb from 687 standard normal 688 distribution. 689 +/ 690 T rNormal(T = double)() 691 if (isFloatingPoint!T) 692 { 693 return rndGen.rNormal!T; 694 } 695 696 ///ditto 697 T rNormal(T = double, UniformRNG)(ref UniformRNG rng) 698 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 699 { 700 T p, p0, p1; 701 do 702 { 703 p0 = uniform!"[]"(-1.0, 1.0, rng); 704 p1 = uniform!"[]"(-1.0, 1.0, rng); 705 p = p0 * p0 + p1 * p1; 706 } 707 while (p >= 1); 708 return p0 * sqrt(-2 * log(p)/p); 709 } 710 711 /// 712 unittest 713 { 714 auto x = rNormal() * 5 + 1; 715 } 716 717 718 /++ 719 Function to generate random observation from 720 standard exponential 721 distribution. 722 +/ 723 T rExponential(T = double)() 724 if (isFloatingPoint!T) 725 { 726 return rndGen.rExponential!T; 727 } 728 729 ///ditto 730 T rExponential(T = double, UniformRNG)(ref UniformRNG rng) 731 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 732 { 733 return -rng.uniform01!T.log; 734 } 735 736 /// 737 unittest 738 { 739 auto x = rExponential() * 5; 740 } 741 742 743 /++ 744 Function to generate random observation from a 745 gamma 746 distribution. 747 748 References 749 "Computer Generation of Statistical Distributions" by Richard Saucier 750 +/ 751 T rGamma(T = double)(T shape) 752 if (isFloatingPoint!T) 753 { 754 return rndGen.rGamma!T(shape); 755 } 756 757 ///ditto 758 T rGamma(T = double, UniformRNG)(ref UniformRNG rng, T shape) 759 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 760 in { 761 assert(shape.isNormal); 762 assert(shape > 0); 763 } 764 body { 765 enum T E = 4.5; 766 enum T D = 2.5040773967762740733732583523868748412194809812852436493487L; //1 + log( E ); 767 768 if (shape == 1) 769 return rng.rExponential!T; 770 771 immutable A = 1 / sqrt( 2. * shape - 1 ); 772 immutable B = shape - log( 4. ); 773 immutable Q = shape + 1 / A; 774 immutable C = 1 + shape / E; 775 if (shape < 1) 776 for (;;) 777 { 778 T p = C * rng.uniform01!T; 779 T y = void, f = void; 780 if ( p > 1 ) 781 { 782 y = -log( ( C - p ) / shape ); 783 f = pow(y, shape - 1); 784 } 785 else 786 { 787 y = pow( p, 1 / shape ); 788 f = exp(-y); 789 } 790 if ( rng.uniform01!T <= f) 791 return y; 792 } 793 else 794 for (;;) 795 { 796 T p0 = rng.uniform01!T; 797 T p1 = rng.uniform01!T; 798 T v = A * log( p0 / ( 1. - p0 ) ); 799 T y = shape * exp( v ); 800 T z = p0 * p0 * p1; 801 T w = B + Q * v - y; 802 if ( w + D - E * z >= 0.0 || w >= log(z) ) 803 return y; 804 } 805 } 806 807 /// 808 unittest 809 { 810 auto x = rGamma(2.0) * 5; 811 } 812 813 814 /++ 815 Function to generate random observation from a 816 generalized gamma 817 distribution. 818 +/ 819 T rGeneralizedGamma(T = double)(T shape, T power) 820 if (isFloatingPoint!T) 821 { 822 return rndGen.rGeneralizedGamma!T(shape, power); 823 } 824 825 ///ditto 826 T rGeneralizedGamma(T = double, UniformRNG)(ref UniformRNG rng, T shape, T power) 827 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 828 in { 829 assert(power.isFinite); 830 assert(shape.isNormal); 831 assert(shape > 0); 832 } 833 body { 834 return rng.rGamma!T(shape).pow(1/power); 835 } 836 837 /// 838 unittest 839 { 840 auto x = rGeneralizedGamma(0.8, 3.0) * 5; 841 } 842 843 844 /++ 845 Function to generate random observation from a 846 inverse-gamma 847 distribution. 848 +/ 849 T rInverseGamma(T = double)(T shape) 850 if (isFloatingPoint!T) 851 { 852 return rndGen.rInverseGamma!T(shape); 853 } 854 855 ///ditto 856 T rInverseGamma(T = double, UniformRNG)(ref UniformRNG rng, T shape) 857 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 858 in { 859 assert(shape.isNormal); 860 assert(shape > 0); 861 } 862 body { 863 return 1 / rng.rGamma!T(shape); 864 } 865 866 /// 867 unittest 868 { 869 auto x = rInverseGamma(2.0) * 5; 870 } 871 872 873 /++ 874 Function to generate random observation from a 875 inverse Gaussian 876 distribution. 877 878 References: 879 Michael, John R.; Schucany, William R.; Haas, Roy W. (May 1976). "Generating Random Variates Using Transformations with Multiple Roots". 880 +/ 881 T rInverseGaussian(T = double)(T lambda, T mu) 882 if (isFloatingPoint!T) 883 { 884 return rndGen.rInverseGaussian!T(lambda, mu); 885 } 886 887 ///ditto 888 T rInverseGaussian(T = double, UniformRNG)(ref UniformRNG rng, T lambda, T mu) 889 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 890 in { 891 assert(lambda.isNormal); 892 assert(lambda > 0); 893 assert(mu.isNormal); 894 assert(mu > 0); 895 } 896 body { 897 immutable nu = mu * mu; 898 immutable v = rng.rNormal!T; 899 lambda *= 2; 900 immutable y = v * v * nu / lambda; 901 immutable x = mu + (y - sqrt(y * (mu + y))); 902 return rng.uniform01!T <= mu / (mu + x) ? x : nu / x; 903 } 904 905 /// 906 unittest 907 { 908 auto x = rInverseGaussian(2.0, 3.0) * 5; 909 } 910 911 912 /++ 913 Function to generate random observation from a 914 Chi-squared 915 distribution. 916 +/ 917 T rChiSquare(T = double)(T shape) 918 if (isFloatingPoint!T) 919 { 920 return rndGen.rChiSquare!T(shape); 921 } 922 923 ///ditto 924 T rChiSquare(T = double, UniformRNG)(ref UniformRNG rng, T shape) 925 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 926 in { 927 assert(shape.isNormal); 928 assert(shape > 0); 929 } 930 body { 931 return rng.rGamma(shape / 2) * 2; 932 } 933 934 /// 935 unittest 936 { 937 auto x = rChiSquare(2.0) * 5; 938 } 939 940 941 /++ 942 Function to generate random observation from a 943 Student's t-distribution. 944 +/ 945 T rStudentT(T = double)(T shape) 946 if (isFloatingPoint!T) 947 { 948 return rndGen.rStudentT!T(shape); 949 } 950 951 ///ditto 952 T rStudentT(T = double, UniformRNG)(ref UniformRNG rng, T shape) 953 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 954 in { 955 assert(shape.isNormal); 956 assert(shape > 0); 957 } 958 body { 959 return rng.rNormal!T / sqrt(rng.rChiSquare!T(shape)/shape); 960 } 961 962 /// 963 unittest 964 { 965 auto x = rStudentT(2.0) * 5; 966 } 967 968 969 /++ 970 Function to generate random observation from a 971 Weibull 972 distribution. 973 +/ 974 T rWeibull(T = double)(T shape) 975 if (isFloatingPoint!T) 976 { 977 return rndGen.rWeibull!T(shape); 978 } 979 980 ///ditto 981 T rWeibull(T = double, UniformRNG)(ref UniformRNG rng, T power) 982 if (isFloatingPoint!T && isUniformRNG!UniformRNG) 983 in { 984 assert(power.isFinite); 985 } 986 body { 987 return rng.rExponential!T.pow(1/power); 988 } 989 990 /// 991 unittest 992 { 993 auto x = rWeibull(2.0) * 5 + 1; 994 }