Skip to content

NDArray pins an empty-array repr NumPy does not produce, and go-ndarray v0.12.0 (which integrate/optimize v0.2.0 require) says so #822

Closed

Description

@tannevaled

The claim and the counter-evidence

internal/vm/ndarray_test.go pins

p NDArray.zeros(0)   # => array([])

and the file states, at the top, that "every expected value in this file is derived by hand from NumPy's definition of the operation — never by" running anything. For almost every row the NumPy expression is named in a comment beside it. This row is the one in that table with no such comment, and it is the one that is wrong.

NumPy's own test suite says the answer is array([], dtype=float64): an empty array always prints its dtype, because its elements cannot convey one.

The evidence is not my reading of NumPy. go-ndarray v0.12.0 ships display_numpy_test.go, whose header says the expected strings are "the expected strings of NumPy's own test suite, numpy/_core/tests/test_arrayprint.py at tag v2.5.3 … copied verbatim; the test each comes from is named beside it", and it holds:

{must(Zeros(0)), "array([], dtype=float64)",
    "an empty array prints [] and always its dtype; shape (0,) is implied"},
{must(Zeros(0, 3)), "array([], shape=(0, 3), dtype=float64)",
    "an empty array of any other shape also prints its shape"},

The release that introduced it is titled "Repr prints exactly what NumPy 2.5.3's repr prints" (go-ndarray#49) and covers 26 repr cases.

So on go-ndarray v0.12.0 the row fails, and the failure is rbgo's expectation being wrong rather than a regression upstream:

--- FAIL: TestNDArrayContract
    ndarray_test.go:221: src="p NDArray.zeros(0)"
         got="array([], dtype=float64)"
        want="array([])"

What this asks for

go-ndarray is at v0.12.0; this repository pins v0.11.0. Two things follow, and they are separable:

  1. The expectation is wrong on either version — array([]) is not what NumPy prints. Even without upgrading, the row should say so, or say explicitly that it records what v0.11.0 produces and that NumPy differs.
  2. The upgrade is more than this row. go-ndarray v0.12.0 reworks Repr against 26 NumPy cases; rbgo's contract test exercises a handful of them. Whoever upgrades should read that list rather than fixing failures one at a time — an empty array of another shape, for instance, gains shape=(0, 3), which nothing here tests yet.

There is a second reason to take the upgrade deliberately. go-numerics/integrate and /optimize are at v0.2.0, and both require go-ndarray v0.12.0. Go resolves to the maximum of all requirements, so adopting either v0.2.0 moves go-ndarray for NDArray, FFT, Special and Stats at once. #819 therefore pins those two at v0.1.0 and leaves this decision here, with its own witness.

module tag requires go-ndarray
go-numerics/special v0.1.0 v0.11.0
go-statistics/stats v0.1.0 v0.11.0
go-numerics/integrate v0.2.0 v0.12.0
go-numerics/optimize v0.2.0 v0.12.0

The two v0.2.0 releases are performance work whose own notes say results unchanged to the last bit, so the risk in the upgrade is concentrated in go-ndarray's printing, not in the numerics.

What has NOT been measured

I built the whole tree on go-ndarray v0.12.0 with integrate/optimize v0.2.0 and it compiles, and the NDArray/FFT/Special/Stats tests pass apart from the row above. I could not complete a full-suite run on that pair: this machine was carrying 30 concurrent agent sessions and a load average of 70, and the test binary was killed with signal: terminated at 208s — which is contention, not a verdict. So treat "only that one row fails" as a partial result from the numeric tests, not as a clean full-suite pass.

Measured 2026-10-17 with go1.27.2, CGO_ENABLED=0.

🤖 Generated with Claude Code

Activity

  1. tannevaled commented on Oct 17, 2026

    @tannevaled
    ContributorAuthor

    One thing measured since filing, which turns this from an optional upgrade into a scheduled one.

    main of all four modules already requires go-ndarray v0.12.0, while the tags this repository pins do not:

    module pinned tag → requires main → requires
    go-numerics/special v0.1.0 → v0.11.0 v0.12.0
    go-statistics/stats v0.1.0 → v0.11.0 v0.12.0
    go-numerics/integrate v0.1.0 → v0.11.0 (v0.2.0 → v0.12.0) v0.12.0
    go-numerics/optimize v0.1.0 → v0.11.0 (v0.2.0 → v0.12.0) v0.12.0

    (The tags' own go.mod files, read from the module cache, not from main — a higher tag can hold older code and the reverse is true here.)

    So the next tag any of the four cuts pulls this repository to v0.12.0, whether or not #819 or anything else asks for it. The array([]) expectation then fails on the first Renovate PR that bumps one of them, and the person looking at it will be reading a dependency bump rather than a printing change.

    Renovate is live in both organisations and is not the thing that is behind: it ran successfully on 14, 15, 16 and 17 October in each, and has opened zero pull requests because there was nothing to open — the four repositories' own main branches are already current. Zero Renovate PRs here means "nothing to do", not "not running", which is worth stating because the two look identical from the pull-request list.

    Nothing to change in this issue's conclusion; it just has a clock on it now.

    🤖 Generated with Claude Code

  2. tannevaled commented on Oct 18, 2026

    @tannevaled
    ContributorAuthor

    Resolved by #821, which did both halves of this rather than the easy one.

    The expectation is corrected to array([], dtype=float64).

    The read-across was done too, which was the part that mattered more here: #821 states that "the other literal reprs in the tests were already NumPy's", so the 26 repr cases were looked at as a set rather than fixed one failure at a time — which is what this issue asked for.

    Verified rather than taken on trust, on a branch now rebased onto that merge:

    • the whole NDArray contract suite passes on go-ndarray v0.12.0, so every literal repr the tests encode is NumPy's;
    • the only array([…]) left in prose is the README's ellipsis illustration (array([[0., 1.], ...])), which claims nothing exact;
    • the documentation site has none in prose, and the # => … examples it does carry are now run against a real interpreter by tools/docexample (docs#27), so an answer that drifts there fails rather than being read.

    The upgrade also removed the reason #819 pinned go-numerics/{integrate,optimize} at v0.1.0 — their v0.2.0 required v0.12.0 and would have dragged the numeric stack along with them. #819 now carries v0.2.0, and the 113 closed forms and 42 identities pass unchanged on the new pair, which is the check on those releases' own claim that results are unchanged to the last bit.

    Closing.

    🤖 Generated with Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions