Skip to content

ndarray: accept axis: on reductions and cumsum - #883

Merged
tannevaled merged 3 commits into
mainfrom
ndarray-kwargs
Nov 4, 2026
Merged

tannevaled merged 3 commits into
mainfrom
ndarray-kwargs

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Problem

a.sum(axis: 0) raised unknown keyword: :axis, although the file header promises reductions "with an axis and keepdims:" and NumPy spells it axis=0.

Cause

Not issue #873. Natives do receive keyword arguments (ndKw splits the trailing Hash and validates names). The binding simply listed only keepdims as an accepted keyword for sum/prod/mean/max/min/argmax/argmin, took the axis only positionally, and cumsum took no keyword at all. The old test a.argmin(axis: 1) even asserted the error. The "unknown keyword" message here is produced by ndKw itself (ndarray.go), not generically for natives.

Related to #873 only in that ndKw also treats a positional trailing Hash as keywords (existing test concatenate([a], {"axis" => 0})); that remains until #873 is fixed.

Change

  • ndOptAxis: axis from either form, nil = no axis, both = ArgumentError.
  • sum/prod/mean/max/min/argmax/argmin/cumsum accept axis:; positional unchanged.
  • Header comment updated. Tests in ndarray_test.go: values by hand from NumPy, positional controls, error cases.

Before: a.sum(axis: 0) -> unknown keyword: :axis (ArgumentError)
After: array([3., 5., 7.])

go test ./internal/vm/ passes; ndOptAxis 100% covered.

🤖 Generated with Claude Code

tannevaled and others added 2 commits November 3, 2026 19:16
a.sum(axis: 0) raised "unknown keyword: :axis": the reductions
(sum/prod/mean/max/min/argmax/argmin) took keepdims: as their only keyword
and axis only positionally, and cumsum took no keyword at all. The file header
promised "reductions with an axis and keepdims:". Natives do receive keyword
arguments (ndKw splits the trailing Hash), so this is not issue 873; the
binding simply never listed axis among the accepted names.

ndOptAxis reads the axis from either form; nil means no axis; giving both is
an ArgumentError. The positional form is unchanged and covered by controls.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 20839f4 into main Nov 4, 2026
26 checks passed
@tannevaled
tannevaled deleted the ndarray-kwargs branch November 4, 2026 11:44
tannevaled added a commit to go-embedded-ruby/docs that referenced this pull request Nov 4, 2026
… since ruby#883 (#45)

The page listed the reductions and showed `a.sum`, so a reader had no way to
learn that `sum`/`prod`/`mean`/`max`/`min`/`argmax`/`argmin` reduce along an
axis at all, let alone that go-embedded-ruby/ruby#883 taught them NumPy's
`axis:` spelling. `cumsum` was missing from the list entirely.

Every figure in the new example was taken from a real run of rbgo built at
go-embedded-ruby/ruby@6eb7e4ad, not from reading the binding:

    p a.sum                      # => 15.0
    p a.sum(axis: 0).to_a        # => [3.0, 5.0, 7.0]
    p a.argmax(axis: 1).to_a     # => [2.0, 2.0]

The same run confirms the two statements the prose makes: `a.sum(0)` and
`a.sum(axis: 0)` return the same array, and `a.sum(0, axis: 0)` raises
ArgumentError("axis given both positionally and as a keyword").

numericsurface against that binary: 258 names over 11 module sections, 0
disagree.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
tannevaled added a commit that referenced this pull request Nov 4, 2026
… setter (#886) (#888)

Two sentences had fallen behind the code they describe.

**NDArray.** The line read "(axis, keepdims: true)", so the only documented way
to reduce along an axis was the positional one. #883 taught the reductions and
cumsum NumPy's `axis:` spelling, and cumsum was not in the sentence at all.
Verified against rbgo built at 6eb7e4a:

    a.sum                   => 15.0
    a.sum(axis: 0).to_a     => [3.0, 5.0, 7.0]
    a.sum(0).to_a           => [3.0, 5.0, 7.0]
    a.cumsum(axis: 1).to_a  => [0.0, 1.0, 3.0, 3.0, 7.0, 12.0]
    a.argmax(axis: 1).to_a  => [2.0, 2.0]
    a.sum(0, axis: 0)       => ArgumentError: axis given both positionally and
                               as a keyword

**Predefined globals.** The list claims each global gets "the setter MRI's
variable table gives it rather than one untyped slot", then groups `$;`, `$,`
and `$/` as though one setter covered them. Init_IO wires `$,` and `$\` to
rb_str_setter and `$/`/`$-0` to deprecated_rs_setter, and #886 made that split
real: `$/` now stores a frozen plain String of the bytes it was given instead
of the object itself. The README now says so, because that difference is
exactly the claim the sentence makes.

rubydiff against MRI 4.0.7, same program, five lines of output:

    AGREE   exit 0
      A rbgo-main   ruby 4.0.9 [arm64-darwin]
      B ruby@4.0    ruby 4.0.7 (2026-09-15 revision 229531a6cf) +PRISM

The same program under the system ruby 2.6.10 diverges on exactly the two
`$/` lines and agrees on the two `$,`/`$\` lines, which is the control: the
freezing is 4.0 behaviour, not something rbgo invented.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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