Skip to content

feat: add C implementation for blas/ext/base/ndarray/swhere - #16017

Open
yb175 wants to merge 4 commits into
stdlib-js:developfrom
yb175:feat/ndarray-swhere-native-c
Open

yb175 wants to merge 4 commits into
stdlib-js:developfrom
yb175:feat/ndarray-swhere-native-c

Conversation

@yb175

@yb175 yb175 commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Resolves a part of #14819

Description

What is the purpose of this pull request?

This pull request:

  • Adds a native C ndarray implementation for swhere, using the existing strided C routine.
  • Adds the Node.js addon and uses the native implementation when available, with the JavaScript implementation as a fallback.
  • Adds native tests, benchmarks, a C example, and C API documentation.

Related Issues

Does this pull request have any related issues?

This pull request has the following related issues:

Questions

Any questions for reviewers of this pull request?

No.

Other

Any other information relevant to this pull request? This may include screenshots, references, and/or implementation notes.

No.

Checklist

Please ensure the following tasks are completed before submitting this pull request.

AI Assistance

When authoring the changes proposed in this PR, did you use any kind of AI assistance?

  • Yes
  • No

If you answered "yes" above, how did you use AI assistance?

  • Code generation (e.g., when writing an implementation or fixing a bug)
  • Test/benchmark generation
  • Documentation (including examples)
  • Research and understanding

Disclosure

If you answered "yes" to using AI assistance, please provide a short disclosure indicating how you used AI assistance. This helps reviewers determine how much scrutiny to apply when reviewing your contribution. Example disclosures: "This PR was written primarily by Claude Code." or "I consulted ChatGPT to understand the codebase, but the proposed changes were fully authored manually by myself.".

used AI for analyzing pattern of recently merged PRs and test and benchmarking generation.


@stdlib-js/reviewers

@yb175
yb175 requested a review from a team October 9, 2026 20:00
@stdlib-bot stdlib-bot added BLAS Issue or pull request related to Basic Linear Algebra Subprograms (BLAS). Needs Review A pull request which needs code review. labels Oct 9, 2026
@stdlib-bot

stdlib-bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Report

Package Statements Branches Functions Lines
blas/ext/base/ndarray/swhere $\\color{green}208/208$
$\\color{green}+0.00\\%$
$\\color{green}7/7$
$\\color{green}+0.00\\%$
$\\color{green}2/2$
$\\color{green}+0.00\\%$
$\\color{green}208/208$
$\\color{green}+0.00\\%$

The above coverage report was generated for the changes in this PR.

@ujjwalv01 ujjwalv01 left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! The approach for passing the boolean condition to C looks good. I've left a few line-wise suggestions to match the conventions used in the other blas/ext/base/ndarray add-ons (e.g., dxpy). Mind making these changes?

Comment on lines +25 to +27
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition and assigns them to an output ndarray.
*
* @param arrays list containing a condition, two input ndarrays, and an output ndarray

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition and assigns them to an output ndarray.
*
* @param arrays list containing a condition, two input ndarrays, and an output ndarray
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition.
*
* ## Notes
*
* - The function expects the following ndarrays:
*
* - a one-dimensional condition ndarray.
* - first one-dimensional input ndarray.
* - second one-dimensional input ndarray.
* - a one-dimensional output ndarray.
*
* @param arrays list containing ndarrays

The description should match lib/main.js verbatim, and the Notes list matches other add-ons (e.g., blas/ext/base/ndarray/dxpy).

Comment on lines +30 to +49
const struct ndarray *condition = arrays[ 0 ];
const struct ndarray *x = arrays[ 1 ];
const struct ndarray *y = arrays[ 2 ];
const struct ndarray *out = arrays[ 3 ];

