npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pmf64

v0.2.0

Published

Finite binary64 probability measures with explicit independence, accountable compression, and density-aware elicitation.

Readme

pmf64

Finite probability measures for JavaScript, with adaptive exact kernels, explicit independence, accountable bounded algebra, and editable belief curves kept separate from atom semantics.

import {
  Pmf,
  independentSum,
  compressMeanPreserving,
} from "pmf64";

const estimate = Pmf.fromWeights([8, 10, 14], [1, 3, 1]);
const noise = Pmf.fromWeights([-1, 0, 1], [1, 2, 1]);

const observed = independentSum(estimate, noise);
const compact = compressMeanPreserving(observed, 5);

compact.distribution.mean();       // same mean, to binary64 rounding
compact.report.wasserstein1;       // measured distributional distortion
compact.report.varianceDelta;      // measured variance change

Status: 0.2.0, second public release.

Six contracts, no blur

Atom measures, exhaustive algebra, numerical execution, deliberate approximation, propagated approximation evidence, and editable elicitation curves are different things. pmf64 keeps each boundary explicit rather than making one object pretend to satisfy all six contracts.

Pmf: exact finite atom semantics

A Pmf denotes a discrete probability measure:

$$ P(X = x_i) = p_i. $$

Support is finite, sorted, unique binary64. Canonical mass is normalized in log space. Duplicate support points merge; caller-owned arrays cannot mutate the measure.

const prior = Pmf.fromLogMasses(
  [0, 1, 2],
  [0, -1000 * Math.LN10, -4],
);

prior.logMassAt(1);              // finite: the atom still exists
prior.linearMassAt(1);           // 0: linear binary64 underflow
prior.linearMasses().underflowedAtoms;

The log representation preserves relative mass far below the linear binary64 floor. Linear views report how many represented atoms underflowed.

Approximation: a value plus an error account

Exact pairwise operations can grow to $nm$ atoms. Compression is never disguised as an ordinary Pmf method:

const approximation = compressMeanPreserving(observed, 32);

approximation.distribution;
approximation.report.massResidual;
approximation.report.meanResidual;
approximation.report.varianceDelta;
approximation.report.kolmogorovDistance;
approximation.report.wasserstein1;
approximation.report.transportCost;

Each removed interior atom is split barycentrically between its current neighbors. This preserves total mass and the first moment in the atom model. Candidate removals are prioritized by their expected transport distance—not by how visually flat a mass plot looks.

The report measures what changed. wasserstein1 and Kolmogorov distance are computed from representable linear-mass views; sourceMetricUnderflowAtoms and resultMetricUnderflowAtoms state that coverage boundary separately. transportCost is the binary64 evaluation of the explicit sequential coupling. That coupling bounds $W_1$ mathematically, but the two reported floating-point evaluations can cross by rounding; the field does not pretend to be a directed-rounding bound. varianceDelta is null when either second moment is outside finite binary64; compression still returns, while the unavailable metric remains explicit.

Reduction is a policy, not a universal claim:

import { approximate } from "pmf64";

approximate(observed, 32, { kind: "w1-neighbor-transport" });
approximate(observed, 32, { kind: "range-median-mean" });
approximate(observed, 32, { kind: "centroidal-w2" });
approximate(observed, 32, { kind: "moment-quadrature" });

The anchor policy retains the range endpoints and source left median, enforces $F(m^-) < 0.5 \le F(m)$, and preserves the first moment by neighbor transport. Centroidal reduction greedily minimizes adjacent Ward costs. Moment quadrature preserves mass, mean, and variance when a representable nonnegative elimination exists; infeasible contracts throw ApproximationInfeasibleError.

Bounded algebra: propagated estimates, not hidden guarantees

Bounded operations return a distribution together with frozen evidence:

import { boundedIndependentSum, exactBounded } from "pmf64";

const bounded = boundedIndependentSum(exactBounded(left), right, 64);
bounded.distribution;
bounded.evidence.wasserstein1Estimate;
bounded.evidence.estimateEvaluation; // "binary64-not-directed"
bounded.evidence.steps;

The evidence records every reduction in expression-tree order. Its estimates use ordinary binary64 arithmetic; they are not outward-rounded certified upper bounds.

PmfDraft: relative-density elicitation

An editable curve expresses relative density intent with respect to an axis coordinate. It is mutable, versioned, undoable, and deliberately not a Pmf.

