Description
I encountered incorrect output from @stdlib/stats/base/dists/rayleigh/mgf while cross-checking the MGF implementations against their closed forms. The Rayleigh implementation multiplies the whole (1 + ...) expression by the √(π/2)·(1 + erf) factor, when that factor should only multiply the σt term.
The current code in lib/main.js (and identically in lib/factory.js and src/main.c) reads as follows:
sigmat = t * sigma;
out = 1.0 + (sigmat * exp( sigmat*sigmat / 2.0 ));
out *= SQRT_HALF_PI * ( erf( sigmat / SQRT2 ) + 1.0 );
The closed form for the Rayleigh MGF is M(t) = 1 + σt·√(π/2)·e^((σt)²/2)·(1 + erf(σt/√2)), so the trailing factor ends up being applied to the leading 1 as well. The quickest way to see the problem is that mgf(0, σ) returns √(π/2) ≈ 1.2533 instead of 1, and every moment-generating function must equal 1 at t = 0. The error also flips signs in places: mgf(-1, 4) currently returns -0.947, whereas the correct value is +0.053.
The JSDoc and README examples (mgf(1.0, 3.0) → ~678.508 and similar) match the buggy formula, so the incorrect values are baked into the documentation as well. I checked the neighboring mgf implementations (geometric, negative-binomial, Poisson, gamma, normal, and others), and this problem looks isolated to Rayleigh.
Proposed Approach
The fix is a small regrouping of the existing expression so that the √(π/2)·(1 + erf) factor multiplies only the σt term, applied in all three places that carry the formula:
sigmat = t * sigma;
out = 1.0 + ( sigmat * SQRT_HALF_PI * exp( sigmat*sigmat / 2.0 ) * ( erf( sigmat / SQRT2 ) + 1.0 ) );
Concretely, I propose updating lib/main.js, lib/factory.js, and src/main.c, along with the corresponding doc examples in README.md, docs/repl.txt, docs/types/index.d.ts, lib/index.js, and lib/native.js. The corrected example outputs, verified against stdlib's own erf implementation, are mgf(1.0, 3.0) → ~677.005, mgf(1.0, 2.0) → ~37.2, mgf(-1.0, 4.0) → ~0.053, mgf.factory(0.5)(1.0) → ~1.982, and mgf.factory(0.5)(0.5) → ~1.387. I have these changes ready locally and fully verified .
I’m happy to prepare a PR for this if the team is aligned , however please let me know if there’s another direction you'd prefer to take...! :)
Related Issues
Related to my earlier discussion #15298
Questions
I have no blocking questions. I would, however, appreciate guidance on whether the pull request should also add numeric fixtures to test.mgf.js and test.factory.js, since those tests currently only assert non-NaN output, which is how this slipped through unnoticed.
Demo
N/A
Reproduction
- var mgf = require( '@stdlib/stats/base/dists/rayleigh/mgf' );
- mgf( 0.0, 1.0 ); // returns ~1.253 instead of 1
- mgf( 1.0, 3.0 ); // returns ~678.508 instead of ~677.005
- mgf( -1.0, 4.0 ); // returns ~-0.947 instead of ~0.053
Expected Results
1
677.0046886610472
0.05339046834575567
Actual Results
1.2533141373155003
678.5079332431045
-0.9465301436239729
Version
0.4.1 (develop)
Environments
Node.js
Browser Version
N/A
Node.js / npm Version
Node v26.5.0 (the numbers were verified against stdlib's own erf, so the discrepancy lies in the formula rather than the environment).
Platform
macOS (darwin). This is a formula-level bug and is platform-independent.
Checklist
Description
I encountered incorrect output from
@stdlib/stats/base/dists/rayleigh/mgfwhile cross-checking the MGF implementations against their closed forms. The Rayleigh implementation multiplies the whole(1 + ...)expression by the√(π/2)·(1 + erf)factor, when that factor should only multiply theσtterm.The current code in
lib/main.js(and identically inlib/factory.jsandsrc/main.c) reads as follows:The closed form for the Rayleigh MGF is M(t) = 1 + σt·√(π/2)·e^((σt)²/2)·(1 + erf(σt/√2)), so the trailing factor ends up being applied to the leading 1 as well. The quickest way to see the problem is that mgf(0, σ) returns √(π/2) ≈ 1.2533 instead of 1, and every moment-generating function must equal 1 at t = 0. The error also flips signs in places: mgf(-1, 4) currently returns -0.947, whereas the correct value is +0.053.
The JSDoc and README examples (mgf(1.0, 3.0) → ~678.508 and similar) match the buggy formula, so the incorrect values are baked into the documentation as well. I checked the neighboring mgf implementations (geometric, negative-binomial, Poisson, gamma, normal, and others), and this problem looks isolated to Rayleigh.
Proposed Approach
The fix is a small regrouping of the existing expression so that the √(π/2)·(1 + erf) factor multiplies only the σt term, applied in all three places that carry the formula:
Concretely, I propose updating lib/main.js, lib/factory.js, and src/main.c, along with the corresponding doc examples in README.md, docs/repl.txt, docs/types/index.d.ts, lib/index.js, and lib/native.js. The corrected example outputs, verified against stdlib's own erf implementation, are mgf(1.0, 3.0) → ~677.005, mgf(1.0, 2.0) → ~37.2, mgf(-1.0, 4.0) → ~0.053, mgf.factory(0.5)(1.0) → ~1.982, and mgf.factory(0.5)(0.5) → ~1.387. I have these changes ready locally and fully verified .
I’m happy to prepare a PR for this if the team is aligned , however please let me know if there’s another direction you'd prefer to take...! :)
Related Issues
Related to my earlier discussion #15298
Questions
I have no blocking questions. I would, however, appreciate guidance on whether the pull request should also add numeric fixtures to test.mgf.js and test.factory.js, since those tests currently only assert non-NaN output, which is how this slipped through unnoticed.
Demo
N/A
Reproduction
Expected Results
Actual Results
Version
0.4.1 (develop)
Environments
Node.js
Browser Version
N/A
Node.js / npm Version
Node v26.5.0 (the numbers were verified against stdlib's own erf, so the discrepancy lies in the formula rather than the environment).
Platform
macOS (darwin). This is a formula-level bug and is platform-independent.
Checklist