API_SUFFIX(stdlib_strided_swhere_ndarray)(
stdlib_ndarray_dimension( condition, 0 ),
(const bool *)stdlib_ndarray_data( condition ),
stdlib_ndarray_stride_elements( condition, 0 ),
stdlib_ndarray_offset_elements( condition ),
(const float *)stdlib_ndarray_data( x ),
stdlib_ndarray_stride_elements( x, 0 ),
stdlib_ndarray_offset_elements( x ),
(const float *)stdlib_ndarray_data( y ),
stdlib_ndarray_stride_elements( y, 0 ),
stdlib_ndarray_offset_elements( y ),
(float *)stdlib_ndarray_data( out ),
stdlib_ndarray_stride_elements( out, 0 ),
stdlib_ndarray_offset_elements( out )
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const struct ndarray *condition = arrays[ 0 ];
const struct ndarray *x = arrays[ 1 ];
const struct ndarray *y = arrays[ 2 ];
const struct ndarray *out = arrays[ 3 ];
API_SUFFIX(stdlib_strided_swhere_ndarray)(
stdlib_ndarray_dimension( condition, 0 ),
(const bool *)stdlib_ndarray_data( condition ),
stdlib_ndarray_stride_elements( condition, 0 ),
stdlib_ndarray_offset_elements( condition ),
(const float *)stdlib_ndarray_data( x ),
stdlib_ndarray_stride_elements( x, 0 ),
stdlib_ndarray_offset_elements( x ),
(const float *)stdlib_ndarray_data( y ),
stdlib_ndarray_stride_elements( y, 0 ),
stdlib_ndarray_offset_elements( y ),
(float *)stdlib_ndarray_data( out ),
stdlib_ndarray_stride_elements( out, 0 ),
stdlib_ndarray_offset_elements( out )
);
const struct ndarray *condition = arrays[ 0 ];
const struct ndarray *x = arrays[ 1 ];
const struct ndarray *y = arrays[ 2 ];
const struct ndarray *out = arrays[ 3 ];
const CBLAS_INT N = stdlib_ndarray_dimension( condition, 0 );
const CBLAS_INT strideC = stdlib_ndarray_stride_elements( condition, 0 );
const CBLAS_INT offsetC = stdlib_ndarray_offset_elements( condition );
const CBLAS_INT strideX = stdlib_ndarray_stride_elements( x, 0 );
const CBLAS_INT offsetX = stdlib_ndarray_offset_elements( x );
const CBLAS_INT strideY = stdlib_ndarray_stride_elements( y, 0 );
const CBLAS_INT offsetY = stdlib_ndarray_offset_elements( y );
const CBLAS_INT strideOut = stdlib_ndarray_stride_elements( out, 0 );
const CBLAS_INT offsetOut = stdlib_ndarray_offset_elements( out );
const bool *dataC = (const bool *)stdlib_ndarray_data( condition );
const float *dataX = (const float *)stdlib_ndarray_data( x );
const float *dataY = (const float *)stdlib_ndarray_data( y );
float *dataOut = (float *)stdlib_ndarray_data( out );
API_SUFFIX(stdlib_strided_swhere_ndarray)( N, dataC, strideC, offsetC, dataX, strideX, offsetX, dataY, strideY, offsetY, dataOut, strideOut, offsetOut );

Same layout as blas/ext/base/ndarray/dxpy/src/main.c.

#endif

/**
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition and assigns them to an output ndarray.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition and assigns them to an output ndarray.
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition.

Comment on lines +32 to +36
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition.
*
* @private
* @param {ArrayLikeObject<Object>} arrays - array-like object containing ndarrays
* @returns {ndarray} output ndarray

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition.
*
* @private
* @param {ArrayLikeObject<Object>} arrays - array-like object containing ndarrays
* @returns {ndarray} output ndarray
* Takes elements from one of two one-dimensional single-precision floating-point ndarrays depending on a condition.
*
* ## Notes
*
* - The function expects the following ndarrays:
*
* - a one-dimensional condition ndarray.
* - first one-dimensional input ndarray.
* - second one-dimensional input ndarray.
* - a one-dimensional output ndarray.
*
* @private
* @param {ArrayLikeObject<Object>} arrays - array-like object containing ndarrays
* @returns {ndarray} output ndarray
*
* @example
* var BooleanVector = require( '@stdlib/ndarray/vector/bool' );
* var Float32Vector = require( '@stdlib/ndarray/vector/float32' );
*
* var condition = new BooleanVector( [ true, false, true, false, true ] );
* var x = new Float32Vector( [ 1.0, 2.0, 3.0, 4.0, 5.0 ] );
* var y = new Float32Vector( [ 6.0, 7.0, 8.0, 9.0, 10.0 ] );
* var out = new Float32Vector( 5 );
*
* var v = swhere( [ condition, x, y, out ] );
* // returns <ndarray>[ 1.0, 7.0, 3.0, 9.0, 5.0 ]

Notes and example copied from lib/main.js, as in blas/ext/base/ndarray/dxpy/lib/native.js.

Comment on lines 112 to +114
<!-- /.examples -->

<section class="c">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<!-- /.examples -->
<section class="c">
<!-- /.examples -->
<!-- C interface documentation. -->
* * *
<section class="c">


```c
const struct ndarray *arrays[] = { condition, x, y, out };
stdlib_blas_ext_swhere( arrays );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
stdlib_blas_ext_swhere( arrays );
#include "stdlib/ndarray/ctor.h"
#include "stdlib/ndarray/dtypes.h"
#include "stdlib/ndarray/index_modes.h"
#include "stdlib/ndarray/orders.h"
#include "stdlib/ndarray/base/bytes_per_element.h"
#include <stdbool.h>
#include <stdint.h>
// Create ndarrays:
const bool dataC[] = { true, false, true, false, true };
const float dataX[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
const float dataY[] = { 6.0f, 7.0f, 8.0f, 9.0f, 10.0f };
float dataOut[] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
int64_t shape[] = { 5 };
int64_t cstrides[] = { STDLIB_NDARRAY_BOOL_BYTES_PER_ELEMENT };
int64_t strides[] = { STDLIB_NDARRAY_FLOAT32_BYTES_PER_ELEMENT };
int8_t submodes[] = { STDLIB_NDARRAY_INDEX_ERROR };
struct ndarray *condition = stdlib_ndarray_allocate( STDLIB_NDARRAY_BOOL, (uint8_t *)dataC, 1, shape, cstrides, 0, STDLIB_NDARRAY_ROW_MAJOR, STDLIB_NDARRAY_INDEX_ERROR, 1, submodes );
struct ndarray *x = stdlib_ndarray_allocate( STDLIB_NDARRAY_FLOAT32, (uint8_t *)dataX, 1, shape, strides, 0, STDLIB_NDARRAY_ROW_MAJOR, STDLIB_NDARRAY_INDEX_ERROR, 1, submodes );
struct ndarray *y = stdlib_ndarray_allocate( STDLIB_NDARRAY_FLOAT32, (uint8_t *)dataY, 1, shape, strides, 0, STDLIB_NDARRAY_ROW_MAJOR, STDLIB_NDARRAY_INDEX_ERROR, 1, submodes );
struct ndarray *out = stdlib_ndarray_allocate( STDLIB_NDARRAY_FLOAT32, (uint8_t *)dataOut, 1, shape, strides, 0, STDLIB_NDARRAY_ROW_MAJOR, STDLIB_NDARRAY_INDEX_ERROR, 1, submodes );
// Perform computation:
const struct ndarray *arrays[] = { condition, x, y, out };
stdlib_blas_ext_swhere( arrays );
// Free allocated memory:
stdlib_ndarray_free( condition );
stdlib_ndarray_free( x );
stdlib_ndarray_free( y );
stdlib_ndarray_free( out );

The usage block should show a full example, as in blas/ext/base/ndarray/dxpy/README.md.

Comment on lines +142 to +144
The function accepts the following argument:

- **arrays**: `[in] struct ndarray**` list containing a one-dimensional boolean condition ndarray, two one-dimensional single-precision floating-point input ndarrays, and a one-dimensional single-precision floating-point output ndarray.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The function accepts the following argument:
- **arrays**: `[in] struct ndarray**` list containing a one-dimensional boolean condition ndarray, two one-dimensional single-precision floating-point input ndarrays, and a one-dimensional single-precision floating-point output ndarray.
The function accepts the following arguments:
- **arrays**: `[in] struct ndarray**` list containing the following ndarrays:
- `[in] struct ndarray*` a one-dimensional condition ndarray.
- `[in] struct ndarray*` first one-dimensional input ndarray.
- `[in] struct ndarray*` second one-dimensional input ndarray.
- `[out] struct ndarray*` a one-dimensional output ndarray.


<section class="examples">

### Examples

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C Examples section is empty. It should contain the code from examples/c/example.c (minus the // cppcheck-suppress lines), as in blas/ext/base/ndarray/dxpy/README.md.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind restructuring this to match the other add-on examples (e.g., blas/ext/base/ndarray/dxpy/examples/c/example.c): one commented step per setting (ndims, shape, strides, offset, order, index mode), // cppcheck-suppress invalidPointerCast on its own line above each stdlib_ndarray_allocate, and exit( 1 ) on allocation failure instead of freeing and returning.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here: mind following the structure of blas/ext/base/ndarray/swax/benchmark/c/benchmark.length.c: variables declared at the top, random_uniform, the should not return NaN checks, and no goto?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test/test.js should only contain the main-export and native/JS selection tests (see blas/ext/base/ndarray/dxpy/test/test.js). The remaining tests (arity, condition selection, strides, offsets, empty ndarrays) already live in test.main.js and test.native.js, so mind removing them from here?

cbuf = new BooleanArray( [ false, true, false, true, false, false ] );
xbuf = new Float32Array( [ 1.0, 100.0, 2.0, 100.0, 3.0, 100.0, 4.0 ] );
ybuf = new Float32Array( [ 40.0, 30.0, 20.0, 10.0 ] );
obuf = new Float32Array( 12 );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
obuf = new Float32Array( 12 );
obuf = new Float32Array( [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] );

The moved tests should stay the same as test/test.js on develop, with only the require changed. The comment edits in this test (// cbuf[4] = false => y[3] → // false => y[3]) should be reverted as well. Same in test.native.js.

@ujjwalv01 ujjwalv01 added Needs Changes Pull request which needs changes before being merged. and removed Needs Review A pull request which needs code review. labels Oct 10, 2026
@yb175

yb175 commented Oct 10, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @ujjwalv01 for reviewing the PR , I have addressed the requested changes
do lemme know if anything else needed from my side 😊

*/
static void print_summary( int total, int passing ) {
printf( "#\n" );
printf( "1..%d\n", total );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
printf( "1..%d\n", total );
printf( "1..%d\n", total ); // TAP plan

Keep the shared boilerplate unchanged.

}

/**
* Prints benchmark results.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Prints benchmark results.
* Prints benchmarks results.

t.strictEqual( swhere( [ c, x, y, out ] ), out, 'returns the output ndarray' );
t.strictEqual( isSameFloat32Array( obuf, new Float32Array( [ 1, 5, 3 ] ) ), true, 'selects the expected values' );
t.end();
});

@ujjwalv01 ujjwalv01 Oct 10, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
});

test.native.js should mirror test/test.js on develop (only the native wiring changes), so mind removing this extra test L394 - L415 ?

var tape = require( 'tape' );
var isSameFloat32Array = require( '@stdlib/assert/is-same-float32array' );
var Float32Array = require( '@stdlib/array/float32' );
var Uint8Array = require( '@stdlib/array/uint8' );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var Uint8Array = require( '@stdlib/array/uint8' );

Not needed once the extra test is removed.


## C APIs

<section class="intro">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<section class="intro">
<!-- Section to include introductory text. Make sure to keep an empty line after the intro `section` element and another before the `/section` close. -->
<section class="intro">

Keep the standard comments, as in dxpy.


<!-- /.intro -->

<section class="usage">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<section class="usage">
<!-- C usage documentation. -->
<section class="usage">


<!-- /.usage -->

<section class="notes">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<section class="notes">
<!-- C API usage notes. Make sure to keep an empty line after the `section` element and another before the `/section` close. -->
<section class="notes">


<!-- /.notes -->

<section class="examples">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<section class="examples">
<!-- C API usage examples. -->
<section class="examples">

@ujjwalv01

Copy link
Copy Markdown
Member

Thanks for the updates, looks much better! A few small leftovers. Mind making these?

@yb175

yb175 commented Oct 11, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the updates, looks much better! A few small leftovers. Mind making these?

addressed the remaining comments.

@ujjwalv01 ujjwalv01 added Feature Issue or pull request for adding a new feature. Needs Review A pull request which needs code review. C Issue involves or relates to C. and removed Needs Changes Pull request which needs changes before being merged. labels Oct 11, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BLAS Issue or pull request related to Basic Linear Algebra Subprograms (BLAS). C Issue involves or relates to C. Feature Issue or pull request for adding a new feature. Needs Review A pull request which needs code review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants