Skip to content

Commit 7a04cb9

Browse files
fix: address bug and improve C implementation of math/base/special/hyp2f1
PR-URL: #11353 Ref: #11325 Reviewed-by: Athan Reines <kgryte@gmail.com>
1 parent c274a49 commit 7a04cb9

File tree

3 files changed

+93
-2
lines changed

3 files changed

+93
-2
lines changed

lib/node_modules/@stdlib/math/base/special/hyp2f1/manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"@stdlib/math/base/special/ln",
4848
"@stdlib/math/base/special/abs",
4949
"@stdlib/math/base/special/pow",
50+
"@stdlib/math/base/special/max",
5051
"@stdlib/constants/float64/pinf",
5152
"@stdlib/constants/float64/nan"
5253
]
@@ -72,6 +73,7 @@
7273
"@stdlib/math/base/special/ln",
7374
"@stdlib/math/base/special/abs",
7475
"@stdlib/math/base/special/pow",
76+
"@stdlib/math/base/special/max",
7577
"@stdlib/constants/float64/pinf",
7678
"@stdlib/constants/float64/nan"
7779
]
@@ -97,6 +99,7 @@
9799
"@stdlib/math/base/special/ln",
98100
"@stdlib/math/base/special/abs",
99101
"@stdlib/math/base/special/pow",
102+
"@stdlib/math/base/special/max",
100103
"@stdlib/constants/float64/pinf",
101104
"@stdlib/constants/float64/nan"
102105
]

lib/node_modules/@stdlib/math/base/special/hyp2f1/src/main.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "stdlib/math/base/special/ln.h"
4040
#include "stdlib/math/base/special/abs.h"
4141
#include "stdlib/math/base/special/pow.h"
42+
#include "stdlib/math/base/special/max.h"
4243
#include "stdlib/math/base/assert/is_nan.h"
4344
#include "stdlib/constants/float64/pinf.h"
4445
#include "stdlib/constants/float64/nan.h"
@@ -235,6 +236,38 @@ static double hys2f1( double a, double b, const double c, const double x, double
235236
return s;
236237
}
237238

239+
/**
240+
* Evaluates 2F1(a, b; b; x) when `b = c` is a negative integer using AMS55 #15.4.2.
241+
*
242+
* @param a first parameter
243+
* @param b second parameter (equals c, a non-positive integer)
244+
* @param x argument
245+
* @return function value, or NaN if precision is insufficient
246+
*/
247+
static double hyp2f1NegCEqualBC( const double a, const double b, const double x ) {
248+
double collectorMax;
249+
double collector;
250+
double sum;
251+
double k;
252+
253+
collectorMax = 1.0;
254+
collector = 1.0;
255+
sum = 1.0;
256+
257+
if ( stdlib_base_abs( b ) >= 1.0e5 ) {
258+
return STDLIB_CONSTANT_FLOAT64_NAN;
259+
}
260+
for ( k = 1.0; k <= -b; k ++ ) {
261+
collector *= ( ( a + k - 1.0 ) * x ) / k;
262+
collectorMax = stdlib_base_max( stdlib_base_abs( collector ), collectorMax );
263+
sum += collector;
264+
}
265+
if ( 1.0e-16 * ( 1.0 + ( collectorMax / stdlib_base_abs( sum ) ) ) > 1.0e-7 ) {
266+
return STDLIB_CONSTANT_FLOAT64_NAN;
267+
}
268+
return sum;
269+
}
270+
238271
/**
239272
* Applies transformations for `|x|` near unity before performing a power series expansion.
240273
*
@@ -289,7 +322,7 @@ static double hyt2f1( const double a, const double b, const double c, const doub
289322
d = c - a - b;
290323
id = stdlib_base_round( d );
291324

292-
if ( x > 0.9 && !negIntA && !negIntB ) {
325+
if ( x > 0.85 && !negIntA && !negIntB ) {
293326
if ( isInteger( d ) == false ) {
294327
// Try the power series first:
295328
y = hys2f1( a, b, c, x, &err );
@@ -386,7 +419,11 @@ static double hyt2f1( const double a, const double b, const double c, const doub
386419
y = -y;
387420
}
388421
q = stdlib_base_pow( s, id );
389-
y = ( id > 0.0 ) ? y*q : y1*q;
422+
if ( id > 0.0 ) {
423+
y *= q;
424+
} else {
425+
y1 *= q;
426+
}
390427
y += y1;
391428
}
392429
*loss = err;
@@ -477,6 +514,11 @@ double stdlib_base_hyp2f1( const double a, const double b, const double c, const
477514
if ( ax < 1.0 || x == -1.0 ) {
478515
if ( b == c ) {
479516
// 2F1(a,b;b;x) = (1-x)**(-a):
517+
if ( negIntB ) {
518+
// For negative integer b=c use the finite polynomial (AMS55 #15.4.2):
519+
y = hyp2f1NegCEqualBC( a, b, x );
520+
return y;
521+
}
480522
y = stdlib_base_pow( s, -a );
481523
return y;
482524
}

lib/node_modules/@stdlib/math/base/special/hyp2f1/test/test.native.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ var edgeCases1 = require( './fixtures/python/edge_cases1.json' );
4343
var edgeCases2 = require( './fixtures/python/edge_cases2.json' );
4444
var edgeCases3 = require( './fixtures/python/edge_cases3.json' );
4545
var edgeCases4 = require( './fixtures/python/edge_cases4.json' );
46+
var edgeCases5 = require( './fixtures/python/edge_cases5.json' );
47+
var edgeCases6 = require( './fixtures/python/edge_cases6.json' );
4648
var outliers = require( './fixtures/python/outliers.json' );
4749

4850

@@ -254,6 +256,50 @@ tape( 'the function correctly evaluates the hypergeometric function', opts, func
254256
t.end();
255257
});
256258

259+
tape( 'the function correctly evaluates the hypergeometric function', function test( t ) {
260+
var expected;
261+
var a;
262+
var b;
263+
var c;
264+
var x;
265+
var v;
266+
var i;
267+
268+
a = edgeCases5.a;
269+
b = edgeCases5.b;
270+
c = edgeCases5.c;
271+
x = edgeCases5.x;
272+
expected = edgeCases5.expected;
273+
274+
for ( i = 0; i < x.length; i++ ) {
275+
v = hyp2f1( a[ i ], b[ i ], c[ i ], x[ i ] );
276+
t.strictEqual( isAlmostSameValue( v, expected[ i ], 12 ), true, 'returns expected value.' );
277+
}
278+
t.end();
279+
});
280+
281+
tape( 'the function correctly evaluates the hypergeometric function (edge case 6)', function test( t ) {
282+
var expected;
283+
var a;
284+
var b;
285+
var c;
286+
var x;
287+
var v;
288+
var i;
289+
290+
a = edgeCases6.a;
291+
b = edgeCases6.b;
292+
c = edgeCases6.c;
293+
x = edgeCases6.x;
294+
expected = edgeCases6.expected;
295+
296+
for ( i = 0; i < x.length; i++ ) {
297+
v = hyp2f1( a[ i ], b[ i ], c[ i ], x[ i ] );
298+
t.strictEqual( isAlmostSameValue( v, expected[ i ], 1 ), true, 'returns expected value.' );
299+
}
300+
t.end();
301+
});
302+
257303
tape( 'the function correctly evaluates the hypergeometric function', opts, function test( t ) {
258304
var expected;
259305
var a;

0 commit comments

Comments
 (0)