Skip to content

poly

Polynomials in pure Go: evaluation, arithmetic, least-squares fitting, roots and orthogonal bases, with the semantics of NumPy. Released v0.1.0.

Status: released v0.1.0.

Polynomials in pure Go (CGO_ENABLED=0): evaluation, arithmetic, least-squares fitting, roots, and the Chebyshev, Legendre, Hermite, HermiteE and Laguerre bases, with the semantics of numpy.polyfit, numpy.roots, the numpy.poly1d helpers and the numpy.polynomial package.

go get github.com/go-approx/poly@v0.1.0
import "github.com/go-approx/poly"

fit, _ := poly.Polyfit(x, y, 3, nil)        // highest degree first, like np.polyfit
r, _ := poly.Roots(fit.Coef)                // companion-matrix eigenvalues
v := poly.Polyval(fit.Coef, 0.5)            // Horner; PolyvalComp is compensated

s, _, _ := poly.FitSeries(poly.Chebyshev, x, y, 8, nil) // lowest degree first, like numpy.polynomial
y0, _ := s.Val(0.3)

Coefficient order differs between the two families, as in NumPy: the poly1d family is highest degree first, the numpy.polynomial family lowest degree first.

What is provided

hereNumPy
Polyval, PolyvalAllnp.polyval
PolyvalComp, PolyvalCompAll(none) compensated Horner, Graillat-Langlois-Louvet 2009
Polyder, Polyintnp.polyder, np.polyint
Conv, Deconv, Polymul, Polyadd, Polysubnp.convolve, np.polydiv, np.polymul, np.polyadd, np.polysub
PolyFromRoots, PolyFromComplexRootsnp.poly
Polyfit (weights, rcond, covariance scaled or unscaled, rank)np.polyfit
Rootsnp.roots
Basis (Chebyshev, Legendre, Hermite, HermiteE, Laguerre) with Val, Vander, Der, Int, RootsBasis, FitBasischebval, chebvander, chebder, chebint, chebroots, chebfit and the leg, herm, herme, lag equivalents
Series (NewSeries, FitSeries, Val, Der, Int, Roots), MapParms, MapDomain, GetDomainnumpy.polynomial.Chebyshev etc. with domain/window mapping

Verification

The reference is NumPy 2.5.4 (SciPy 1.18.1 installed alongside), run as an oracle from Go. A tagged test (-tags oracle) generates the fixtures in testdata/ (deterministic inputs, large cases regenerated from their seed); the ordinary tests read them and need no Python. Octave is not installed on the host, so no Octave oracle was run.

Measured against NumPy, as the README reports:

quantityresult
Polyval, Polyder, Polyint, Polyadd, Polysub, Deconv, PolyFromRoots3434 of 3434 values bit-identical
Conv, Polymul229 of 917 bit-identical; max error 0.90 eps relative to the sum of abs(a_i b_j) (NumPy’s convolve uses a different summation order)
PolyvalCompworst error / theoretical bound = 0.00095; more accurate than Horner at 677 of 780 points
each of the 5 bases: Val, Vander, Der, Int, Series.Val/Der/Int10016 of 10016 values bit-identical
Roots8 of 24 cases identical in value and order; well-conditioned random degree <= 50: max positional error 4e-15
Polyfit78 fits; coefficient deviation at most 1.67 conditioning units

The bit-identical rows hold on every architecture because every product that feeds a sum is rounded explicitly, which forbids fused multiply-add. The least-squares fits are not bit-identical across architectures: their QR runs on go-simd/floats, whose Dot adds in SIMD lanes in an order that depends on the vector width. The fit tolerances hold on every architecture CI runs.

Gates: gofmt -l . empty, go vet, go test -race, 100.0% statement coverage with -coverpkg=./..., and a CI that runs Linux, macOS, Windows, arm64, qemu riscv64/loong64/ppc64le/s390x/arm, 386, compiles every GOOS/GOARCH and runs govulncheck.

Performance

docs/perf.md has the comparison of Go against NumPy on one core, interleaved rounds. It is only meaningful on an idle machine; the table states the load it was taken at. The Householder QR of the fits runs its inner products and updates on go-simd/floats (Dot, Axpy: assembly generated by go-asmgen), and the Vandermonde and series design matrices are built in cache-sized row blocks.

Gaps

float64 only; one-dimensional coefficient and data arrays; a single integer degree (no list of degrees as np.polyfit allows in 2.x). No complex-coefficient Polyval or fits. No numpy.polynomial.Polynomial (power-basis series) class beyond the poly1d functions. Conv is not bit-identical to np.convolve. No Octave oracle.

Source: go-approx/poly, BSD-3-Clause.

Created on by tannevaled

Modified on by tannevaled