import { PmfDraft, densityView } from "pmf64";

const draft = PmfDraft.uniform({
  domain: [1, 1_000],
  atoms: 257,
  scale: "log",
});

draft.beginEdit();                     // pointer down
draft.drawBetween(40, 0.1, 70, 0.5);  // pointer moves
draft.drawBetween(70, 0.5, 90, 0.8);
draft.endEdit();                       // one undo entry
draft.smooth({ radius: 3, passes: 2 });

const belief = draft.commit();
const display = densityView(belief, "log");

beginEdit()/endEdit() group an asynchronous pointer gesture into one undo step; cancelEdit() rolls it back. edit(callback) provides the same transaction with automatic rollback when a synchronous edit throws.

commit() applies trapezoidal quadrature in the chosen axis coordinate and produces an immutable atom measure. Constant density on a log axis therefore means uniform mass per unit of log-coordinate—not uniform mass per unit of raw $x$.

densityView divides atom mass by local axis-coordinate width. Plotting raw mass as curve height on irregular support is wrong: tightly spaced atoms can each carry less mass while representing greater local density. logDensity is authoritative when linear density is outside binary64 range; linearUnderflowAtoms and linearOverflowAtoms report that projection boundary.

Exact composition

Deterministic dependence

map is a pushforward of one random variable. flatMap composes a value-dependent stochastic kernel.

const shifted = prior.map((value) => value + 10);
const measured = prior.flatMap((value) =>
  Pmf.fromWeights([value - 1, value + 1], [1, 1]),
);

flatMap is how dependence enters this finite model. It obeys the finite-distribution monad laws under the package's binary64 support equality.

Independent random variables

Independence is stated in the function name rather than hidden behind arithmetic-looking methods:

import {
  independentDifference,
  independentProduct,
  independentQuotient,
  independentSum,
} from "pmf64";

const sum = independentSum(left, right);
const difference = independentDifference(left, right);
const product = independentProduct(left, right);

independentDifference(x, x) does not cancel: it draws two independent copies and has variance $2\operatorname{Var}(X)$. Likewise, expanding an expression can change dependence; distributivity is not a law of independently re-drawn references.

Support-unit brands are optional and compile-time only. Additive operations require matching brands:

interface Dollars { readonly dollars: unique symbol }
const revenue = Pmf.fromWeights<Dollars>([10, 20], [1, 3]);
const cost = Pmf.fromWeights<Dollars>([4, 8], [1, 1]);
const gross: Pmf<Dollars> = independentDifference(revenue, cost);

For custom operations, use independentLift2.

const maximum = independentLift2(left, right, Math.max);

Adaptive operation kernel

PmfKernel owns reusable scratch space and chooses a numerically admissible convolution path:

import { PmfKernel } from "pmf64";

const kernel = new PmfKernel({
  maxResultAtoms: 100_000,
  maxPairEvaluations: 10_000_000,
  maxWorkspaceBytes: 64 * 1024 * 1024,
});
const plan = kernel.planSum(left, right);     // immutable conservative envelope
const exact = kernel.sum(left, right);        // limits checked before execution
const repeated = kernel.sumIid(die, 1_000);   // exponentiation by squaring
const bounded = kernel.productBounded(left, right, 256);

Integer/dyadic lattices bypass pairwise support verification only when their coordinates prove the binary64 result cells exact. Otherwise the planner verifies the proposed lattice or uses sparse composition. Representable, well-conditioned lattice masses use compensated direct convolution or FFT; extreme tails automatically stay in the log-domain oracle.

Forced FFT is explicit and can reject an uncertifiable dynamic range. Automatic selection never accepts an FFT result outside its reported coefficient, normalization, imaginary-residual, and discarded-tail envelopes.

Large irregular sums and products use sorted row streams rather than an $n\times m$ Map. Bounded products interleave that stream with reduction, keep working support bounded, and accumulate each reduction boundary in the error evidence. The expression-tree order remains visible because bounded algebra is not associative.

Plans expose result-atom, pair-evaluation, and workspace-byte upper bounds. A configured limit rejects before the governed allocation with PmfResourceLimitError; stable operational failures expose a code recognized by isPmfOperationalError.

Optional WebAssembly direct kernel

import { PmfKernel } from "pmf64";
import { loadWasmKernel, packagedWasmUrl } from "pmf64/wasm";

