Skip to content

Commit 2e66bec

Browse files
authored
feat: add accessor arrays support to blas/ext/base/gnansumkbn
PR-URL: #5032 Reviewed-by: Athan Reines <[email protected]>
1 parent feb71c7 commit 2e66bec

File tree

7 files changed

+358
-3
lines changed

7 files changed

+358
-3
lines changed

lib/node_modules/@stdlib/blas/ext/base/gnansumkbn/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ var v = gnansumkbn.ndarray( 5, x, 2, 1 );
109109
## Notes
110110

111111
- If `N <= 0`, both functions return `0.0`.
112+
- Both functions support array-like objects having getter and setter accessors for array element access (e.g., [`@stdlib/array/base/accessor`][@stdlib/array/base/accessor])
112113
- Depending on the environment, the typed versions ([`dnansumkbn`][@stdlib/blas/ext/base/dnansumkbn], [`snansumkbn`][@stdlib/blas/ext/base/snansumkbn], etc.) are likely to be significantly more performant.
113114

114115
</section>
@@ -185,6 +186,8 @@ console.log( v );
185186

186187
[mdn-typed-array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
187188

189+
[@stdlib/array/base/accessor]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/array/base/accessor
190+
188191
[@neumaier:1974a]: https://doi.org/10.1002/zamm.19740540106
189192

190193
[@stdlib/blas/ext/base/dnansumkbn]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/blas/ext/base/dnansumkbn

lib/node_modules/@stdlib/blas/ext/base/gnansumkbn/docs/types/index.d.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@
2020

2121
/// <reference types="@stdlib/types"/>
2222

23-
import { NumericArray } from '@stdlib/types/array';
23+
import { NumericArray, Collection, AccessorArrayLike } from '@stdlib/types/array';
24+
25+
/**
26+
* Input array.
27+
*/
28+
type InputArray = NumericArray | Collection<number> | AccessorArrayLike<number>;
2429

2530
/**
2631
* Interface describing `gnansumkbn`.
@@ -40,7 +45,7 @@ interface Routine {
4045
* var v = gnansumkbn( x.length, x, 1 );
4146
* // returns 1.0
4247
*/
43-
( N: number, x: NumericArray, strideX: number ): number;
48+
( N: number, x: InputArray, strideX: number ): number;
4449

4550
/**
4651
* Computes the sum of strided array elements, ignoring `NaN` values and using an improved Kahan–Babuška algorithm and alternative indexing semantics.
@@ -57,7 +62,7 @@ interface Routine {
5762
* var v = gnansumkbn.ndarray( x.length, x, 1, 0 );
5863
* // returns 1.0
5964
*/
60-
ndarray( N: number, x: NumericArray, strideX: number, offsetX: number ): number;
65+
ndarray( N: number, x: InputArray, strideX: number, offsetX: number ): number;
6166
}
6267

6368
/**

lib/node_modules/@stdlib/blas/ext/base/gnansumkbn/docs/types/test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* limitations under the License.
1717
*/
1818

19+
import AccessorArray = require( '@stdlib/array/base/accessor' );
1920
import gnansumkbn = require( './index' );
2021

2122

@@ -26,6 +27,7 @@ import gnansumkbn = require( './index' );
2627
const x = new Float64Array( 10 );
2728

2829
gnansumkbn( x.length, x, 1 ); // $ExpectType number
30+
gnansumkbn( x.length, new AccessorArray( x ), 1 ); // $ExpectType number
2931
}
3032

3133
// The compiler throws an error if the function is provided a first argument which is not a number...
@@ -85,6 +87,7 @@ import gnansumkbn = require( './index' );
8587
const x = new Float64Array( 10 );
8688

8789
gnansumkbn.ndarray( x.length, x, 1, 0 ); // $ExpectType number
90+
gnansumkbn.ndarray( x.length, new AccessorArray( x ), 1, 0 ); // $ExpectType number
8891
}
8992

