Skip to content

NDArray: the go-ndarray playground's Ruby API, on go-ndarray v0.11.0 - #813

Merged
tannevaled merged 1 commit into
mainfrom
ndarray-playground-contract
Oct 11, 2026
Merged

tannevaled merged 1 commit into
mainfrom
ndarray-playground-contract

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

What

The go-ndarray playground shows each pipeline as Go | Ruby | NumPy and runs the Ruby column in the browser through cmd/wasm. This PR implements the Ruby NDArray API its code generator emits (the playground contract), in internal/vm/ndarray.go.

Area Added or changed
Creation arange(stop) / (start, stop) / (start, stop, step), linspace(start, stop, num = 50), eye(n, m = n, k = 0), zeros()/ones() (0-d with no shape; a shape Array is accepted too), full(value, *shape)
Views reshape (with -1), T, squeeze(*axes), expand_dims(axis), dup/copy (a copy of the data)
Indexing a[...], NumPy basic indexing: Integer, true for :, a Range (1...3, 1.., ...3, inclusive 1..2 / 1..-1) or an Enumerator::ArithmeticSequence ((0..) % 2, (1...7) % 3, (-1..) % -1, (5...1) % -2, 1.step(7, 3)). All-Integer, one per axis, returns the element as a Float.
Elementwise square, ** (scalar; array with broadcasting; 2 ** a through NDArray#coerce), clip, NDArray.maximum/minimum (a number on either side broadcasts)
Reductions sum/mean/max/min/prod/argmax/argmin take (axis, keepdims:); with no axis they return a Float, or an Integer flat index for argmax/argmin. cumsum / cumsum(axis)
Products and joins NDArray.outer, NDArray.concatenate([...], axis:), NDArray.stack([...], axis:)
Inspecting p a / inspect / to_s print go-ndarray's NumPy-style Repr (array([[0., 1., 2.],\n [3., 4., 5.]])) instead of Array(shape=[2 3], data=[...])

Bugs fixed

  • NDArray.arange(5) raised ArgumentError: arange: runtime error: index out of range [1] with length 1, which was a recovered Go panic. NDArray.full() failed the same way. Every method now checks its arguments. A wrong count raises ArgumentError (MRI's wrong number of arguments (given n, expected m)), and a wrong type raises TypeError. A go-ndarray error is re-raised with go-ndarray's own message: IndexError for an index or axis out of range, ArgumentError for the rest.
  • TestNativeFaultBecomesRubyError used NDArray.arange(6) as its example of a faulting native. It now lives in native_fault_internal_test.go and plants its own faulting native, so the safety net no longer depends on a binding staying broken.
  • The README said in three places that the browser target GOOS=js GOARCH=wasm "does not build". It has built since internal/vm, ci: fix the js/wasm build, and make the guard that caught it reachable #684. readme_wasm_lane_test.go now compares a flattened README, so the reworded claims are caught across line breaks and bold markers. Checked: the test fails on the old README.

Dependency

github.com/go-ndarray/ndarray goes from v0.3.0 to v0.11.0, and github.com/go-fft/fft from v0.1.14 to v0.23.0 with it. This is done here rather than through the grouped Renovate PR #792, which is still dirty and is not touched.

How the expectations were derived

  • Every expected value in ndarray_test.go was worked out by hand from NumPy's definition of the operation. None was taken from the binding's own output.
  • TestNDArraySlicing walks the contract's indexing table row by row, plus inclusive ranges, negative steps, clamping, empty slices and multi-axis indexing. Each row holds three things that must agree: the Ruby index, the hand-derived NumPy elements and shape, and the same slice taken through go-ndarray's Go API (Slice(A/All/R/Rng/From/To/Step)).
  • TestNDArrayMatchesGoOracle compares p output with go-ndarray's Repr of the same computation done in Go. go-ndarray's Go API is the oracle the playground's Go column uses.
  • array ** array has no go-ndarray counterpart. It is checked against elementwise math.Pow after broadcasting.
  • TestNDArrayArgumentChecks calls every method with too few arguments, too many, and wrong types. It asserts the exception class and message, and that no message contains runtime error.
  • ndarray.go is at 100% coverage. The ToS row leaves both BELOW100 records.
  • web/smoke.mjs now runs an NDArray program in the JS host, which is the playground's use case.

Known differences from NumPy (go-ndarray's, not the binding's)

  • Repr formats floats in their shortest form, so linspace(0, 1, 5) prints array([ 0., 0.25, 0.5, 0.75, 1.]). NumPy prints array([0. , 0.25, 0.5 , 0.75, 1. ]). Large arrays also get a , shape=(...) suffix.
  • clip(lo, hi) with lo > hi raises ArgumentError (go-ndarray's error). NumPy returns hi everywhere.
  • argmax(axis) returns an int64 array, as NumPy does. to_a still yields Floats, as the contract requires.

🤖 Generated with Claude Code

The go-ndarray playground shows each pipeline as Go | Ruby | NumPy and runs the
Ruby column in the browser through cmd/wasm. This implements the API its code
generator emits (ruby-ndarray-contract.md):

- creation: arange(stop | start, stop | start, stop, step), linspace, eye(n, m, k),
  zeros()/ones() (0-d with no shape, or a shape Array), full(value, *shape)
- views: reshape (with -1), T, squeeze(*axes), expand_dims, dup/copy (copy of
  the data), and NumPy basic indexing through a[...]: Integer, true for ':', a
  Range (exclusive, inclusive, endless, beginless) or an
  Enumerator::ArithmeticSequence ((0..) % 2, (-1..) % -1, (5...1) % -2)
- elementwise: square, ** (scalar, array with broadcasting, and 2 ** a through
  NDArray#coerce), clip, NDArray.maximum/minimum
- reductions: sum/mean/max/min/prod/argmax/argmin with (axis, keepdims:), cumsum
- products and joins: NDArray.outer, NDArray.concatenate/stack(axis:)
- p/inspect/to_s print go-ndarray's NumPy-style Repr

Every method now checks its arguments: a wrong count raises ArgumentError, a
wrong type TypeError, an index or axis out of range IndexError, other go-ndarray
errors ArgumentError with go-ndarray's message. NDArray.arange(5) and
NDArray.full() used to raise "ArgumentError: arange: runtime error: index out of
range [1] with length 1", a recovered Go panic. TestNativeFaultBecomesRubyError
relied on that bug; it now plants its own faulting native.

go-ndarray v0.3.0 -> v0.11.0 (and go-fft v0.1.14 -> v0.23.0 with it), outside
the grouped Renovate PR #792.

The README no longer says the browser target does not build (it has since
#684), in three places; readme_wasm_lane_test.go now catches that wording across
line breaks. web/smoke.mjs runs an NDArray program in the JS host.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 3c86317 into main Oct 11, 2026
9 checks passed
@tannevaled
tannevaled deleted the ndarray-playground-contract branch October 11, 2026 17:22
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