TesseroPHP, Serious math, without leaving PHP
Numerical computing for PHP 8.2+
N-dimensional arrays, linear algebra, FFT, NumPy-identical random numbers, sparse matrices, optimisation and mixed-integer programming, on one small SIMD C kernel, with results matched against NumPy and SciPy to full parity and re-checked by an independent BCMath oracle.
$ composer config repositories.tessero vcs https://github.com/DMJ-CV-91913/tessero
$ composer require tessero/tessero:dev-main
$ vendor/bin/tessero build
$ vendor/bin/tessero doctor
Tessero is not on Packagist yet and has no tagged release, so there are no prebuilt binaries. These commands install the main branch from GitHub; the build step needs a C compiler. Install guide
A quick taste
Operators, broadcasting and solvers, in PHP.
Work with arrays the way you would in NumPy: element-wise maths, boolean masks, reshaping and reductions, plus SciPy-style optimisation. One C kernel drives both backends, so the exact same numbers come back whether you run the FFI package or the native Zend extension.
use Tessero\Ext\NDArray; // native extension: operators & masks
use Tessero\Ext\Engine;
$load = NDArray::array($hourlyLoad); // PHP array -> float64
$z = ($load - $load->mean()) / $load->std(); // operators, broadcasting
$peak = $load[$z->gt(2.0)]; // boolean mask
$day = $load->reshape(-1, 24)->mean(0); // hour-of-day profile
$plan = Engine::linprog([-40, -30], [[2, 1], [1, 1]], [100, 80]);
$plan['x']; // [20, 60]
$plan['ineqlin']; // shadow prices [-10, -20], SciPy's convention
Tessero\NDArray
and Tessero\Optimize\LinearProgramming::linprog($c, $A_ub, $b_ub),
which returns the identical result array (['x' => ..., 'fun' => ..., 'ineqlin' => ...]).
1,554 / 1,560
kernel functions verified on both backends. FFI and the native Zend extension return identical numbers.
100.0%
of NumPy core, fully covered and verified symbol by symbol on both backends, with non-numeric and non-deterministic symbols transparently excluded.
100.0%
of SciPy, fully covered and verified symbol by symbol on both backends, with non-numeric and non-deterministic symbols transparently excluded.
Every figure here is generated from the library's coverage report, never hand-typed.
See the full NumPy & SciPy coverage report →
Checked three independent ways.
Every symbol is compared to NumPy and SciPy on both backends. Core scalar and combinatorial functions are re-derived from first principles by an independent BCMath oracle, a second correctness check that shares no code with the kernel. And real multi-step workflows are run end to end on both backends, so compositions are verified, not just functions in isolation. The workflow checks are representative, not exhaustive.
Coverage by module
| Module | In scope | FFI | Extension |
|---|---|---|---|
| NumPy core | 683 | 683 (100.0 %) | 683 (100.0 %) |
| numpy | 340 | 340 (100.0 %) | 340 (100.0 %) |
| numpy.ndarray | 56 | 56 (100.0 %) | 56 (100.0 %) |
| numpy.linalg | 31 | 31 (100.0 %) | 31 (100.0 %) |
| numpy.fft | 18 | 18 (100.0 %) | 18 (100.0 %) |
| numpy.polynomial | 6 | 6 (100.0 %) | 6 (100.0 %) |
| numpy.polynomial.polynomial | 28 | 28 (100.0 %) | 28 (100.0 %) |
| numpy.polynomial.chebyshev | 33 | 33 (100.0 %) | 33 (100.0 %) |
| numpy.polynomial.legendre | 31 | 31 (100.0 %) | 31 (100.0 %) |
| numpy.polynomial.laguerre | 31 | 31 (100.0 %) | 31 (100.0 %) |
| numpy.polynomial.hermite | 31 | 31 (100.0 %) | 31 (100.0 %) |
| numpy.polynomial.hermite_e | 31 | 31 (100.0 %) | 31 (100.0 %) |
| numpy.random | 4 | 4 (100.0 %) | 4 (100.0 %) |
| numpy.random.Generator | 43 | 43 (100.0 %) | 43 (100.0 %) |
| SciPy | 1497 | 1497 (100.0 %) | 1497 (100.0 %) |
| scipy.special | 336 | 336 (100.0 %) | 336 (100.0 %) |
| scipy.stats | 296 | 296 (100.0 %) | 296 (100.0 %) |
| scipy.linalg | 92 | 92 (100.0 %) | 92 (100.0 %) |
| scipy.fft | 35 | 35 (100.0 %) | 35 (100.0 %) |
| scipy.optimize | 65 | 65 (100.0 %) | 65 (100.0 %) |
| scipy.integrate | 28 | 28 (100.0 %) | 28 (100.0 %) |
| scipy.interpolate | 54 | 54 (100.0 %) | 54 (100.0 %) |
| scipy.signal | 156 | 156 (100.0 %) | 156 (100.0 %) |
| scipy.signal.windows | 26 | 26 (100.0 %) | 26 (100.0 %) |
| scipy.sparse | 38 | 38 (100.0 %) | 38 (100.0 %) |
| scipy.sparse.linalg | 33 | 33 (100.0 %) | 33 (100.0 %) |
| scipy.sparse.csgraph | 22 | 22 (100.0 %) | 22 (100.0 %) |
| scipy.spatial | 15 | 15 (100.0 %) | 15 (100.0 %) |
| scipy.spatial.distance | 27 | 27 (100.0 %) | 27 (100.0 %) |
| scipy.cluster.vq | 4 | 4 (100.0 %) | 4 (100.0 %) |
| scipy.cluster.hierarchy | 32 | 32 (100.0 %) | 32 (100.0 %) |
| scipy.ndimage | 75 | 75 (100.0 %) | 75 (100.0 %) |
| scipy.constants | 163 | 163 (100.0 %) | 163 (100.0 %) |
Generated from the library's coverage report, full tables & translation reference →
Parity, and past it
Beyond NumPy and SciPy
The coverage above is measured against NumPy and SciPy's own surface, so it stops at 100%. Tessero also ships numerics those libraries do not have.
Reproducible f32 functions
rsqrt,
exp2,
exp,
erf,
recip and
tanh
are built only from correctly-rounded f32 operations, so each returns one defined bit
pattern, identical across SIMD level, thread count and CPU architecture. On top of them sit
the composites
sigmoid,
gelu_tanh,
softmax,
rmsnorm and
layernorm
and their gradients.
np.exp
and
scipy.special.erf
give you a correct value; they do not promise the same bits on different hardware. These do,
and each is pinned to a golden SHA-256 so any build can prove it reproduces the exact bytes.
Primitive, composite and gradient goldens are all confirmed on two independent hosts.
One result, every machine
The CPU result is the canonical one, so the same model evaluated on a laptop and in a data centre agrees exactly, not just to a tolerance. Talario's GPU kernels mirror these definitions and match them bit for bit on NVIDIA A10, A100 and H100 PCIe, attested at 1024 x 1024; other GPUs are not yet attested. IEEE pins +, * and fused multiply-add, but not exp or erf, so building them from the pinned operations is what makes the whole result reproducible.
Measured, not asserted
BLAS-class speed, on both backends
Dense matrix multiplication routes through OpenBLAS, so heavy linear algebra runs at BLAS-class throughput. On a commodity Intel i7-4790, routing matmul through OpenBLAS is roughly 17x faster at four threads, and about 9x single-threaded. Both backends share one C kernel, so they measure the same. The benchmark is committed and reproducible, run back to back in one process with the hardware stated.
Works with your stack
What you can build
Tessero reproduces the numerical core that real scientific Python relies on, so the patterns behind scikit-learn, pandas, statsmodels, astropy, networkx, librosa and PyMC can run in native PHP: standardize and reduce feature matrices, fit and evaluate models, run FFTs and signal pipelines, solve linear and mixed-integer programs, and more. Usage-weighted parity across an 18-package corpus means the calls real code makes are the calls Tessero verifies.
API reference
Every class, function and signature
Browse the full, NumPy/SciPy-mapped API, generated from the library itself.
API tokens
Personal access tokens
Signed-in members can mint scoped tokens for scripts and CI from the members area.
Mission
Tessero's mission is to make PHP a first-class language for numerical and scientific computing. We provide numerically identical NumPy and SciPy arrays, statistics, linear algebra and optimization for PHP, with full verified parity against the reference implementations measured from recorded test runs. We build in the open, credit the work we stand on, and work with the wider PHP community so that the libraries, frameworks and applications PHP developers already use can do serious numerical work in native PHP, with no call-out to Python, running the same C kernel through either a compiler-free package or a native extension.