Normalization¶
All hyperbolic normalization layers in one place — previously these were scattered across the convolution and Hypformer sections. For which normalizer to use between which layers, see the NN Layers guide.
Channel convention
HRC and gyro norms take the spatial dimension num_features = d (not ambient).
PoincareBatchNorm2D also takes spatial d (Poincaré has no time component).
Poincaré Batch Normalization¶
PoincareBatchNorm2D operates in tangent space (matching conv-layer I/O), mapping to
the manifold internally for the geometric operations (Einstein midpoint, Fréchet
variance, parallel transport, variance rescaling). Use between Poincaré conv layers
following the ResNet pattern conv → bn → relu → conv → bn → skip. The midpoint /
variance helpers it builds on live in Primitives.
hyperbolix.nn_layers.PoincareBatchNorm2D ¶
PoincareBatchNorm2D(
manifold_module: Poincare,
num_features: int,
*,
use_running_average: bool = False,
momentum: float = 0.9,
eps: float = 1e-06,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Poincaré batch normalization for 2D feature maps.
Operates in tangent space (matching conv layer I/O). Internally maps to the manifold for geometric operations (midpoint, parallel transport, variance rescaling), then maps back to tangent space.
Follows nnx.BatchNorm interface: use_running_average is a
constructor parameter overridable at call time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifold_module
|
Poincare
|
Poincaré manifold instance. |
required |
num_features
|
int
|
Number of channels (Poincaré ball dimension per pixel). |
required |
use_running_average
|
bool
|
If True, use running statistics instead of batch statistics (default: False). Overridable at call time. |
False
|
momentum
|
float
|
EMA momentum for running statistics (default: 0.9). |
0.9
|
eps
|
float
|
Numerical stability floor (default: 1e-6). |
1e-06
|
param_dtype
|
DTypeLike
|
Storage dtype of the learnable parameters and running statistics
(default: jnp.float32). Compute precision of manifold operations is
set by |
float32
|
Notes
The learned variance self.var is an unconstrained scalar used as
sqrt(var / (batch_var + eps)) — if gradient descent drives it
negative, the sqrt produces NaN. This matches the reference (van
Spengler's PoincareBatchNorm uses an unconstrained nn.Parameter
under the same sqrt), so we keep the parameterization; in practice the
init at 1.0 and typical learning rates keep it positive. If you observe
NaNs originating here, reparameterize via softplus in your model or
clamp self.var after optimizer steps.
References
van Spengler et al. "Poincaré ResNet." ICML 2023.
Source code in hyperbolix/nn_layers/poincare_batchnorm.py
__call__ ¶
__call__(
x_BHWC: Float[Array, "B H W C"],
c: float = 1.0,
use_running_average: bool | None = None,
) -> Float[Array, "B H W C"]
Apply Poincaré batch normalization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_BHWC
|
(Array, shape(B, H, W, C))
|
Tangent-space input features. |
required |
c
|
float
|
Curvature (positive, default: 1.0). |
1.0
|
use_running_average
|
bool or None
|
Override constructor setting. None uses constructor value. |
None
|
Returns:
| Type | Description |
|---|---|
(Array, shape(B, H, W, C))
|
Normalized tangent-space output. |
Source code in hyperbolix/nn_layers/poincare_batchnorm.py
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | |
Gyro Normalization (Hyperboloid, Proper Velocity & Poincaré)¶
Intrinsic normalization that operates directly on manifold points via gyrovector
operations (addition, scalar_mul) — no tangent-space round trip for the affine
part. Batch normalization is provided for the Lorentz/Hyperboloid and Proper Velocity
models (the Poincaré ball's batch normalizer is the tangent-space PoincareBatchNorm2D
above); the per-sample radial RMSNorm covers all three.
- Gyrogroup Batch Normalization (
HyperboloidGyroBatchNorm,ProperVelocityGyroBatchNorm) — a port of GyroBN (Chen et al., ICLR 2024 / 2025). Centers by gyro-translating with the inverse batch mean, scales by the inverse Fréchet standard deviation, biases with a learned manifold point, and keeps running statistics for evaluation (use_running_average). The Hyperboloid batch mean is the closed-form Lorentz centroid; the PV mean is the closed-form log-Euclidean mean. Use for faithful hyperbolic ResNets. - Gyro radial RMSNorm (
HyperboloidGyroRMSNorm,ProperVelocityGyroRMSNorm,PoincareGyroRMSNorm) — a per-sample, batch-independent normalizer (no running statistics, identical in train and eval, valid at batch size 1 — the properties RL workloads want). Each point's geodesic radius is rescaled to a learned targetgammavia a single gyro scalar-multiplication, with optional gyro-bias (use_bias). The manifold analog of RMSNorm: normalizes magnitude (hierarchy depth) while preserving direction. Möbiusscalar_mul(Poincaré) and Lorentz/PVscalar_mulscale geodesic radius identically, so the same layer body serves all three models.
hyperbolix.nn_layers.HyperboloidGyroBatchNorm ¶
Bases: GyroBatchNormBase
GyroBN for the Hyperboloid (Lorentz) model.
Inputs are ambient (..., D+1) hyperboloid points; num_features is the
spatial dimension D. The batch mean is the closed-form Lorentz centroid
(HELM, Chen et al. 2024) via :func:lorentz_midpoint — exact and JIT-friendly,
matching the estimator the ILNN GyroBN reference uses in practice.
Source code in hyperbolix/nn_layers/gyro_normalization.py
hyperbolix.nn_layers.ProperVelocityGyroBatchNorm ¶
Bases: GyroBatchNormBase
GyroBN for the Proper Velocity (PV) model.
Inputs are (..., D) PV points; num_features = D. PV has no closed-form
centroid, so the batch mean is the closed-form log-Euclidean mean
expmap_0(mean_i logmap_0(x_i)) (the GyroBN reference's use_euclid_stats
mode): no iteration, fully vmap/JIT-clean.
Source code in hyperbolix/nn_layers/gyro_normalization.py
hyperbolix.nn_layers.HyperboloidGyroRMSNorm ¶
Bases: GyroRMSNormBase
Gyro radial RMSNorm for the Hyperboloid (Lorentz) model.
Inputs are ambient (..., D+1) hyperboloid points; num_features = D.
Source code in hyperbolix/nn_layers/gyro_normalization.py
hyperbolix.nn_layers.ProperVelocityGyroRMSNorm ¶
Bases: GyroRMSNormBase
Gyro radial RMSNorm for the Proper Velocity (PV) model.
Inputs are (..., D) PV points; num_features = D.
Source code in hyperbolix/nn_layers/gyro_normalization.py
hyperbolix.nn_layers.PoincareGyroRMSNorm ¶
Bases: GyroRMSNormBase
Gyro radial RMSNorm for the Poincaré ball model.
Inputs are on-ball points (..., D) (no time coordinate); num_features = D.
The Poincaré ball has no time dimension, so embed_spatial_0 is the identity and
the ambient/spatial distinction collapses. The radial rescale uses Möbius
scalar_mul exactly as the Hyperboloid/PV variants use Lorentz/PV scalar_mul;
all three scale the geodesic radius dist_0(x) -> |r|·dist_0(x) linearly, so a
single r = gamma / (dist_0(x) + eps) sends every sample to radius ≈ gamma.
This is the per-sample, batch-independent counterpart to the (tangent-space,
batch-statistics) :class:PoincareBatchNorm2D — valid at batch size 1 and identical
in train and eval.
Source code in hyperbolix/nn_layers/gyro_normalization.py
HRC Normalization & Dropout (Hyperboloid)¶
HRC-wrapped normalizers and dropout for the Hyperboloid (Hypformer). They apply a
Euclidean operation to the space components and reconstruct the time component, with
curvature-change support. FGGMeanOnlyBatchNorm pairs with
FGGLinear(use_weight_norm=True).
hyperbolix.nn_layers.HRCBatchNorm ¶
HRCBatchNorm(
num_features: int,
*,
rngs: Rngs,
momentum: float = 0.99,
epsilon: float = 1e-05,
eps: float = 1e-07,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Hyperbolic Regularization Component with batch normalization.
Applies batch normalization to spatial components of hyperboloid points, then reconstructs the time component for the output curvature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_features
|
int
|
Number of spatial features to normalize. |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization. |
required |
momentum
|
float
|
Momentum for running statistics (default: 0.99). |
0.99
|
epsilon
|
float
|
Small value for numerical stability in batch norm (default: 1e-5). |
1e-05
|
eps
|
float
|
Small value for numerical stability in HRC (default: 1e-7). |
1e-07
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32). |
float32
|
Attributes:
| Name | Type | Description |
|---|---|---|
bn |
BatchNorm
|
Flax batch normalization. |
eps |
float
|
Numerical stability parameter for HRC. |
Notes
Training vs Evaluation Mode: During training (use_running_average=False), batch norm computes statistics from the current batch and updates running averages. During evaluation (use_running_average=True), it uses the accumulated running statistics.
Examples:
>>> from flax import nnx
>>> from hyperbolix.nn_layers import HRCBatchNorm
>>>
>>> # Create batch norm layer
>>> bn = HRCBatchNorm(num_features=64, rngs=nnx.Rngs(0))
>>>
>>> # Training mode
>>> y_train = bn(x, c_in=1.0, c_out=2.0, use_running_average=False)
>>>
>>> # Evaluation mode
>>> y_eval = bn(x, c_in=1.0, c_out=2.0, use_running_average=True)
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
__call__ ¶
__call__(
x: Float[Array, "batch dim_plus_1"],
c_in: float = 1.0,
c_out: float = 1.0,
use_running_average: bool | None = None,
) -> Float[Array, "batch dim_plus_1"]
Apply HRC batch normalization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, dim+1)
|
Input points on hyperboloid with curvature c_in. |
required |
c_in
|
float
|
Input curvature (default: 1.0). |
1.0
|
c_out
|
float
|
Output curvature (default: 1.0). |
1.0
|
use_running_average
|
bool or None
|
If True, use running statistics (eval mode). If False, use batch statistics (train mode). If None, use the default set during initialization. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
y |
Array of shape (batch, dim+1)
|
Output points on hyperboloid with curvature c_out. |
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
hyperbolix.nn_layers.HRCLayerNorm ¶
HRCLayerNorm(
num_features: int,
*,
rngs: Rngs,
epsilon: float = 1e-05,
eps: float = 1e-07,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Hyperbolic Regularization Component with layer normalization.
Applies layer normalization to spatial components of hyperboloid points, then reconstructs the time component for the output curvature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_features
|
int
|
Number of spatial features to normalize. |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization. |
required |
epsilon
|
float
|
Small value for numerical stability in layer norm (default: 1e-5). |
1e-05
|
eps
|
float
|
Small value for numerical stability in HRC (default: 1e-7). |
1e-07
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32). |
float32
|
Attributes:
| Name | Type | Description |
|---|---|---|
ln |
LayerNorm
|
Flax layer normalization. |
eps |
float
|
Numerical stability parameter for HRC. |
Examples:
>>> from flax import nnx
>>> from hyperbolix.nn_layers import HRCLayerNorm
>>>
>>> ln = HRCLayerNorm(num_features=64, rngs=nnx.Rngs(0))
>>> y = ln(x, c_in=1.0, c_out=2.0)
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
__call__ ¶
__call__(
x: Float[Array, "batch dim_plus_1"],
c_in: float = 1.0,
c_out: float = 1.0,
) -> Float[Array, "batch dim_plus_1"]
Apply HRC layer normalization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, dim+1)
|
Input points on hyperboloid with curvature c_in. |
required |
c_in
|
float
|
Input curvature (default: 1.0). |
1.0
|
c_out
|
float
|
Output curvature (default: 1.0). |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
y |
Array of shape (batch, dim+1)
|
Output points on hyperboloid with curvature c_out. |
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
hyperbolix.nn_layers.HRCRMSNorm ¶
HRCRMSNorm(
num_features: int,
*,
rngs: Rngs,
epsilon: float = 1e-06,
eps: float = 1e-07,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Hyperbolic Regularization Component with RMS normalization.
Applies RMS normalization to spatial components of hyperboloid points, then reconstructs the time component for the output curvature. RMSNorm is a simpler and faster variant of LayerNorm that only normalizes by the root mean square without centering.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_features
|
int
|
Number of spatial features to normalize. |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization. |
required |
epsilon
|
float
|
Small value for numerical stability in RMS norm (default: 1e-6). |
1e-06
|
eps
|
float
|
Small value for numerical stability in HRC (default: 1e-7). |
1e-07
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32). |
float32
|
Attributes:
| Name | Type | Description |
|---|---|---|
rms |
RMSNorm
|
Flax RMS normalization. |
eps |
float
|
Numerical stability parameter for HRC. |
Notes
RMSNorm Formula: y = x / RMS(x) * scale, where RMS(x) = sqrt(mean(x^2) + epsilon)
RMSNorm is more efficient than LayerNorm as it skips mean subtraction and is commonly used in modern transformers (e.g., LLaMA, GPT-NeoX).
Examples:
>>> from flax import nnx
>>> from hyperbolix.nn_layers import HRCRMSNorm
>>>
>>> rms = HRCRMSNorm(num_features=64, rngs=nnx.Rngs(0))
>>> y = rms(x, c_in=1.0, c_out=2.0)
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
__call__ ¶
__call__(
x: Float[Array, "batch dim_plus_1"],
c_in: float = 1.0,
c_out: float = 1.0,
) -> Float[Array, "batch dim_plus_1"]
Apply HRC RMS normalization.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, dim+1)
|
Input points on hyperboloid with curvature c_in. |
required |
c_in
|
float
|
Input curvature (default: 1.0). |
1.0
|
c_out
|
float
|
Output curvature (default: 1.0). |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
y |
Array of shape (batch, dim+1)
|
Output points on hyperboloid with curvature c_out. |
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
hyperbolix.nn_layers.HRCDropout ¶
Bases: Module
Hyperbolic Regularization Component with dropout.
Applies dropout to spatial components of hyperboloid points, then reconstructs the time component for the output curvature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rate
|
float
|
Dropout probability (fraction of units to drop). |
required |
rngs
|
Rngs
|
Random number generators for dropout. |
required |
eps
|
float
|
Small value for numerical stability (default: 1e-7). |
1e-07
|
Attributes:
| Name | Type | Description |
|---|---|---|
dropout |
Dropout
|
Flax dropout layer. |
eps |
float
|
Numerical stability parameter. |
Examples:
>>> from flax import nnx
>>> from hyperbolix.nn_layers import HRCDropout
>>>
>>> dropout = HRCDropout(rate=0.1, rngs=nnx.Rngs(dropout=42))
>>> y = dropout(x, c_in=1.0, c_out=1.0, deterministic=False)
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
__call__ ¶
__call__(
x: Float[Array, "batch dim_plus_1"],
c_in: float = 1.0,
c_out: float = 1.0,
deterministic: bool = False,
) -> Float[Array, "batch dim_plus_1"]
Apply HRC dropout.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, dim+1)
|
Input points on hyperboloid with curvature c_in. |
required |
c_in
|
float
|
Input curvature (default: 1.0). |
1.0
|
c_out
|
float
|
Output curvature (default: 1.0). |
1.0
|
deterministic
|
bool
|
If True, no dropout is applied (for evaluation mode). |
False
|
Returns:
| Name | Type | Description |
|---|---|---|
y |
Array of shape (batch, dim+1)
|
Output points on hyperboloid with curvature c_out. |
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
hyperbolix.nn_layers.FGGMeanOnlyBatchNorm ¶
FGGMeanOnlyBatchNorm(
num_features: int,
*,
momentum: float = 0.99,
eps: float = 1e-07,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Mean-only batch normalization for FGG-LNN layers.
Subtracts the batch mean from spatial components without dividing by variance, then adds a learnable bias. This avoids the instability that standard BatchNorm causes in hyperbolic space (exponentially large/small variance denominators) while still centering activations for stable training.
Designed to pair with weight normalization (FGGLinear(use_weight_norm=True)),
which handles magnitude control. Together they replace standard BatchNorm in
fully hyperbolic FGG-LNN networks.
Formula (applied to spatial components via HRC):
y = x - E[x] + bias
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_features
|
int
|
Number of spatial features (D, not D+1). |
required |
momentum
|
float
|
Exponential moving average momentum for running mean (default: 0.99).
Update: |
0.99
|
eps
|
float
|
Numerical stability floor for HRC time reconstruction (default: 1e-7). |
1e-07
|
param_dtype
|
DTypeLike
|
Storage dtype of the learnable bias and running mean (default: jnp.float32). |
float32
|
References
Klis et al. "Fast and Geometrically Grounded Lorentz Neural Networks" (2026), §4.4. Salimans & Kingma "Weight Normalization" (2016).
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
__call__ ¶
__call__(
x: Float[Array, "batch dim_plus_1"],
c_in: float = 1.0,
c_out: float = 1.0,
use_running_average: bool = False,
) -> Float[Array, "batch dim_plus_1"]
Apply mean-only batch normalization via HRC.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
(Array, shape(B, D + 1) or (B, ..., D + 1))
|
Input points on hyperboloid with curvature |
required |
c_in
|
float
|
Input curvature (default: 1.0). |
1.0
|
c_out
|
float
|
Output curvature (default: 1.0). |
1.0
|
use_running_average
|
bool
|
If True, use running mean (eval mode). If False, compute from batch and update running mean (train mode). Default: False. |
False
|
Returns:
| Type | Description |
|---|---|
Array, same shape as input
|
Points on hyperboloid with curvature |
Source code in hyperbolix/nn_layers/hyperboloid_regularization.py
Euclidean input scaling (hybrid networks)¶
HyperPPFeatureScaling is not an on-manifold normalizer — it runs entirely in
Euclidean space, applied after the last Euclidean layer and before expmap_0 to
either manifold. It is RMSNorm-based (RMSNorm + Lipschitz activation + dimension
scaling + optional learned rescaling), bounding the output norm via
rho_max = atanh(alpha) / sqrt(c). See the
boundary-pattern recipe.
hyperbolix.nn_layers.HyperPPFeatureScaling ¶
HyperPPFeatureScaling(
dim: int,
*,
alpha: float | None = None,
activation: Callable | None = jax.nn.tanh,
param_dtype: DTypeLike = jnp.float32,
rngs: Rngs,
)
Bases: Module
Hyper++ feature scaling for hybrid Euclidean-hyperbolic networks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dim
|
int
|
Embedding dimension (needed for nnx.Linear and 1/sqrt(d) scaling). |
required |
alpha
|
float or None
|
Value in (0, 1) enabling learned rescaling. None disables it and the layer is entirely parameter-free. Default: None. |
None
|
activation
|
Callable or None
|
Lipschitz activation function. Default: jax.nn.tanh. None to skip. |
tanh
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32). |
float32
|
rngs
|
Rngs
|
Random number generators for sub-layers. |
required |
Examples:
>>> from flax import nnx
>>> from hyperbolix.nn_layers import HyperPPFeatureScaling
>>>
>>> # Parameter-free mode
>>> layer = HyperPPFeatureScaling(dim=64, rngs=nnx.Rngs(0))
>>> y = layer(x, c=1.0)
>>>
>>> # Learned rescaling mode
>>> layer = HyperPPFeatureScaling(dim=64, alpha=0.9, rngs=nnx.Rngs(0))
>>> y = layer(x, c=0.1)
Source code in hyperbolix/nn_layers/hybrid_regularization.py
__call__ ¶
Apply Hyper++ feature scaling pipeline.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_BD
|
Array of shape (B, D)
|
Euclidean features from the last Euclidean layer. |
required |
c
|
float
|
Curvature parameter (positive). Passed at call time per library convention to support learnable curvature. |
1.0
|
Returns:
| Type | Description |
|---|---|
Array of shape (B, D)
|
Scaled features ready for expmap_0. |
Source code in hyperbolix/nn_layers/hybrid_regularization.py
Example¶
import jax
from hyperbolix.manifolds import Hyperboloid, ProperVelocity
from hyperbolix.nn_layers import HyperboloidGyroBatchNorm, HyperboloidGyroRMSNorm, ProperVelocityGyroRMSNorm
c = 1.0
hyp = Hyperboloid()
bn = HyperboloidGyroBatchNorm(hyp, num_features=16) # num_features = spatial D
rms = HyperboloidGyroRMSNorm(hyp, num_features=16)
spatial = jax.random.normal(jax.random.PRNGKey(0), (8, 16)) * 0.2
x_amb = jax.vmap(hyp.expmap_0, in_axes=(0, None))(jax.vmap(hyp.embed_spatial_0)(spatial), c) # (8, 17)
h = bn(x_amb, c=c) # training (running stats updated)
h = bn(x_amb, c=c, use_running_average=True) # evaluation (frozen stats)
h = rms(x_amb, c=c) # per-sample, batch-free
pv_rms = ProperVelocityGyroRMSNorm(ProperVelocity(), num_features=16, use_bias=True)
h_pv = pv_rms(jax.random.normal(jax.random.PRNGKey(1), (8, 16)) * 0.2, c=c) # (8, 16)