# gosl: numerical computing in Go that admits it is a wrapper

> Gosl does not try to beat BLAS in Go; it calls OpenBLAS, LAPACK, UMFPACK, MUMPS, QUADPACK and FFTW3 and puts a Go API on top. The README also says plainly that the library is in maintenance mode and points new projects elsewhere.

**cpmech/gosl** — Linear algebra, eigenvalues, FFT, Bessel, elliptic, orthogonal polys, geometry, NURBS, numerical quadrature, 3D transfinite interpolation, random numbers, Mersenne twister, probability distributions, optimisation, differential equations.

- Repository: https://github.com/cpmech/gosl
- Stars: 1,884 · Forks: 148
- Language: Go
- License: BSD-3-Clause
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/cpmech-gosl

## The performance argument is stated against itself

Most scientific Go libraries eventually have to explain why they exist when a Fortran library already does the job. Gosl answers that question before you ask it, and the answer is unusually blunt: a naive Go matrix-matrix multiplication runs more than 100 times slower than OpenBLAS in the project's own benchmark.

That number is from the maintainer's benchmark, not an independent measurement, but it sets the design constraint honestly. Gosl links against OpenBLAS, LAPACK, UMFPACK, MUMPS, QUADPACK, and FFTW3, and the reason is that a rewrite in native Go is unlikely to match their speed.

So the value Gosl adds is not performance, it is interface. You get the same numerical kernels behind Go types, so a simulation written in Gosl is a normal Go module, imports cleanly, and participates in the same build and test tooling as the rest of your service. The heavy lifting happens in C and Fortran, and the price is that your build environment now has to contain those libraries.

The `go.mod` is a single line beyond the module path, declaring `go 1.17`, and the README notes Gosl has no Go dependencies. Everything it brings in arrives through cgo.

## Maintenance mode is the first fact to read

The Status section states that Gosl is mature and in maintenance mode, that it is stable and used in published work, and that new development has moved to a successor project called Russell. Reports about existing functionality are welcome; new features are unlikely.

That does not make it a bad dependency. Stable code used in published work has a property that actively developed code does not: it is unlikely to change under you. If you are maintaining a simulation and the numerics already work, maintenance mode is a feature.

It does change what you should do with it. If you are choosing a library today, the README tells you to look at Russell first. If you are maintaining something that already depends on gosl, you are in the supported case: the code is stable and reports are accepted.

The release history reflects the same story. v1.2.12 shipped on 2023-05-31 and the next release, v1.2.13, came on 2026-09-28 with the note that it was the first since 2023 and carried a UMFPACK build fix. v1.2.14 followed the same day with a Docker toolchain and a rewritten README and no code changes. The last push was on 2026-09-28. That is a project that resumed shipping in order to fix a build and improve its onboarding, not one that is adding capability.

## Docker is the recommended path, and the ./gosl wrapper hides the toolchain

Because Gosl links against native libraries, the README makes Docker the easiest route and calls it the only requirement. The image contains the libraries, nothing is added to your system, and no Go toolchain is required on the host. It works the same on Linux, macOS, and Windows.

Building and testing the library itself is three commands:

```bash
git clone https://github.com/cpmech/gosl.git
cd gosl
docker build -f docker/Dockerfile -t gosl:ci .    # Ubuntu 24.04, a pinned Go, the libraries
./gosl all                                        # build, install and test every package
```

The `./gosl` script wraps `docker run`, and the command table it implements is short enough to be worth copying into your own workflow:

| Command | What it does |
|---|---|
| `./gosl all` | build, install and test every package |
| `./gosl test`, `./gosl race` | the tests, with and without the race detector |
| `./gosl vet`, `./gosl fmt` | `go vet ./...`; `gofmt -s -l .` |
| `./gosl cover`, `./gosl bench` | coverage profile into `.docker-cache/`; benchmarks |
| `./gosl shell`, `./gosl go <args>` | a shell in the container; any other `go` command |

Three properties of that design matter in practice. It runs as your own user, so files it produces belong to you rather than to root. It caches into a gitignored `.docker-cache/`, and the README gives timings for the full run: 4.6 seconds once warm, 14.2 seconds from an empty cache. And build and test run with no network access, which is possible precisely because Gosl has no Go dependencies to fetch.

To see what a given image contains, `docker run --rm gosl:ci` prints the library versions the image ships.

## Running your own code inside that image

Any Ubuntu 24.04 image with the same libraries works, and the CI image is one. The invocation maps your host directory into the container and keeps the Go caches in `/tmp`:

```bash
docker run --rm -it --network=bridge --user "$(id -u):$(id -g)" \
  -e HOME=/tmp -e GOCACHE=/tmp/gocache -e GOMODCACHE=/tmp/gomod -e GOPATH=/tmp/gopath \
  -v "$PWD":/w -w /w gosl:ci bash
```

Inside, you start a module and pull Gosl in:

```bash
go mod init example.com/demo
go get github.com/cpmech/gosl
```

The example the README gives is a symmetric positive definite linear solve, small enough to read fully:

```go
package main

import (
	"fmt"

	"github.com/cpmech/gosl/la"
)

func main() {
	a := la.NewMatrix(2, 2)
	a.Set(0, 0, 2)
	a.Set(1, 1, 3)

	b := la.Vector{4, 9}
	x := la.Vector{0, 0}
	la.SolveRealLinSysSPD(x, a, b)

	fmt.Printf("x = [%.6g %.6g]\n", x[0], x[1])
}
```

Running it prints:

```bash
$ go run .
x = [2 3]
```

`la.NewMatrix` and the indexed `Set` method are the construction API, `la.Vector` is a plain slice, and the solver writes into an output vector you pass in.

