Skip to content

Commit dd3acfd

Browse files
anandkaranubcPlaneshifterstdlib-bot
authored
refactor: update stats/base/dists/triangular/mgf implementation and test tolerances
PR-URL: #4768 Closes: #4704 Co-authored-by: Philipp Burckhardt <pburckhardt@outlook.com> Co-authored-by: stdlib-bot <noreply@stdlib.io> Reviewed-by: Philipp Burckhardt <pburckhardt@outlook.com> Reviewed-by: Athan Reines <kgryte@gmail.com> Signed-off-by: Philipp Burckhardt <pburckhardt@outlook.com>
1 parent f6829da commit dd3acfd

File tree

18 files changed

+500
-80
lines changed

18 files changed

+500
-80
lines changed

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/benchmark/benchmark.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
var bench = require( '@stdlib/bench' );
2424
var Float64Array = require( '@stdlib/array/float64' );
25-
var randu = require( '@stdlib/random/base/randu' );
25+
var uniform = require( '@stdlib/random/base/uniform' );
2626
var isnan = require( '@stdlib/math/base/assert/is-nan' );
2727
var EPS = require( '@stdlib/constants/float64/eps' );
2828
var pkg = require( './../package.json' ).name;
@@ -47,10 +47,10 @@ bench( pkg, function benchmark( b ) {
4747
mode = new Float64Array( len );
4848

4949
for ( i = 0; i < len; i++ ) {
50-
t[ i ] = randu() * 5.0;
51-
min[ i ] = randu() * 10.0;
52-
max[ i ] = min[ i ] + ( randu() * 40.0 ) + EPS;
53-
mode[ i ] = min[ i ] + ( ( max[ i ] - min[ i ] ) * randu() );
50+
t[ i ] = uniform( 0.0, 5.0 );
51+
min[ i ] = uniform( 0.0, 10.0 );
52+
max[ i ] = uniform( min[ i ] + EPS, min[ i ] + 40.0 );
53+
mode[ i ] = uniform( min[ i ], max[ i ]);
5454
}
5555

5656
b.tic();
@@ -73,6 +73,7 @@ bench( pkg+':factory', function benchmark( b ) {
7373
var mode;
7474
var min;
7575
var max;
76+
var len;
7677
var t;
7778
var y;
7879
var i;
@@ -81,11 +82,15 @@ bench( pkg+':factory', function benchmark( b ) {
8182
max = 1.5;
8283
mode = 0.5;
8384
mymgf = mgf.factory( min, max, mode );
85+
len = 100;
86+
t = new Float64Array( len );
87+
for ( i = 0; i < len; i++ ) {
88+
t[ i ] = uniform( 0.0, 5.0 );
89+
}
8490

8591
b.tic();
8692
for ( i = 0; i < b.iterations; i++ ) {
87-
t = randu() * 5.0;
88-
y = mymgf( t );
93+
y = mymgf( t[ i % len ]);
8994
if ( isnan( y ) ) {
9095
b.fail( 'should not return NaN' );
9196
}

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/benchmark/benchmark.native.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
var resolve = require( 'path' ).resolve;
2424
var bench = require( '@stdlib/bench' );
2525
var Float64Array = require( '@stdlib/array/float64' );
26-
var randu = require( '@stdlib/random/base/randu' );
26+
var uniform = require( '@stdlib/random/base/uniform' );
2727
var isnan = require( '@stdlib/math/base/assert/is-nan' );
2828
var tryRequire = require( '@stdlib/utils/try-require' );
2929
var EPS = require( '@stdlib/constants/float64/eps' );
@@ -56,10 +56,10 @@ bench( pkg+'::native', opts, function benchmark( b ) {
5656
mode = new Float64Array( len );
5757

5858
for ( i = 0; i < len; i++ ) {
59-
t[ i ] = randu() * 5.0;
60-
min[ i ] = randu() * 10.0;
61-
max[ i ] = min[ i ] + ( randu() * 40.0 ) + EPS;
62-
mode[ i ] = min[ i ] + ( ( max[ i ] - min[ i ] ) * randu() );
59+
t[ i ] = uniform( 0.0, 5.0 );
60+
min[ i ] = uniform( 0.0, 10.0 );
61+
max[ i ] = uniform( min[ i ] + EPS, min[ i ] + 40.0 );
62+
mode[ i ] = uniform( min[ i ], max[ i ]);
6363
}
6464

6565
b.tic();

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/lib/factory.js

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
var constantFunction = require( '@stdlib/utils/constant-function' );
2424
var isnan = require( '@stdlib/math/base/assert/is-nan' );
2525
var exp = require( '@stdlib/math/base/special/exp' );
26-
var pow = require( '@stdlib/math/base/special/pow' );
26+
var phi2 = require( './phi2.js' );
2727

2828

2929
// MAIN //
@@ -45,10 +45,6 @@ var pow = require( '@stdlib/math/base/special/pow' );
4545
* // returns ~10.205
4646
*/
4747
function factory( a, b, c ) {
48-
var bmc;
49-
var bma;
50-
var cma;
51-
5248
if (
5349
isnan( a ) ||
5450
isnan( b ) ||
@@ -58,9 +54,6 @@ function factory( a, b, c ) {
5854
) {
5955
return constantFunction( NaN );
6056
}
61-
bmc = b - c;
62-
bma = b - a;
63-
cma = c - a;
6457
return mgf;
6558

6659
/**
@@ -75,19 +68,19 @@ function factory( a, b, c ) {
7568
* // returns <number>
7669
*/
7770
function mgf( t ) {
78-
var ret;
79-
8071
if ( isnan( t ) ) {
8172
return NaN;
8273
}
83-
if ( t === 0.0 ) {
84-
return 1.0;
74+
if ( a < c ) {
75+
if ( c < b ) {
76+
return exp( c*t ) * ( ( (c-a)*phi2( (a-c)*t ) ) + ( (b-c)*phi2( (b-c)*t ) ) ) / ( b-a ); // eslint-disable-line max-len
77+
}
78+
return exp( c*t ) * phi2( ( a-c ) * t );
79+
}
80+
if ( c < b ) {
81+
return exp( c*t ) * phi2( ( b-c ) * t );
8582
}
86-
ret = ( bmc * exp( a*t ) ) - ( bma * exp( c*t ) );
87-
ret += cma * exp( b*t );
88-
ret *= 2.0;
89-
ret /= bma * cma * bmc * pow( t, 2.0 );
90-
return ret;
83+
return exp( c*t );
9184
}
9285
}
9386

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/lib/main.js

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
var isnan = require( '@stdlib/math/base/assert/is-nan' );
2424
var exp = require( '@stdlib/math/base/special/exp' );
25-
var pow = require( '@stdlib/math/base/special/pow' );
25+
var phi2 = require( './phi2.js' );
2626

2727

2828
// MAIN //
@@ -73,11 +73,6 @@ var pow = require( '@stdlib/math/base/special/pow' );
7373
* // returns NaN
7474
*/
7575
function mgf( t, a, b, c ) {
76-
var bmc;
77-
var bma;
78-
var cma;
79-
var ret;
80-
8176
if (
8277
isnan( t ) ||
8378
isnan( a ) ||
@@ -88,17 +83,16 @@ function mgf( t, a, b, c ) {
8883
) {
8984
return NaN;
9085
}
91-
if ( t === 0.0 ) {
92-
return 1.0;
86+
if ( a < c ) {
87+
if ( c < b ) {
88+
return exp( c*t ) * ( ( (c-a)*phi2( (a-c)*t ) ) + ( (b-c)*phi2( (b-c)*t ) ) ) / ( b-a ); // eslint-disable-line max-len
89+
}
90+
return exp( c*t ) * phi2( ( a-c ) * t );
91+
}
92+
if ( c < b ) {
93+
return exp( c*t ) * phi2( ( b-c ) * t );
9394
}
94-
bmc = b - c;
95-
bma = b - a;
96-
cma = c - a;
97-
ret = ( bmc * exp( a*t ) ) - ( bma * exp( c*t ) );
98-
ret += cma * exp( b*t );
99-
ret *= 2.0;
100-
ret /= bma * cma * bmc * pow( t, 2.0 );
101-
return ret;
95+
return exp( c*t );
10296
}
10397

10498

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2025 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var expm1 = require( '@stdlib/math/base/special/expm1' );
24+
25+
26+
// MAIN //
27+
28+
/**
29+
* Helper function for repeated computation in the MGF formula.
30+
*
31+
* @private
32+
* @param {number} x - input value
33+
* @returns {number} evaluated result
34+
*/
35+
function phi2( x ) {
36+
if ( x === 0.0 ) {
37+
return 1.0;
38+
}
39+
return ( 2.0 * ( expm1( x ) - x ) ) / ( x*x );
40+
}
41+
42+
43+
// EXPORTS //
44+
45+
module.exports = phi2;

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"@stdlib/math/base/napi/quaternary",
4242
"@stdlib/math/base/assert/is-nan",
4343
"@stdlib/math/base/special/exp",
44-
"@stdlib/math/base/special/pow"
44+
"@stdlib/math/base/special/expm1"
4545
]
4646
},
4747
{

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/src/main.c

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,21 @@
1818

1919
#include "stdlib/stats/base/dists/triangular/mgf.h"
2020
#include "stdlib/math/base/assert/is_nan.h"
21+
#include "stdlib/math/base/special/expm1.h"
2122
#include "stdlib/math/base/special/exp.h"
22-
#include "stdlib/math/base/special/pow.h"
23+
24+
/**
25+
* Helper function for repeated computation in the MGF formula.
26+
*
27+
* @param x input value
28+
* @return evaluated result
29+
*/
30+
static double phi2( const double x ) {
31+
if ( x == 0.0 ) {
32+
return 1.0;
33+
}
34+
return ( 2.0 * ( stdlib_base_expm1( x ) - x ) ) / ( x*x );
35+
}
2336

2437
/**
2538
* Evaluates the moment-generating function (MGF) for a triangular distribution with lower limit `a`, upper limit `b`, and mode `c` at a value `t`.
@@ -35,29 +48,19 @@
3548
* // returns ~1.021
3649
*/
3750
double stdlib_base_dists_triangular_mgf( const double t, const double a, const double b, const double c ) {
38-
double bmc;
39-
double bma;
40-
double cma;
41-
double ret;
42-
if (
43-
stdlib_base_is_nan( t ) ||
44-
stdlib_base_is_nan( a ) ||
45-
stdlib_base_is_nan( b ) ||
46-
stdlib_base_is_nan( c ) ||
47-
a > c ||
48-
c > b
49-
) {
50-
return 0.0/0.0; // NaN
51+
if ( stdlib_base_is_nan( t ) || stdlib_base_is_nan( a ) || stdlib_base_is_nan( b ) || stdlib_base_is_nan( c ) || a > c || c > b ) {
52+
return 0.0 / 0.0; // NaN
5153
}
52-
if ( t == 0.0 ) {
53-
return 1.0;
54+
if ( a < c ) {
55+
if ( c < b ) {
56+
double term1 = ( c-a ) * phi2( ( a-c ) * t );
57+
double term2 = ( b-c ) * phi2( ( b-c ) * t );
58+
return stdlib_base_exp( c*t ) * ( term1+term2 ) / ( b-a );
59+
}
60+
return stdlib_base_exp( c*t ) * phi2( ( a-c ) * t );
61+
}
62+
if ( c < b ) {
63+
return stdlib_base_exp( c*t ) * phi2( ( b-c ) * t );
5464
}
55-
bmc = b - c;
56-
bma = b - a;
57-
cma = c - a;
58-
ret = ( bmc * stdlib_base_exp( a*t ) ) - ( bma * stdlib_base_exp( c*t ) );
59-
ret += cma * stdlib_base_exp( b*t );
60-
ret *= 2.0;
61-
ret /= bma * cma * bmc * stdlib_base_pow( t, 2.0 );
62-
return ret;
65+
return stdlib_base_exp( c*t );
6366
}

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/test/fixtures/julia/a_eq_c_eq_b.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/test/fixtures/julia/a_eq_c_less_b.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/test/fixtures/julia/a_less_c_eq_b.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/test/fixtures/julia/a_less_c_less_b.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/test/fixtures/julia/large_range.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/test/fixtures/julia/medium_range.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/test/fixtures/julia/runner.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,15 @@ b = ( rand( 1000 ) .* 80.0 ) .+ a;
9393
c = a .+ ( b .- a ) .* rand();
9494
x = rand( 1000 );
9595
gen( x, a, b, c, "large_range.json" );
96+
97+
# Case: a < c < b
98+
gen( x, a, b, c, "a_less_c_less_b.json" );
99+
100+
# Case: a < c = b
101+
gen( x, a, b, b, "a_less_c_eq_b.json" );
102+
103+
# Case: a = c < b
104+
gen( x, a, b, a, "a_eq_c_less_b.json" );
105+
106+
# Case: a = c = b
107+
gen( x, a, a, a, "a_eq_c_eq_b.json" );

lib/node_modules/@stdlib/stats/base/dists/triangular/mgf/test/fixtures/julia/small_range.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)