const wasm = await loadWasmKernel(packagedWasmUrl);
const kernel = new PmfKernel({ convolution: "wasm", wasmKernel: wasm });
const sum = kernel.sum(left, right);
wasm.dispose();

The retained Rust/WASM artifact is dependency-free, memory-bounded, and never loaded by the default kernel. Loading or numerical failure is surfaced; there is no silent JavaScript substitute. FFT remains faster for large, well-conditioned lattices; WASM supplies a faster direct path where its copying cost is amortized.

Non-finite outcomes are never silently discarded

Finite support can still produce non-finite arithmetic, notably division by zero or overflow. The default is rejection with NonFiniteOutcomeError.

If conditioning on finiteness is the intended model, request it and keep the leak visible:

const result = independentQuotient(numerator, denominator, {
  onNonFinite: "condition",
});

result.distribution;
result.conditionedAwayMass;
result.logConditionedAwayMass;
result.retainedMass;

A result with no finite outcomes is always an error.

Queries and updates

prior.mean();
prior.variance();
prior.standardDeviation();
prior.skewness();              // null for a point mass
prior.excessKurtosis();        // null for a point mass
prior.entropyNats();
prior.entropyBits();
prior.modes();                 // every tied global mode
prior.cdf(1);
prior.logProbabilityGreaterThan(1); // stable suffix query for tiny upper tails
prior.probabilityAtLeast(1);
prior.probabilityBetween({
  lower: { value: 0, inclusive: false },
  upper: { value: 2, inclusive: true },
});
prior.quantile(0.9);           // always an atom
prior.sample(randomSource);

Derived statistics, linear/log CDFs, lattice metadata, modes, and alias tables are cached lazily behind the immutable value. sample() and sampleN() retain quantile-inversion behavior; sampleFast() and sampleFastN() explicitly use the cached alias table and reject when linear projection would discard log-domain atoms.

Strict and inclusive event methods use cached prefix or suffix log accumulations directly. Upper tails never come from 1 - cdf(x), so representable log-domain tails are not lost to linear subtraction.

Expectations and moments do not pass through an underflow-prone linear-mass projection. Weighted binary terms are accumulated exactly and rounded once, ties to even, to the final binary64 grid. A finite moment outside binary64 range raises RangeError rather than returning a success-shaped infinity.

quantile(q) is the left inverse of the atom CDF: the least support value whose cumulative mass is at least $q$. It does not interpolate between atoms; interpolation belongs to a continuous or curve model.

entropyNats() and entropyBits() are Shannon entropy of the atom measure. densityView(...).differentialEntropyNats is the separate quadrature estimate $-\sum_i p_i\log(p_i/\Delta u_i)$ with respect to the selected axis coordinate $u$.

Conditioning and likelihood-style reweighting return their normalizers:

const conditioned = prior.condition((value) => value > 0);
conditioned.distribution;
conditioned.logRetainedMass;

const posterior = prior.reweightLog((value) => logLikelihood(value));
posterior.distribution;
posterior.logNormalizer;

Comparison

import { compareIndependent } from "pmf64";

const comparison = compareIndependent(left, right);
comparison.lessThan;
comparison.equalTo;
comparison.greaterThan;

The three branches partition independent pair space and sum to one. Atom equality is exact binary64 support equality; it is not an epsilon-neighborhood comparison.

Numerical and ownership boundaries

  • Support values must be finite. -0 canonicalizes to 0.
  • Linear masses must be finite and non-negative. Log masses may be finite or -Infinity.
  • Public arrays are copies. Immutable derived caches never cross the ownership boundary.
  • Exact operations retain a log-domain oracle; linear, FFT, and WASM paths run only under explicit numerical admissibility checks.
  • Safe dyadic lattice coordinates prove their cells directly; other candidate lattices are checked against pairwise binary64 outcomes.
  • Approximation evidence is evaluated in ordinary binary64, not directed rounding, and never claims bounded associativity.
  • Sampling resolution is limited by the caller's uniform random source.
  • Correlated joint distributions and continuous probability laws remain outside this finite-measure kernel.

See DESIGN.md for the laws and implementation correspondence, and docs/canonicality.md for the release denominator and named gaps.

Development

npm test
npm run bench
npm run smoke
npm publish --dry-run --access public

The packed-artifact gates install the tarball into empty consumers, check ESM and strict TypeScript resolution, and exercise a complete risk workflow. CI covers Node 22, 24, and 26.

License

MIT © Xyra Sinclair