Changelog¶
All notable changes to Hyperbolix will be documented in this file.
Unreleased¶
Changed¶
- FGG layer init defaults changed to norm-preserving.
FGGLinearandFGGConv2Dnow default toreset_params="fan_out"(Gaussian,std=sqrt(1/out_spatial)) withinit_bias=0.0and a newgain=1.0multiplier, so the output spatial norm tracks the input (‖z‖ ≈ gain·‖x_spatial‖) instead of growing assqrt(out_channels)from step 0. This is a deliberate deviation from the Klis et al. 2026 classification reference (which normalizes magnitude away with BatchNorm/FGGMeanOnlyBatchNormafter each layer): it is motivated by unnormalized stacks — e.g. an RL backbone feeding a bounded Poincaré-ball projection, where the fan-in default saturated the projection. The newgainknob sets the effective column std togain/sqrt(out_spatial); it is a no-op forreset_params="eye"and is renormalized away underuse_weight_norm=True. Restore the previous reference-style init withreset_params="eye", init_bias=0.5(FGGLinear) orreset_params="lorentz_kaiming", init_bias=0.5(FGGConv2D).FGGLorentzMLR(a terminal Euclidean-logits head) is intentionally unchanged. Behavior change — existing FGG models pick up the new init unless they pass the oldreset_params/init_biasexplicitly - All NN layers now take a
param_dtypeconstructor argument (defaultjnp.float32) controlling the storage dtype of every trainable parameter and persistent state (batch-norm statistics, VQ codebooks, scalar scales/temperatures). The manifold'sdtypecontrols compute precision only. Under globaljax_enable_x64, parameters previously materialized as float64 (2× parameter/optimizer memory, float64 checkpoints); they now stay float32 unlessparam_dtype=jnp.float64is passed explicitly. The Riemannian optimizers complete the contract:egrad2rgrad/expmap/ptransprun in the manifold dtype, but returned updates and momentum buffers are cast back to the parameter's storage dtype (previously a float32ManifoldParamnext to a float64 manifold silently promoted its optimizer state to float64 after one step). See Numerical Stability — Storage vs. Compute Dtype. Breaking changes:FGGMeanOnlyBatchNorm(dtype=...)renamed toparam_dtype=...PoincareBatchNorm2DandHypVQEmbeddingPoincarestate is no longer pinned tomanifold.dtype— it defaults to float32; passparam_dtype=manifold.dtypeto restore the old behavior- Checkpoints saved under global x64 before this change carry float64 leaves and will dtype-mismatch on restore into float32-storage modules — re-initialize or cast the state tree on load
- Manifolds are now plain Python classes (not
nnx.Module). This structurally prevents shared-manifold bugs innnx.scan/nnx.fori_loop— manifolds become static graphdef attributes with no state pytree entries learnable=Trueremoved from manifold constructors. Use theLearnableCurvaturemodule instead — assign one instance per distinct curvature on your model. See the Manifolds User GuideProductManifoldcurvature API redesigned — every geometry method (dist,expmap,logmap,proj,origin,tangent_inner, etc.) now takes a positionalcargument that must be a sequence of lengthn_factors(one curvature per factor) instead of the silently-ignored scalarc. There is no default and no scalar broadcast: passproduct.curvaturesfor static curvatures, or build the sequence fromLearnableCurvaturecalls for trainable ones. The protocol-levelCurvaturetype was widened toScalarCurvature | Sequence[ScalarCurvature]soProductManifoldsatisfies theManifoldprotocol —isinstance(product, Manifold)isTrueand generic code typed againstManifoldaccepts product instances. The product still has nocattribute (useproduct.curvatures). Breaking change — call sites that passedc=0.0(or any scalar) must now pass a per-factor sequence(c_0, c_1, …)ProductManifoldis now a plain class;from_signatureaccepts 3- and 4-tuple specs only (5-tuplelearnableoverride removed)learnable_curvature()/get_curvature()functional helpers replaced by theLearnableCurvature(nnx.Module)class. The class bundles the raw parameter, reparameterization scheme, and clamp bounds in one object, making accidental init/recovery mismatches structurally impossible. Call sites change fromc = get_curvature(self.c_raw)toc = self.curvature(). Breaking change — no deprecation shimHyperboloid.additionnow implements the Lorentz gyrovector additionx ⊕ y = Exp_x(PT_{0→x}(Log_0(y)))(Chen et al. 2025b; Shi et al. 2026, Eq. 1), andProductManifold.additiondelegates it per factor. It forms a gyrocommutative gyrogroup (identity = origin, inverse⊖x = (-1) ⊙ x = [x₀, -x_s]) and coincides with Poincaré Möbius addition under the stereographic isometry. This replaces the earlier coordinate-wise formula, which was incorrect at every curvature (e.g.origin ⊕ y ≠ y) and failed silently via a trailing projection.Hyperboloid.scalar_mul(geodesic scaling, = Eq. 2) is unchanged. Breaking change —additionno longer raisesNotImplementedError
Added¶
-
LearnableCurvature(nnx.Module)— canonical module for trainable curvature inhyperbolix.utils.curvature, also exported at top level (from hyperbolix import LearnableCurvature). Supports two reparameterizations:parameterization="softplus"(default):c = softplus(raw), bounded gradient via sigmoid (van Spengler 2023 convention)parameterization="log":c = exp(raw), scale-invariant gradientdc/draw = c(MERU convention, preferred for compiled RL loops)
Default clamp
[0.1, 10.0]is applied to the recoveredc(not the raw parameter) as a hard stability guard for long compiled training loops; passc_min=None, c_max=Noneto disable. Updated by any standard Euclideannnx.Optimizer— no Riemannian optimizer needed - Product manifold (hyperbolix.manifolds.ProductManifold) — heterogeneous-curvature composition \(M_1 \times M_2 \times \dots \times M_n\) where each factor may be any base manifold with its own curvature (Gu et al. 2019). Points are flat concatenated arrays of shape(total_dim,); geometry methods take a per-factor curvature sequence as positionalcat call time. Provides Pythagorean L2 geodesic distance plus auxiliarydist_l1/dist_min/component_distreductions, full per-factor decomposition ofexpmap/logmap/ptransp/proj/egrad2rgrad/tangent_inner, anorigin(c)helper, and afrom_signaturefactory accepting 3- and 4-tuple specs -Hyperboloid.log_radius_concat— log-radius-preserving concatenation of N Lorentz points (Shi et al. 2026, Sec. 4.3), the hyperboloid analog of Poincaré β-concatenation. Rescales each block's spatial part byexp(½·(ψ(N·d/2) − ψ(d/2)))(digammaψ) so the expected log spatial radius stays invariant to the post-concat dimension, then recomputes the time coordinate to keep the result on the manifold. Reduces tohcatwhenN = 1; convolutional layers keep using the unscaledhcatby default - Proper Velocity (PV) manifold (hyperbolix.manifolds.ProperVelocity) — unconstrained \(\mathbb{R}^n\) model of hyperbolic geometry from Chen et al. (2026), with complete geometric operations:addition,scalar_mul,dist,expmap/logmap(at origin and arbitrary base points),ptransp/ptransp_0,egrad2rgrad, and Riemannian inner product - Proper Velocity neural-network layers: -HypLinearPV: PV fully-connected layer (Thm 5.3 / Eq. 22) -HypConv2DPV: PV 2D convolution with raw Euclidean patch concatenation (Sec 5.3) — no beta-scaling, dimension-preserving -HypRegressionPV: PV multinomial-logistic-regression head (Thm 5.2 / Eq. 19) - Poincaré vector quantization layers (hyperbolix.nn_layers): -HypVQEmbeddingPoincare: HVQ-VAE quantizer (Chen et al. 2025) — explicit on-ball codebook held as a non-parameternnx.Variablebuffer, geodesic nearest-neighbour selection, copy-gradient straight-through estimator, and a hyperbolic-EMA codebook update (GGBall, Bu et al. 2026, Eqs. 41-43) with optional dead-code revival via theema_updatemethod (called afteroptimizer.update; the optimizer never touches the codebook, only the commitment loss trains the encoder) -HypVQMLRPoincare: HyperVQ quantizer (Goswami et al. 2025) — implicit codebook = the rows of an internalHypRegressionPoincarePP, Gumbel-Softmax straight-through selection on the categorical weights (so plainoptax.adamtrains it), with adeterministicflag (toggled bymodel.eval()/model.train()) for a deterministic argmax MAP estimate at inference -PoincareVQOutput: shared pytree-friendlyNamedTuplereturn type(quantized, indices, loss, perplexity, z);quantizedis cast to float32 at the manifold→decoder boundary -poincare_weighted_midpoint: reusable Poincaré weighted gyromidpoint (GGBall Eq. 41), the Poincaré analog oflorentz_midpointand a weighted generalization ofpoincare_midpoint- MkDocs Material documentation system - Complete API reference documentation - Getting Started guide - CI/CD workflow for documentation builds - Positional encoding layers for hyperbolic Transformers: -lorentz_residual: Lorentzian midpoint-based residual connection -hope: Hyperbolic Rotary Positional Encoding (functional) -HyperbolicRoPE: NNX module wrapper for HOPE -HypformerPositionalEncoding: Learnable positional encoding with HTCLinear - Class-based manifold API with automatic dtype casting (Poincare,Hyperboloid,Euclidean) - Isometry mappings now span all three models — Poincaré ↔ Hyperboloid, Poincaré ↔ Proper Velocity, and Hyperboloid ↔ Proper Velocity. New functions:pv_to_poincare,poincare_to_pv,pv_to_hyperboloid,hyperboloid_to_pv(inhyperbolix.manifolds.isometry_mappings). All six maps are exact, curvature-correct Riemannian isometries (PVNN Eq. 4; Chen et al. 2026). The directpv_to_hyperboloid(PV coords are the hyperboloid's spatial part; time reconstructed from⟨z,z⟩_L = -1/c) avoids the near-boundary blow-up of composing through the ball -Manifoldstructural protocol for type-safe manifold dispatch - Causal attention masking (causal=True) for all three hyperbolic attention variants: -HyperbolicSoftmaxAttention: lower-triangular-infmask before softmax -HyperbolicFullAttention: lower-triangular-infmask on Lorentzian similarity scores -HyperbolicLinearAttention: O(N) cumulative-sum recurrence viajax.lax.scan(Katharopoulos et al. 2020), keeping O(N) complexity in causal mode - Tiny Shakespeare character-level benchmark (benchmarks/bench_shakespeare_attention.py) comparing all four model variants (Euclidean + 3 hyperbolic) with causal attention
Changed¶
- Breaking: Manifold public functions renamed to private (
dist()→_dist()); use class methods instead - Replaced
with_precision()wrapper withPoincare(dtype=jnp.float64)pattern - Breaking:
VERSION_LORENTZIAN_PROXYremoved from the Poincaré manifold. The formula treated Poincaré ball coordinate 0 as a Lorentz time component, so it was only meaningful for hyperboloid inputs and returneddist(x, x) = -2/c ≠ 0for ball points; the docs additionally mis-advertised it as "best near boundary". The reference implementation never had it.dist/dist_0now select between 3 versions (VERSION_MOBIUS_DIRECT,VERSION_MOBIUS,VERSION_METRIC_TENSOR). For a true Lorentzian (squared Minkowski) proxy, convert to the hyperboloid viaisometry_mappings.poincare_to_hyperboloidfirst - Breaking:
HypformerPositionalEncoding.epsilonis no longer a learnablennx.Param— it is a fixed, non-negative constructor float (default 1.0), matching the Hypformer reference (which keeps it a plain non-trainable tensor). A learnable epsilon could be driven below -1 by gradient descent, wherex + epsilon * pleaves the upper hyperboloid sheet and theabs()in thelorentz_residualnormalizer silently masks the violation.lorentz_residualnow documents thew_y >= 0requirement - Legacy Ganea layers (
HypLinearPoincare,HypRegressionPoincare) accept acurvatureconstructor argument (float or callable, default 1.0) for their manifold-valuedbiastag instead of hardcoding 1.0. The Riemannian optimizer uses this tag for the bias update, so it must match thecpassed at call time — previously anyc ≠ 1.0silently applied the wrong Riemannian correction to the bias
Added (VQ)¶
squared_commitmentflag onHypVQEmbeddingPoincare(defaultFalse). The cited references genuinely disagree on the commitment penalty: HVQ-VAE uses the plain geodesic distanced(z, sg(q)), GGBall's L_HVQVAE usesd²(the Euclidean VQ-VAE convention). The flag selects between them without touching library code
Fixed¶
- Silent float64 promotion under x64 in hyperboloid layers. Runtime buffers created without an explicit dtype (
FGGConv2DSAME/origin padding buffer, causal linear-attention scan carries, full-attention uniform head-averaging weights) now derive their dtype from the input, and the scalar attention parameters (temperature,scale,attn_bias) are cast to the compute dtype at use. Previously, withjax_enable_x64active, these defaulted to float64 and promoted float32 activations through the rest of the layer FGGConv2Ddocstring stated the default weight init is"kaiming"; the actual default is"lorentz_kaiming"PoincareBatchNorm2Dnow documents that the learned variance is an unconstrained scalar under a sqrt (NaN if driven negative) — kept as-is to match the van Spengler reference, with a workaround noted- Poincaré ↔ Hyperboloid isometry maps were only correct at
c=1.poincare_to_hyperboloid/hyperboloid_to_poincareused unit-ball stereographic formulas missing their√cfactors, so round-trips and distance preservation silently failed at any other curvature (the recommended default isc=0.1). Both are now curvature-correct at allc, verified by round-trip, isometry, commutative-diagram, and extreme-curvature tests across dtypes - Test fixture
uniform_pointsgenerated hyperboloid points at the wrong geodesic radius forc ≠ 1(an extra/√con the spatial coordinate, masked by a trailing projection). Corrected so generated points are the exact Poincaré images at any curvature
[0.1.4] - 2026-02¶
Added¶
- Pure JAX implementation of hyperbolic manifolds (Euclidean, Poincaré, Hyperboloid)
- 13+ neural network layers (linear, convolutional, regression)
- Hypformer components: HTC/HRC with curvature-change support
- 4 hyperbolic activation functions (ReLU, Leaky ReLU, Tanh, Swish)
- Riemannian optimizers (RSGD, RAdam) with automatic manifold detection
- Wrapped normal distributions for VAEs
- Comprehensive test suite (1,400+ tests)
- CI/CD pipeline with benchmarking
- vmap-native API design
Changed¶
- Migrated from PyTorch to pure JAX/Flax NNX
- Unified package structure:
hyperbolix_jax→hyperbolix
References¶
- Based on research by Ganea et al. (2018), Bécigneul & Ganea (2019), Bdeir et al. (2023)