Two caveats follow, and both are the kind that surface days later. The build and module caches live in `/tmp` inside the container and are thrown away with it; to keep them between runs, add `-v "$HOME/.cache/gosl-docker":/cache -e GOCACHE=/cache/build -e GOMODCACHE=/cache/mod`. And a binary built this way links against the container's shared libraries, so it runs in a container like this one or on a host where the same libraries are installed.

## The native path still exists, and it is what CI uses

For a Debian or Ubuntu host, the README documents the path the CI workflow follows, on Ubuntu with Go 1.20 or newer. Install Go as described at go.dev, then install the C and Fortran libraries Gosl links against:

```
sudo apt-get install \
  gcc \
  gfortran \
  libfftw3-dev \
  liblapacke-dev \
  libmetis-dev \
  libmumps-seq-dev \
  libopenblas-dev \
  libsuitesparse-dev
```

Then clone, build, and test:

```
git clone https://github.com/cpmech/gosl.git
cd gosl
./all.bash
```

`./all.bash` builds every package and runs its tests, printing `SUCCESS!` when it finishes. That script is in the repository root alongside the `gosl` wrapper, the package directories, and `zdocs/`.

Two details in the package list explain most of those dependencies. `libmetis-dev` and `libsuitesparse-dev` are there for the sparse solvers, which map to UMFPACK and MUMPS. `liblapacke-dev` is the C interface to LAPACK rather than Fortran LAPACK itself. `gfortran` is required because two of the libraries are Fortran.

So the choice is not container versus native so much as reproducible versus convenient. Docker guarantees a fixed set of library versions that you can print with a single command; a native install gives you whatever your distribution ships, which is easier on a workstation and harder to reproduce.

## Four essential packages and eight main ones

The package layout divides deliberately into a small core you will use everywhere and a set of subject areas.

The essential packages are `chk`, which provides checks on numerical results and helpers for unit testing, `io` for terminal output and file handling, `utl` for series generation such as linspace and other functions in the style of pylab, MATLAB, and Octave, and `la` for linear algebra: vectors, matrices, efficient sparse solvers, eigenvalues, and decompositions.

That `chk` package deserves attention from anyone writing numerical code. Checking that a solver returns the right answer to some tolerance is the difference between a simulation you can trust and one you are watching for plausibility. It is a testing concern that most numerical libraries leave to the user.

The main packages cover the domains: `fun` for special functions, DFT, FFT, Bessel functions, elliptic integrals, orthogonal polynomials, and interpolators; `gm` for geometry algorithms and structures; `hb` for a pseudo-hierarchical binary data file format; `num` for fundamental numerical methods such as root solvers, nonlinear solvers, numerical derivatives, and quadrature; `ode` for ordinary differential equations; `opt` for optimization covering interior point, conjugate gradients, Powell, and gradient descent; `pde` for partial differential equations by FDM, spectral, and FEM methods; and `rnd` for random numbers and probability distributions.

One removal is called out explicitly: the previous `mpi` sub-package has been removed for maintenance reasons, and the README recommends the external library gompi if you need MPI. That is a dependency you now take on separately rather than something Gosl gives you.

## Licence, and what a cgo dependency costs you

Gosl is released under the BSD-3-Clause licence, which is permissive and allows use in proprietary code subject to its conditions. The README notes one component is under a different licence, with the details continuing past what is shown, so if you are doing a full licence audit, read the LICENSE file rather than relying on the summary.

The more substantive cost of this library is not the licence but the dependency chain. Linking OpenBLAS, LAPACK, UMFPACK, MUMPS, QUADPACK, and FFTW3 means your build now requires a C compiler, a Fortran compiler, and development headers for six native libraries. Cross-compilation, which is otherwise routine for Go, becomes a problem the moment cgo enters the picture. Static linking into a single binary is no longer the default behaviour you get for free.

That is the trade in one line. You gain a native numerical stack you did not have to write, and you give up the deployment simplicity that makes Go attractive in the first place. If your service already runs in containers, the second half of that trade mostly disappears.

The package itself is a single module, `github.com/cpmech/gosl`, with no Go dependencies, which keeps the dependency graph small even though the native one is not.

## Conclusion

Adopt gosl if you need mature numerical solvers in Go today and can live inside a container or a Debian host with those native libraries installed. Do not start a new project on it: the README states the library is in maintenance mode, that new features are unlikely, and that development has moved to the successor project Russell. Verify first that the Docker image ships every library version your results depend on, since a binary built inside the container links against the container's shared libraries.

## FAQ

### How do I install gosl?

The recommended path is Docker: build the CI image with `docker build -f docker/Dockerfile -t gosl:ci .` and run `./gosl all` to build, install and test every package. On Debian or Ubuntu you can instead install gcc, gfortran, and the OpenBLAS, LAPACKE, METIS, MUMPS, and SuiteSparse development packages, then run `./all.bash`.

### Is gosl still being developed?

No. The README states it is mature and in maintenance mode, that new features are unlikely, and that development has moved to the successor project Russell. Reports about existing functionality are still welcome, and v1.2.13 was described as the first release since 2023.

### Does gosl support MPI?

Not any more. The previous `mpi` sub-package was removed for maintenance reasons, and the README recommends the external library gompi if you need MPI support.

## Sources

- [cpmech/gosl on GitHub](https://github.com/cpmech/gosl)
- [Issues](https://github.com/cpmech/gosl/issues)
- [License: BSD-3-Clause](https://github.com/cpmech/gosl/blob/main/LICENSE)
- [README](https://github.com/cpmech/gosl/blob/main/README.md)
- [Releases](https://github.com/cpmech/gosl/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/cpmech-gosl
