Fork me on GitHub

NumPy-style n-dimensional arrays in pure Go.
13 dtypes, multicore + SIMD.

go-ndarray is the numpy equivalent for Go: an n-dimensional array in 13 dtypes with creation routines, strided views that share data, broadcasting elementwise ops and ufuncs, reductions (incl. arg / cumulative / clip / where), manipulation, linear algebra (a panel-packed, cache-blocked GEMM, and numpy.linalg), numpy.fft and numpy.random — all in pure Go, with cgo disabled.

11 API families, NumPy's names6 SIMD targetsCGO=0 no BLAS shim
main.go
import "github.com/go-ndarray/ndarray"

a, _ := ndarray.Arange(0, 6, 1)     // [0 1 2 3 4 5]
m, _ := a.Reshape(2, -1)            // -1 inferred -> [[0 1 2] [3 4 5]]

row, _ := ndarray.FromData([]float64{10, 20, 30}, 3)
sum, _ := m.Add(row)                // broadcast (2,3)+(3,) -> (2,3)

t := m.Transpose()                  // zero-copy (3,2) view
cols, _ := m.SumAxis(0, false)      // sum down each column -> [3 5 7]

// NumPy basic-indexing views (share data):
col0, _ := m.Slice(ndarray.All(), ndarray.A(0))     // m[:,0] -> [0 3]

// linear algebra
b, _ := ndarray.Arange(0, 6, 1)
b, _ = b.Reshape(3, 2)
prod, _ := m.MatMul(b)              // (2,3)·(3,2) -> (2,2)
3 repositories — one cgo-free module that cross-compiles to a static binary on every target
Ask it, do not take our word

The example above, actually run.

The lines in the hero, built into a program against the published module and run. Views share data, broadcasting stretches row over both rows, and MatMul goes through the same cache-blocked SIMD kernel as a 4096² product. The output below is a real run, with nothing edited.

go run ./ndcheck — zsh
# go-ndarray v0.7.0 — each step of the example, printed
$ go run ./ndcheck
m         Array(shape=[2 3], data=[0 1 2 3 4 5])
m + row   Array(shape=[2 3], data=[10 21 32 13 24 35])
m.T       Array(shape=[3 2], data=[0 3 1 4 2 5])
sum(m,0)  Array(shape=[3], data=[3 5 7])
m[:,0]    Array(shape=[2], data=[0 3])
m @ b     Array(shape=[2 2], data=[10 13 28 40])
Why go-ndarray

The n-dimensional array, without cgo.

NumPy is the lingua franca of numerical computing, but in Go the choices are gonum (matrix-centric, amd64-only assembly) or a C extension. go-ndarray brings the n-dimensional array vocabulary — shapes, strided views, broadcasting, ufuncs, reductions and linear algebra — to Go with no cgo, cross-compiling to a static binary everywhere. The hot paths are multicore, and SIMD on amd64, arm64, ppc64le, loong64 and riscv64.

🐹

Pure Go

CGO_ENABLED=0 across the org. No cgo, no BLAS shim.

✅

100% test coverage

Gated in CI, differentially checked against numpy.

🔢

NumPy semantics

Row-major strided arrays, NumPy broadcasting, and NumPy basic-indexing Slice returning strided views that share data.

♻️

Workspace arena for loops

Bind the inputs with ws.Use, compute as usual, ws.Reset() at the end of each pass: results stop costing the garbage collector anything. On a Zen 3, x + y on 1 024 elements drops from 2.6 to 0.38 µs (2.1× NumPy).

⚡

SIMD on every 64-bit target

go-asmgen SIMD kernels on amd64 (SSE2, AVX2/FMA picked at run time), arm64 (NEON), ppc64le (VSX), loong64 (LASX, when the CPU has it), riscv64 (RVV, when the CPU has it) and s390x (vector facility, measured on a real IBM z17); the 32-bit targets run the same pure-Go code those kernels are tested against.

🎯

Accurate Exp/Log