9093
// The compiler throws an error if the `ndarray` method is provided a first argument which is not a number...
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
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 isnan = require( '@stdlib/math/base/assert/is-nan' );
24+
var abs = require( '@stdlib/math/base/special/abs' );
25+
26+
27+
// MAIN //
28+
29+
/**
30+
* Computes the sum of strided array elements, ignoring `NaN` values and using an improved Kahan–Babuška algorithm.
31+
*
32+
* ## Method
33+
*
34+
* - This implementation uses an "improved Kahan–Babuška algorithm", as described by Neumaier (1974).
35+
*
36+
* ## References
37+
*
38+
* - Neumaier, Arnold. 1974. "Rounding Error Analysis of Some Methods for Summing Finite Sums." _Zeitschrift Für Angewandte Mathematik Und Mechanik_ 54 (1): 39–51. doi:[10.1002/zamm.19740540106](https://doi.org/10.1002/zamm.19740540106).
39+
*
40+
* @private
41+
* @param {PositiveInteger} N - number of indexed elements
42+
* @param {Object} x - input array object
43+
* @param {Collection} x.data - input array data
44+
* @param {Array<Function>} x.accessors - array element accessors
45+
* @param {integer} strideX - stride length for `x`
46+
* @param {NonNegativeInteger} offsetX - starting index for `x`
47+
* @returns {number} sum
48+
*
49+
* @example
50+
* var toAccessorArray = require( '@stdlib/array/base/to-accessor-array' );
51+
* var arraylike2object = require( '@stdlib/array/base/arraylike2object' );
52+
*
53+
* var x = toAccessorArray( [ 2.0, 1.0, 2.0, -2.0, -2.0, 2.0, 3.0, 4.0 ] );
54+
*
55+
* var v = gnansumkbn( 4, arraylike2object( x ), 2, 1 );
56+
* // returns 5.0
57+
*/
58+
function gnansumkbn( N, x, strideX, offsetX ) {
59+
var xbuf;
60+
var xget;
61+
var sum;
62+
var ix;
63+
var v;
64+
var t;
65+
var c;
66+
var i;
67+
68+
// Cache reference to array data:
69+
xbuf = x.data;
70+
71+
// Cache reference to the element accessors:
72+
xget = x.accessors[ 0 ];
73+
74+
ix = offsetX;
75+
if ( strideX === 0 ) {
76+
if ( isnan( xget( xbuf, ix ) ) ) {
77+
return 0.0;
78+
}
79+
return N * xget( xbuf, ix );
80+
}
81+
sum = 0.0;
82+
c = 0.0;
83+
for ( i = 0; i < N; i++ ) {
84+
v = xget( xbuf, ix );
85+
if ( isnan( v ) === false ) {
86+
t = sum + v;
87+
if ( abs( sum ) >= abs( v ) ) {
88+
c += (sum-t) + v;
89+
} else {
90+
c += (v-t) + sum;
91+
}
92+
sum = t;
93+
}
94+
ix += strideX;
95+
}
96+
return sum + c;
97+
}
98+
99+
100+
// EXPORTS //
101+
102+
module.exports = gnansumkbn;

lib/node_modules/@stdlib/blas/ext/base/gnansumkbn/lib/ndarray.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020

2121
// MODULES //
2222

23+
var arraylike2object = require( '@stdlib/array/base/arraylike2object' );
2324
var isnan = require( '@stdlib/math/base/assert/is-nan' );
2425
var abs = require( '@stdlib/math/base/special/abs' );
26+
var accessors = require( './accessors.js' );
2527

2628

