Dimensionality reduction for wlearn: PCA, t-SNE, UMAP and TriMap for Python, Node and browsers. A standalone C backend is the default; optional Polygrad execution supports CPU, CUDA, WebAssembly and WebGPU. Python and JavaScript share the C algorithms.
| Package | Install | Documentation |
|---|---|---|
npm @wlearn/dimred |
npm install @wlearn/dimred |
js/README.md |
PyPI wlearn-dimred |
pip install wlearn-dimred |
py/README.md |
Models saved in one language load in the other.
- Standalone C is now the default; Polygrad is optional. To keep the previous
execution path, select
backend: 'polygrad'(Python:backend='polygrad'). - C PCA uses a direct double-precision eigensolver; nonlinear methods no longer depend on an iterative Polygrad PCA initialization converging.
- C t-SNE defaults to Barnes-Hut; C UMAP uses sequential scheduled updates and learning rate 1. Layouts can differ from 0.1.0 and the Polygrad backend.
- Native and Wasm C builds share portable math routines, with long-fit byte comparisons, numerical reference tests and layout-quality checks.
- Fitted-model files remain cross-language. Optional Polygrad PCA also gains convergence fixes for close-variance inputs.
Choose backend: 'c' | 'polygrad'. The default c backend runs the standalone
code in src/base/: a native library in Python, packaged WebAssembly in Node
and browsers. It neither imports nor requires Polygrad. All JS fits stay async.
The optional src/polygrad/ implementations build tensor graphs through the
public C API. They use the caller's runtime, not a second embedded Polygrad.
Passing polygrad options or a runtime selects this backend unless backend
is explicitly set. Install Polygrad separately for acceleration. Backend choice
can change numerical algorithms; it is not just a device switch.
The standalone backend uses double-precision centered/scaled covariance or
the smaller Gram matrix, Householder tridiagonalization and implicit QL.
It accepts up to min(features, samples - 1) components. It has no graph
compilation or iterative-subspace cutoff. It is covariance-based, not direct
SVD; ill-conditioned small components remain subject to conditioning limits.
Inputs and portable fitted arrays remain float32. maxIter and tolerance
configure the Polygrad PCA solver, not the standalone direct solver.
Polygrad PCA supports up to 16 components. Up to 64 features it diagonalizes the full covariance with Jacobi rotations. Larger inputs start with reorthogonalized subspace iteration; up to 1,024 features, strict PCA reserves part of the same iteration budget for a full-covariance fallback if needed. Wider inputs keep the bounded-width iterative solver. All tensor computation stays in C-authored Polygrad graphs. This is float32 covariance-based PCA, not a full-precision SVD. Component signs and repeated-eigenvalue bases are not unique. PCA checks the residual against the original centered data and raises on nonconvergence. Nonlinear methods may use a finite approximate projection without requiring the full fallback. The optional backend requires Polygrad 0.7.1 or newer within 0.7.x. Its X86 launcher supports the wider PCA kernels that failed with Polygrad 0.7.0.
barnes_hut: default on C, sparse affinities and quadtree repulsion with opening angle 0.5. Not supported by the Polygrad backend.exact: default on Polygrad; dense affinities, 4 to 2,000 points.sparse: affinities from the k nearest neighbours (k defaults to three times the perplexity), no fixed 2,000-point limit. Repulsion evaluates every pair in blocks ofblockSizerows, so time stays quadratic; memory for repulsion is N x blockSize instead of N x N on Polygrad; the C path accumulates pairs directly.sparsenever silently selects Barnes-Hut. No FFT implementation.- Defaults: 500 iterations, early exaggeration 12 until iteration 250, momentum 0.5 then 0.8, learning rate 50.
The calibration and pair forces follow umap-learn 0.5.7. The optimizers differ:
nNeighborsexcludes the point itself (umap-learn'sn_neighborsis one more). Euclidean metric, local connectivity 1, fuzzy union.- C uses sequential scheduled fuzzy edges and negative sampling, learning rate 1 by default. It follows the reference-style schedule, not identical reference coordinates: neighbor search, PCA initialization and RNG differ.
- Polygrad updates all weighted edges from the same layout snapshot, with five sampled negatives per edge, instead of umap-learn's sequential per-edge SGD. Learning rates are therefore not interchangeable.
aandbcorrespond tomin_dist = 0.1,spread = 1. Initialization uses PCA coordinates instead of a spectral embedding.- C sampling uses xorshift32; Polygrad uses integer hashing of seed and step. Native and Wasm C builds share math for reproducible seeded fits. Polygrad results are not promised bit-identical across devices or with C.
Not implemented: transforming new points, densMAP, supervised UMAP, other metrics.
The objective, momentum and delta-bar-delta gains follow the original implementation. Both backends use the same full-batch objective and PCA scaling. C uses direct gradient accumulation; Polygrad uses autograd. Neighbors and random sampling differ from Annoy/NumPy and between backends. Triplets and final coordinates differ from the reference implementation.
C uses a balanced KD-tree for exact search. Above 2,000 points and 16 features,
it uses bounded tree queries followed by neighbor-of-neighbor refinement;
this is approximate and can change downstream layouts. Polygrad retains exact
blocked search. k is at most 64 in the standalone neighbor API; the low-level
Polygrad sparse t-SNE function also accepts precomputed neighbors with larger k.
C sparse/Barnes-Hut t-SNE uses at most 64 neighbours and requires perplexity
no greater than min(64, samples - 1).
The base backend shares a pinned MIT-licensed musl subset for exp/log/log2/pow
between native and Wasm. Builds disable fast-math and floating-point contraction;
callers must retain round-to-nearest and gradual underflow. Long seeded fits
and math vectors are compared byte-for-byte (excluding NaN payloads). This does
not promise equality with the Polygrad backend or other library versions.
Upstream hashes and adaptations are recorded in src/base/math/origin.json;
redistribution notices are in NOTICE.
make base builds only src/base/, without Polygrad headers or libraries.
make test-base PYTHON=/path/to/python runs sanitizer and independent numerical
checks. Building optional graph authors additionally requires a Polygrad
source tree (which need not be built), Clang and Python 3.10+ to
generate bindings, a C compiler and Node headers for native modules, and
Emscripten for WebAssembly. Set POLYGRAD_PATH if Polygrad is not at
../../polygrad/polygrad.
POLYGRAD_PATH selects build headers and the binding generator, not execution.
Tests and demos resolve the installed Python/npm Polygrad packages by default.
To test a locally built checkout explicitly, set POLYGRAD_RUNTIME=/path/to/polygrad.
Python then uses that checkout's py/ and build/libpolygrad.so; JavaScript
uses its js/ package. Browser tools resolve from this project or WLEARN_PATH.
Workspace npm links are respected; unlink them or use isolated package checks
when verifying registry installations.
make all POLYGRAD_PATH=/path/to/polygrad
make package # stage the npm package and Python sdist
make test-api # estimator API and Python <-> JS model files
make test-python PYTHON=/path/to/python
make test-node PYTHON=/path/to/python
make test-browser PYTHON=/path/to/pythonTests compare against reference implementations, so the Python environment needs NumPy, SciPy, scikit-learn and umap-learn 0.5.7. These are test dependencies only.
make demo-c-api serves an interactive demo on http://127.0.0.1:8081 (set
PORT to change it). See examples/c_api/README.md.
Benchmarks against npm packages are described in bench/README.md.
Source layout:
src/dimred.h standalone public API and shared types
src/base/ plain C algorithms (backend: 'c')
src/polygrad/ optional tensor authors (backend: 'polygrad')
js/, py/ lifecycle, memory marshalling and artifact bindings
Only src/polygrad/ includes Polygrad headers. It may use base algorithms;
the base code must never depend on Polygrad. Graph entry points use
dr_polygrad_*_build; standalone entries use dr_pca_fit, dr_umap_fit, etc.
Polygrad's former X86 kernel-argument limit is fixed in 0.7.1, including the 31-argument neighbour-search kernel used by TriMap.
Apache-2.0. See NOTICE.