Ports of Arm's optimized-routines: worst 0.504 / 0.508 ULP against a 300-bit reference, and correct where Go's amd64 math.Exp returns +Inf (golang/go#81995) and math.Log is wrong on subnormals (golang/go#56600).

🧪

Multi-arch CI

The suite runs on amd64, arm64 and 386 natively and on riscv64/loong64/ppc64le/s390x/arm under qemu, on Linux, macOS and Windows, and compiles for every GOOS/GOARCH pair Go supports; v0.2.0 was also run on real amd64, arm64, ppc64le, riscv64 and loong64 hardware.

💎

The cgo-free ndarray that was missing

Ruby has no cgo-free ndarray (Numo::NArray and NMatrix are C extensions) and gonum's optimized assembly is amd64-only. go-ndarray pairs a portable pure-Go core with multicore fan-out and SIMD kernels.

⚖️

BSD-3-Clause

Exp and Log are MIT-licensed ports of Arm's optimized-routines.

What the module offers

11 families, NumPy's names.

Dtypes

bool, int8…int64, uint8…uint64, float32/float64, complex64/complex128, chosen at run time with NumPy 2 promotion: AsType, Data[T], Item[T], weak Go scalars with Scalar.

Creation

Zeros, Ones, Full, Arange, Linspace, FromData, Eye and the rest of NumPy’s constructors, in any dtype (ZerosOf, FromSlice).

Views and indexing

Reshape, Transpose and NumPy basic indexing with Slice(All(), A(0), …): strided views that share data, no copy.

Broadcasting and ufuncs

Elementwise arithmetic with NumPy broadcasting, and the unary functions, including Exp and Log ported from Arm’s optimized-routines (≤ 0.51 ULP).

Reductions

Sum, Mean, Max, ArgMax, CumSum, Clip, Where, whole-array or along an axis, multicore on large arrays.

Manipulation

Concatenate, Stack and the shape operations NumPy users reach for.

Linear algebra

MatMul and Dot through a panel-packed, cache-blocked GEMM with a go-asmgen SIMD micro-kernel per architecture; package linalg is numpy.linalg (Solve, Inv, Det, QR, SVD, Eig, Lstsq, …), checked against numpy 2.5.3 on 1,040 cases.

Fourier transforms

Package fft is numpy.fft on arrays: FFT, RFFT, FFT2, FFTN and the rest, with n/s, axes and norm, over go-fft.

Random numbers

Package random is numpy.random with NumPy’s numbers for the same seed: PCG64, MT19937, Philox, SFC64, 40 samplers, and the legacy RandomState.

Notebooks and storage

Repr prints like NumPy, Display gives a notebook text and HTML, and MarshalBinary writes the same bytes on every architecture.

Workspace

An arena for loops: Use, Reset, Detach — results stop costing the garbage collector from one pass to the next.

100%
test coverage, differentially checked against numpy
2.3–4.5×
NumPy's speed on whole-array Sum/Mean/Max at 4 Mi (NumPy 2.5.3, 16-core Zen 3)
3.7–5.9×
single-threaded OpenBLAS 0.3.34 on MatMul from 512² (Zen 3)
≤ 0.51
ULP for Exp/Log, ports of Arm's optimized-routines

Measured, wins and losses. Against NumPy 2.5.3 + OpenBLAS 0.3.34 on a 16-core AMD Zen 3: faster on whole-array Sum/Mean/Max at 4 Mi (2.3–4.5×), SumAxis(1) (2.2×), MaxAxis(1) (1.7×), Exp from 256 Ki elements (2.4–5.6×) and MatMul against single-threaded OpenBLAS (3.7–5.9× from 512²); slower against 16-thread OpenBLAS (MatMul 0.42× at 512² and 0.51× at 1024², though 1.44× faster at 256²; Dot 0.69× and mat·vec 0.52×, all measured in one run with NumPy) and on allocating elementwise forms outside a Workspace. On Apple silicon the packed GEMM reaches tuned-BLAS parity at 1024² and beats the pure-Go gonum 4–10×. On the other targets, against its own scalar code on one core: on a RISC-V SpacemiT X60 the RVV kernels make Sum up to 5.8× faster and cut MatMul time by 31–45%; on an IBM z17 the s390x kernels take MatMul 1024² from 340 to 70 ms and cut Max by 92–96%.

Repositories

What is here.

ndarray

library · v0.17.0

The library, in 13 dtypes with NumPy 2 promotion. Creation (Zeros/Arange/Linspace/…), strided views, broadcasting elementwise + ufuncs, reductions (Sum/Max/ArgMax/CumSum/Clip/Where), manipulation (Reshape/Concatenate/Stack), linalg (MatMul/Dot) via a panel-packed cache-blocked GEMM with a go-asmgen SIMD-FMA micro-kernel (NEON 4×8 on arm64, AVX2/FMA 6×8 on amd64, VSX 8×8 on ppc64le, LASX 8×8 on loong64, RVV 6×8 on riscv64, 4×8 on s390x), and a Workspace arena for loops (Use/Reset/Detach). Packages linalg, fft and random are numpy.linalg, numpy.fft and numpy.random.

docs

site

Versioned documentation site (Hugo + Hextra): API reference, the roadmap, and honest benchmark pages versus NumPy 2.5.3 / OpenBLAS 0.3.34. Source →

brand

assets

Logos and icons for the organization, in SVG / PNG / JPG / ICO / ICNS across colour, white and black variants.

The cgo-free ndarray the Go and Ruby ecosystems lacked.

No cgo, no BLAS shim. A portable pure-Go core, multicore, with SIMD kernels on amd64, arm64, ppc64le, loong64, riscv64 and s390x and a Workspace arena that takes the garbage collector out of loops. Faster than NumPy on whole-array and row reductions, on large Exp and on MatMul against single-threaded BLAS, slower against 16-thread BLAS — the numbers are on the performance page.

Read the source on GitHub →