2729
// MAIN //
@@ -55,11 +57,16 @@ function gnansumkbn( N, x, strideX, offsetX ) {
5557
var v;
5658
var t;
5759
var c;
60+
var o;
5861
var i;
5962

6063
if ( N <= 0 ) {
6164
return 0.0;
6265
}
66+
o = arraylike2object( x );
67+
if ( o.accessorProtocol ) {
68+
return accessors( N, o, strideX, offsetX );
69+
}
6370
ix = offsetX;
6471
if ( strideX === 0 ) {
6572
if ( isnan( x[ ix ] ) ) {

lib/node_modules/@stdlib/blas/ext/base/gnansumkbn/test/test.main.js

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

2323
var tape = require( 'tape' );
2424
var Float64Array = require( '@stdlib/array/float64' );
25+
var toAccessorArray = require( '@stdlib/array/base/to-accessor-array' );
2526
var gnansumkbn = require( './../lib' );
2627

2728

@@ -73,6 +74,41 @@ tape( 'the function calculates the sum of strided array elements (ignoring NaN v
7374
t.end();
7475
});
7576

77+
tape( 'the function calculates the sum of strided array elements (ignoring NaN values, accessors)', function test( t ) {
78+
var x;
79+
var v;
80+
81+
x = [ 1.0, -2.0, -4.0, 5.0, 0.0, NaN, 3.0, 0.0, -3.0, 3.0 ];
82+
v = gnansumkbn( x.length, toAccessorArray( x ), 1 );
83+
t.strictEqual( v, 3.0, 'returns expected value' );
84+
85+
x = [ 1.0, -2.0, -4.0, NaN, 5.0, 0.0, 3.0 ];
86+
v = gnansumkbn( x.length, toAccessorArray( x ), 1 );
87+
t.strictEqual( v, 3.0, 'returns expected value' );
88+
89+
x = [ -4.0, NaN, -4.0 ];
90+
v = gnansumkbn( x.length, toAccessorArray( x ), 1 );
91+
t.strictEqual( v, -8.0, 'returns expected value' );
92+
93+
x = [ NaN, 4.0 ];
94+
v = gnansumkbn( x.length, toAccessorArray( x ), 1 );
95+
t.strictEqual( v, 4.0, 'returns expected value' );
96+
97+
x = [ NaN, NaN ];
98+
v = gnansumkbn( x.length, toAccessorArray( x ), 1 );
99+
t.strictEqual( v, 0.0, 'returns expected value' );
100+
101+
x = [ NaN ];
102+
v = gnansumkbn( x.length, toAccessorArray( x ), 1 );
103+
t.strictEqual( v, 0.0, 'returns expected value' );
104+
105+
x = [ 1.0, 1.0e100, 1.0, -1.0e100 ];
106+
v = gnansumkbn( x.length, toAccessorArray( x ), 1 );
107+
t.strictEqual( v, 2.0, 'returns expected value' );
108+
109+
t.end();
110+
});
111+
76112
tape( 'if provided an `N` parameter less than or equal to `0`, the function returns `0.0`', function test( t ) {
77113
var x;
78114
var v;
@@ -123,6 +159,29 @@ tape( 'the function supports a `stride` parameter', function test( t ) {
123159
t.end();
124160
});
125161

162+
tape( 'the function supports a `stride` parameter (accessors)', function test( t ) {
163+
var x;
164+
var v;
165+
166+
x = [
167+
1.0, // 0
168+
2.0,
169+
2.0, // 1
170+
-7.0,
171+
-2.0, // 2
172+
3.0,
173+
4.0, // 3
174+
2.0,
175+
NaN, // 4
176+
NaN
177+
];
178+
179+
v = gnansumkbn( 5, toAccessorArray( x ), 2 );
180+
181+
t.strictEqual( v, 5.0, 'returns expected value' );
182+
t.end();
183+
});
184+
126185
tape( 'the function supports a negative `stride` parameter', function test( t ) {
127186
var x;
128187
var v;
@@ -146,6 +205,29 @@ tape( 'the function supports a negative `stride` parameter', function test( t )
146205
t.end();
147206
});
148207

208+
tape( 'the function supports a negative `stride` parameter (accessors)', function test( t ) {
209+
var x;
210+
var v;
211+
212+
x = [
213+
NaN, // 4
214+
NaN,
215+
1.0, // 3
216+
2.0,
217+
2.0, // 2
218+
-7.0,
219+
-2.0, // 1
220+
3.0,
221+
4.0, // 0
222+
2.0
223+
];
224+
225+
v = gnansumkbn( 5, toAccessorArray( x ), -2 );
226+
227+
t.strictEqual( v, 5.0, 'returns expected value' );
228+
t.end();
229+
});
230+
149231
tape( 'if provided a `stride` parameter equal to `0`, the function returns the sum of the first element repeated N times', function test( t ) {
150232
var x;
151233
var v;
@@ -158,6 +240,18 @@ tape( 'if provided a `stride` parameter equal to `0`, the function returns the s
158240
t.end();
159241
});
160242

243+
tape( 'if provided a `stride` parameter equal to `0`, the function returns the sum of the first element repeated N times (accessors)', function test( t ) {
244+
var x;
245+
var v;
246+
247+
x = [ 1.0, -2.0, -4.0, 5.0, 3.0 ];
248+
249+
v = gnansumkbn( x.length, toAccessorArray( x ), 0 );
250+
t.strictEqual( v, 5.0, 'returns expected value' );
251+
252+
t.end();
253+
});
254+
161255
tape( 'if provided a `stride` parameter equal to `0` and the first element is NaN, the function returns 0', function test( t ) {
162256
var x;
163257
var v;
@@ -170,6 +264,18 @@ tape( 'if provided a `stride` parameter equal to `0` and the first element is Na
170264
t.end();
171265
});
172266

267+
tape( 'if provided a `stride` parameter equal to `0` and the first element is NaN, the function returns 0 (accessors)', function test( t ) {
268+
var x;
269+
var v;
270+
271+
x = [ NaN, -2.0, -4.0, 5.0, 3.0 ];
272+
273+
v = gnansumkbn( x.length, toAccessorArray( x ), 0 );
274+
t.strictEqual( v, 0.0, 'returns expected value' );
275+
276+
t.end();
277+
});
278+
173279
tape( 'the function supports view offsets', function test( t ) {
174280
var x0;
175281
var x1;

0 commit comments

Comments
 (0)