Skip to content

rstsr-core (feat): custom user reduction and cumulative scan functions - #111

Merged
ajz34 merged 3 commits into
RESTGroup:mainfrom
ajz34:261005/custom-reduce
Oct 5, 2026
Merged

ajz34 merged 3 commits into
RESTGroup:mainfrom
ajz34:261005/custom-reduce

Conversation

@ajz34

@ajz34 ajz34 commented Oct 5, 2026

Copy link
Copy Markdown
Member

Changes

New features

  • Add expert-level custom user reductions rt::reduce_all / rt::reduce_axes / rt::reduce_with_args (+ _f): user-provided init/fold/combine/finalize closures over a new OpReduceCustomAPI<T, TS, TO, D> device trait, with generic accumulator TS and output TO (e.g. an Lp-norm or a multi-accumulator mean in one pass).
  • Add rt::cumulative_sum / rt::cumulative_prod (+ _f, *_with_dtype::<TOut>, and TensorAny methods): array-api cumulative scans along a single axis, with CumulativeArgs { axis, include_initial } (include_initial prepends the identity value, growing the scan axis to M + 1).

Details

  • Custom reduce: closures are Send + Sync; combine must be associative — within one output cell elements fold in row-major order, but the combine tree shape is device-defined (parallel chunking on the rayon device). No TensorAny methods: expert-level surface only. reduce_all's docstring carries a "common reductions expressed via this function" table (sum/prod/mean/var/max/l2_norm/count_nonzero/all/any as fold spellings).
  • Cumulative follows the array-api contract: axis = None is valid for 1-D input only (n-D raises; np.cumsum's flatten-on-None is a documented intentional divergence, matching numpy.cumulative_sum), 0-D input is rejected, and dtype = None keeps the input dtype — NumPy/array-api's platform-integer widening is an intentional divergence; *_with_dtype casts per element inside the scan (no materialized cast copy) for the anti-overflow use case.
  • Scan kernels cumulative_cpu_serial / cumulative_cpu_rayon in rstsr-native-impl: per-line sequential fold, scan lines parallelized across the remaining axes; the output keeps the input's axis arrangement (K order). Rayon auto-impls stay shared with DeviceFaer through the existing symlink.
  • Docstring anchors are the non-fallible forms (per api-doc-conventions: the anchor is about failure mode, not return type).
  • Tests: NumPy transcriptions (TestCumsum/TestCumprod test_basic, test_cumulative_include_initial, tracked in numpy_coverage.csv with source hashes) plus custom args-overload / axis-contract / transposed-view / broadcast-axis / zero-size / with_dtype anti-overflow cases, doc-draft twins, and a DeviceFaer runtime test including the parallel-branch path; two divergence entries in numpy_differences.md. Gates: entry_row_cpu 341, lib (faer) 135, doc 194, clippy clean on default/rayon/faer.

PR summarized by

  • Agent: Claude Code
  • Model: glm-5.3

ajz34 and others added 3 commits October 5, 2026 21:57
…ce_with_args)

Expose the internal fold machinery as expert-level free functions over a new
`OpReduceCustomAPI<T, TS, TO, D>` device trait: generic accumulator `TS` and
output `TO`, user-provided init/fold/combine/finalize closures (`Send + Sync`;
`combine` must be associative, documented). No `TensorAny` methods --
expert-level surface only.

- `reduce_all(x, f_init, f, f_sum, f_out)` -> `TO`
- `reduce_axes(x, axes, ...)` -> per-axis tensor (`AxesIndex::None` = whole)
- `reduce_with_args(x, args, ...)` -> `ReduceArgs` (axes + keepdims)
- fallible `_f` twins; docstring anchors are the non-fallible forms
  (`reduce_all` carries a "common reductions expressed via this function"
  table: sum/prod/mean/var/max/l2_norm/count_nonzero/all-any)
- device impls: serial + rayon (the rayon auto_impl file is symlinked into
  device_faer, single source for both cfg contexts)

Tests: parity vs `sum`, tuple-accumulator mean, keepdims, Lp-norm fold,
non-numeric accumulators, zero-size; doc_draft twin; DeviceFaer runtime test.
entry_row_cpu 329, faer lib 134, doc 189, clippy clean (default/rayon/faer).

Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: glm-5.3 <service@zhipuai.cn>
Add the array-api cumulative functions (scan along a single axis; output
keeps the input shape, one larger along the scan axis with
`include_initial`):

- `rt::cumulative_sum` / `rt::cumulative_prod` (+ `_f`, `*_with_dtype`(+-f),
  `TensorAny` methods) over `CumulativeArgs { axis, include_initial }`;
  `axis = None` is only valid for 1-D input (array-api contract; the
  `np.cumsum` flatten-on-None divergence is documented as intentional), 0-D
  input is rejected; `dtype = None` keeps the input dtype (no
  platform-integer widening; `*_with_dtype` is the anti-overflow path,
  cast-per-element inside the scan)
- device traits `OpCumSumAPI`/`OpCumProdAPI` (`TOut = T`) and
  `OpCumSumDtypeAPI`/`OpCumProdDtypeAPI` (explicit `TOut`)
- scan kernels `cumulative_cpu_serial` / `cumulative_cpu_rayon` in
  rstsr-native-impl: per-line sequential fold seeded by the identity, scan
  lines parallelized across the remaining axes; output keeps the input's
  axis arrangement (K order, grown-axis probe via `Layout::new_unchecked`)
- device impls: serial + rayon (the rayon auto_impl file is symlinked into
  device_faer, single source for both cfg contexts)

Tests: NumPy transcriptions (TestCumsum/TestCumprod `test_basic`,
`test_cumulative_include_initial`), custom args-overload / axis-None
contract / transposed-view / broadcast-axis / zero-size / with_dtype
anti-overflow cases; doc_draft twins (byte-identical Display asserts);
DeviceFaer runtime test incl. the parallel-branch path. Tracking rows +
two divergence entries (flatten-on-None, no auto-widening);
api_specification.md paragraph.

entry_row_cpu 341, faer lib 135, doc 194, clippy clean (default/rayon/faer).

Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: glm-5.3 <service@zhipuai.cn>
DeviceFaer::default() follows the global default order, which is ColMajor
under the col_major feature; the arange/into_shape fixture then fills
column-major and the expected values shift. Pin RowMajor like the other
order-dependent faer tests.

Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: glm-5.3 <service@zhipuai.cn>
@ajz34
ajz34 merged commit cc65a48 into RESTGroup:main Oct 5, 2026
13 checks passed
@ajz34
ajz34 deleted the 261005/custom-reduce branch October 5, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant