Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

Unreleased

1.2.103 - 2026-05-21

Compatibility-pack release. Adds the handful of APIs that downstream packages (viva_glyph, viva_tensor) still pull from gleam_community_maths, so they can complete the migration started in viva_math 1.2.101.

A pre-tag Codex GPT-5.5 review flagged a real bug (lp_norm(v, 0.0) crashed on Erlang with badarith while returning Infinity on JavaScript) and the absence of direct local tests for the new APIs. Both were addressed before publishing.

Added — viva_math/vecn

Added — viva_math/statistics

Added — viva_math/precision

Added — test/compat_pack_test.gleam

31 direct unit tests covering all 10 new functions:

Fixed (pre-tag review)

Validated

1.2.102 - 2026-05-21

Roadmap entries closed: Ornstein-Uhlenbeck mood dynamics, deeper Bayesian variational free energy, Wasserstein distance between affective distributions, property-based tests on every closed form.

Added — viva_math/ou (new module)

References: Uhlenbeck & Ornstein (1930); Oravecz, Tuerlinckx & Vandekerckhove (2009) Ornstein-Uhlenbeck Process in Affective Dynamics; Doob (1942).

attractor.ou_mean_reversion remains unchanged (Euler-step deterministic version) — no API break downstream.

Added — viva_math/transport (new module)

References: Villani (2008) Optimal Transport; Peyré & Cuturi (2019) Computational Optimal Transport.

Added — viva_math/free_energy (Bayesian deepening)

References: Beal (2003); Bishop (2006) ch. 10; Friston (2010).

Added — Property tests

Fixed (post-review)

Rigorous mathematical audit (Codex GPT-5.3 + manual cross-check) identified one bug and several documentation gaps, all addressed before release:

Performance (post-SOTA cross-check)

Performance audit (Codex GPT-5.3 + exa-driven literature review covering thermox 2024, OT1D, BONG 2024, Sliced Wasserstein 2024-2025 papers). Literature confirmed our algorithmic choices (Doob exact kernel for OU, closed-form conjugate VI, O(n log n) sort-based 1D Wasserstein) are SOTA; opportunities were in the implementation, not the math.

Fixed (deep audit — Codex GPT-5.5 high reasoning)

Third audit pass surfaced 3 bugs + 2 inconsistencies that the formal-math and performance reviews had missed:

Added — Deep-audit regression tests

test/viva_math_test.gleam gained 7 regression tests covering each of the bugs above plus two algebraic-property checks proposed by the audit:

Project organisation (Gleam best-practice alignment)

Codex GPT-5.5 audit cross-checked against gleam-lang/stdlib, lustre-labs/lustre, gleam-wisp/wisp, lpil/gleeunit, mooreryan/gleam_qcheck repos.

Test coverage — 5 previously untested modules

Codex GPT-5.5 coverage audit identified 5 public modules with zero direct tests: autodiff, autodiff_reverse, matrix_dense, tdigest, special. Closed-form derivative identities (Lyness, dual numbers), algebraic invariants (identity laws, transpose involution), and tabulated reference values (Γ(n)=(n-1)!, B(2,3)=1/12, ψ(1)=−γ) used as anchors per the AD validation literature (Birthe van den Berg et al. 2024; Mazza & Pagani 2022; Dunning & Ertl 2019).

Added — Documentation pass

Closed the docstring gap identified by the audit: every pub fn / pub type in viva_math/autodiff.gleam (22 items) and viva_math/autodiff_reverse.gleam (14 items) now carries a /// comment stating the local derivative rule it implements (e.g., mul: ∂z/∂a = b, ∂z/∂b = a). viva_math/matrix_dense.DenseMat documents its shape invariant. The audit confirmed tdigest and special already had complete public docstrings.

Encapsulation — opaque types

Five types whose direct construction could produce invariant-violating states are now pub opaque type. Surface accessors added where consumers needed read access. Cross-checked against qcheck (pub opaque Seed) and gleam-lang/stdlib (most concrete types are constructed via smart constructors).

External call sites refactored:

Tape, Node, Op, Centroid had zero external call sites — purely internal refactor.

Deprecation — idiomatic Result-returning function names

viva_math/scalar adds wrappers aligned with gleam/float’s convention (name the math operation, let the Result type communicate failure; reserve try for the result.try combinator):

New (idiomatic)Deprecated alias
logarithm/1try_ln/1
logarithm_2/1try_log2/1
logarithm_10/1try_log10/1
square_root/1try_sqrt/1
cube_root/1try_cbrt/1
nth_root/2try_nth_root/2

The legacy try_* functions stay around as @deprecated wrappers — they call the new names so semantics are identical. Internal callers (free_energy, entropy, ou, free_energy_variational_test) all migrated to the idiomatic names, leaving the build warning-free.

Renamed

Numerical precision audit (post-encapsulation)

Codex GPT-5.5 god-audit inventoried the full public surface — 632 items across 29 modules, ~40% direct test coverage — and flagged loose tolerances on closed-form identities. The audit was anchored against CPython’s test_math.py ulp-based testing and N1630 (WG14) edge-case recommendations.

Tolerance tightening — 8 closed-form identities pulled to IEEE-754 precision (1e-3 / 1e-61e-12 / 1e-15):

TestIdentityBeforeAfter
common.sigmoid(0, 1)= 0.5 exact1e-31e-15
softmax_sum_to_oneΣ softmax = 11e-31e-12
vec3_length(3,4,0)Pythagorean triple = 51e-31e-15
scalar.erf(1)tabulated, libm :math.erf1e-61e-12
scalar.softplus(0)= ln(2)1e-61e-15
scalar.logsumexp([0,0])= ln(2)1e-61e-15
constants.pidouble-precision literal1e-61e-15
autodiff.gelu' at 0= 0.5 closed form1e-31e-12

test/test_support.gleam extended with hybrid tolerance helpers:

Added — algebraic identity tests (test/identities_test.gleam)

14 universal-law tests that golden-value testing misses:

Added — edge-case tests (test/edge_cases_test.gleam)

18 boundary tests anchored on N1630 + CPython conventions:

Coverage — Pri 1/2/3 batch (Codex-implemented)

Delegated all three priorities from the previous god-audit to Codex GPT-5.5 via wcgw with a surgical brief covering 6 new test files. Codex implemented every module’s missing surface anchored on closed-form identities, energy-conservation invariants, and tabulated golden values.

6 new test files, 42 new tests:

Documented limitations (AUDIT NEEDED markers)

Codex surfaced two real measurement gaps from the golden-value pass:

  1. scalar.gelu(1.0): the implementation uses the exact erf-based GELU (x·Φ(x)), not the tanh approximation. Golden value adjusted to 0.841_344_746_068_542_9 (exact) with a comment explaining the distinction. No source fix needed — the test was wrong, not the implementation.
  2. special.digamma(5.0): the asymptotic-series implementation (truncated at x⁻¹⁰) bottoms out at ~1.2e-10 absolute error against the tabulated value 1.506_117_668_431_800_5. Test tolerance set to 2e-10 with an AUDIT NEEDED comment in-place; source-side TODO: increase the recurrence threshold from x ≥ 6 to x ≥ 12, or extend the series with two more Bernoulli terms.

Fixed — special.digamma accuracy

The asymptotic series implementation pushes x past a threshold via the recurrence ψ(x) = ψ(x+1) − 1/x before evaluating the series truncated at x⁻¹⁰. The threshold was raised from N = 6 to N = 12, delivering ~1000× error reduction at no measurable runtime cost.

Measured against the tabulated reference ψ(5) = 1.506_117_668_431_800_5:

Thresholdψ(5) returnedAbsolute error
N = 6 (before)1.506_117_668_548_33061.17e-10
N = 12 (after)1.506_117_668_431_92061.20e-13

test/golden_values_test.gleam:42 tightened from 2e-101e-12 and the AUDIT NEEDED comment removed.

Documentation — Tier 0/1/2 (HexDocs-first)

Adopted the idiomatic Gleam approach (cf. lustre, wisp, gleam_stdlib) — no separate docs site framework. The gleam docs build pipeline now publishes API reference + 4 conceptual guides + the changelog directly to hexdocs.pm/viva_math.

gleam.toml gained five [[documentation.pages]] entries that wire these into the HexDocs sidebar:

[[documentation.pages]]
title = "Changelog"
path = "changelog.html"
source = "CHANGELOG.md"

[[documentation.pages]]
title = "PAD Emotional Model"
path = "pad-model.html"
source = "docs/pad-model.md"
# ... (4 more)

Updated — README

Validated

Added — Multivariate Wasserstein (transport.wasserstein_2_multivariate)

True multivariate W₂ via Sinkhorn-Knopp entropic regularization (Cuturi 2013) — solves the OT problem

min_π ⟨π, C⟩ + ε·H(π)   s.t.  π·1 = a, πᵀ·1 = b

with C[i,j] = ‖x_i − y_j‖² (Euclidean squared on Vec3) and uniform marginals. Returns √(⟨π, C⟩) (true distance, dropping the entropic term). Pure Gleam — no GPU, no dependencies. Defensive float.max(ε, 1e-12) floor + safe_ratio against division by zero.

5 new tests in test/transport_test.gleam: empty rejection, identity (W₂(P, P) ≈ 0), single-point translation W₂([0], [(1,0,0)]) = 1, symmetry across iterates, and comparison vs wasserstein_pad on product distributions (where the two should coincide).

References: Cuturi (2013) Sinkhorn Distances: Lightspeed Computation of Optimal Transport; Peyré & Cuturi (2019) Computational Optimal Transport.

Added — ULP-by-ULP validation infrastructure

test/test_support.gleam gained two helpers backed by dual FFI (Erlang + JavaScript) implementing CPython’s ordered-float-bits convention (negative zero ↔ -1; signs flip into a monotonic integer space):

pub fn ulp_distance(a: Float, b: Float) -> Int
pub fn is_close_ulp(a: Float, b: Float, max_ulps: Int) -> Bool

Erlang FFI (test/test_support_ffi.erl) extracts IEEE-754 bits via <<Bits:64/unsigned-integer>> = <<X:64/float>>. JavaScript FFI (test/test_support_ffi.mjs) uses DataView.setFloat64 + BigUint64.

test/golden_mpmath_test.gleam ships 21 reference values computed at 100-bit precision in mpmath and asserts <= 5 ULP agreement by default. Three real outliers, measured and honestly documented:

FunctionULP distanceNote
special.gamma(5.5)8Lanczos series, near-integer arg
special.digamma(1.0)~1100asymptotic series truncation
special.digamma(10.0)~300idem

Everything else (erf, exp, ln, sin, cos, gamma (most args), lgamma) lands within 5 ULP of the 100-bit reference.

Added — JavaScript target (partial)

src/viva_math_random_ffi.mjs implements viva_math/random for the JavaScript target using Mulberry32 (PRNG, seedable, statistically adequate) + Box-Muller for normal_standard / normal_with. jump/1 is a documented no-op on JS (Mulberry32 has no jump-ahead).

gleam.toml no longer pins target = "erlang" — both targets are attempted. Current state:

TargetStatus
gleam test --target erlang521 passed, no failures
gleam test --target javascript❌ compile fails on Erlang-only externals in viva_math/precision (int_to_float, sqrt) and viva_math/autodiff_reverse (exp_f, ln_f, sin_f, cos_f, tanh_f, pow_f)

Decision: ship partial JS support honestly rather than rush a global port. viva_math/random is the canonical module that downstream packages need on the JS target; precision and autodiff_reverse are Erlang-only for now (documented in docs/numerical-accuracy.md and README.md). Future minor release: complete the JS FFI for those two modules.

Completed — JavaScript target (full coverage)

What started as a partial port (viva_math/random only) escalated to a sweep across every Erlang FFI-bound module. Codex GPT-5.5 added @external(javascript, ...) annotations for: scalar, random, precision, autodiff_reverse, matrix, complex, scheduler, statistics, transport, special, plus benches and test helpers. All new JS implementations live in src/viva_math_random_ffi.mjs and reuse Math.exp, Math.log, Math.sqrt, etc.

TargetStatus
gleam test --target erlang522 passed, no failures
gleam test --target javascript522 passed, no failures

Caveat: matrix_dense.gleam emits non-fatal warnings on the JS target about float-little-size(64) BitArray patterns (BEAM-specific representation) — the tests still pass because JS doesn’t actually need the byte-level layout for the public surface.

Improved — special.digamma precision (1000× better)

The threshold of the recurrence-to-asymptotic-series switch went from N = 12 to N = 20. The Bernoulli series truncated at x⁻¹⁰ converges fast enough at x ≥ 20 that the residual is below 5 ULP.

FunctionBefore (N=12)After (N=20)
digamma(1.0) ULP10855
digamma(10.0) ULP2712

test/golden_mpmath_test.gleam had its digamma tolerance brought to the same ≤5 ULP bound used for erf, gamma, lgamma, etc — the audit-revealed outliers are gone.

Improved — Sinkhorn (log-domain + early stopping)

The naive Sinkhorn iteration in wasserstein_2_multivariate was rewritten in log-domain following Schmitzer (2019):

α_i = ε·log(a_i) − ε·logsumexp_j(−C[i,j]/ε + β_j/ε)
β_j = ε·log(b_j) − ε·logsumexp_i(−C[i,j]/ε + α_i/ε)
π[i,j] = exp((α_i + β_j − C[i,j])/ε)

Routes through scalar.logsumexp (numerically stable max + ln(Σ exp(· − max))), so the algorithm now handles small ε (e.g. 0.001) without exp(−C/ε) underflowing to zero.

Early stopping added with marginal violation tol = 1.0e-9:

max(‖u·(K·v) − a‖_∞, ‖v·(Kᵀ·u) − b‖_∞) < tol

max_iter is now an upper bound (documented in the docstring). Empirical performance:

max_iterResultWall time
2000.24503742976953022~5.16 ms
100000.24503742976955287~5.68 ms

The 10 000-iter run does not waste 50× more work — early stopping terminates after convergence.

Final tally — 1.2.102

Test files (current)

test/
├── test_support.gleam                    (shared helpers)
├── autodiff_test.gleam                   (28 tests, new)
├── autodiff_reverse_test.gleam           (16 tests, new)
├── matrix_dense_test.gleam               (17 tests, new)
├── tdigest_test.gleam                    (14 tests, new)
├── special_test.gleam                    (25 tests, new)
├── ou_test.gleam                         (11 tests)
├── transport_test.gleam                  (8 tests)
├── free_energy_variational_test.gleam    (14 tests)
├── viva_math_test.gleam                  (legacy per-domain blocks)
├── fft_test.gleam
├── precision_test.gleam
├── property_test.gleam
├── qcheck_test.gleam
└── sota_test.gleam

1.2.101 - 2026-05-21

Self-contained release. The library no longer depends on gleam_community_maths. All transcendental, trigonometric and root operations previously delegated to that package are now provided by viva_math/scalar via Erlang :math BIFs (no runtime overhead).

Added — viva_math/scalar

Changed

Validated

1.2.100 - 2026-05-21

Scientific computing milestone. The library grew from 7 to 22 modules with compensated summation, autodiff (forward and reverse), symplectic integrators, FFT, quaternions, complex numbers, t-digest streaming quantiles, a binary dense matrix backend, and hierarchical predictive coding aligned with 2025-2026 papers.

Added — High-precision numerics

Added — Activations and SOTA 2026 papers

Added — Automatic differentiation

Added — ODE and dynamical systems

Added — Linear algebra

Added — Probability and statistics

Added — Signal processing

Added — Hierarchical active inference (2026 papers)

Added — Tooling and CI

Added — Testing

Changed

References

1.2.0 - 2026-01-24

Added

Changed

Validated

References

1.1.0 - 2026-01-24

Added

Changed

References

1.0.0 - 2026-01-23

Added

Dependencies

References

Search Document