Primitives & Helpers¶
Cross-cutting functions that back the layers on the other pages — the Hyperboloid transformation/regularization components, point-assembly and concatenation helpers, midpoints, residuals, and the statistical reductions used by normalization. They are collected here (rather than scattered across consumer pages) so a given helper has one documented home.
HTC / HRC components¶
The Hyperbolic Transformation Component (HTC) applies a Euclidean function to the
full point; the Hyperbolic Regularization Component (HRC) applies it to the
space components only, then reconstructs time. Both support curvature changes
(c_in → c_out) and underpin HTCLinear, the HRC* norms, and LorentzConv2D.
hyperbolix.nn_layers.htc ¶
htc(
x: Float[Array, "... in_dim_plus_1"],
f_t: Callable[[Float[Array, ...]], Float[Array, ...]],
c_in: float,
c_out: float,
eps: float = 1e-07,
) -> Float[Array, "... out_dim_plus_1"]
Hyperbolic Transformation Component.
Applies a Euclidean linear transformation f_t to the full hyperboloid point (including time component), then maps the result to the hyperboloid with curvature c_out.
Mathematical formula: space = sqrt(c_in/c_out) * f_t(x) time = sqrt(||space||^2 + 1/c_out) output = [time, space]
where f_t takes the full (dim+1)-dimensional input and produces the output spatial components.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (..., in_dim+1)
|
Input point(s) on the hyperboloid manifold with curvature c_in. All components (time and spatial) are passed to f_t. |
required |
f_t
|
Callable
|
Euclidean linear transformation applied to the full input. Takes (in_dim+1)-dimensional input and produces out_dim-dimensional output (which becomes the spatial components of the output). |
required |
c_in
|
float
|
Input curvature parameter (must be positive, c > 0). |
required |
c_out
|
float
|
Output curvature parameter (must be positive, c > 0). |
required |
eps
|
float
|
Small value for numerical stability (default: 1e-7). |
1e-07
|
Returns:
| Name | Type | Description |
|---|---|---|
y |
Array of shape (..., out_dim+1)
|
Output point(s) on the hyperboloid manifold with curvature c_out. |
Notes
- Unlike HRC, f_t operates on the full point including the time component
- f_t's output dimension determines the output spatial dimension
- This is typically used for learnable linear transformations
- The spatial scaling factor sqrt(c_in/c_out) ensures proper curvature transformation
See Also
hrc : Hyperbolic Regularization Component for spatial-only operations. HTCLinear : Module wrapper for htc with learnable linear transformation.
References
Hypformer paper (citation to be added)
Examples:
>>> import jax
>>> import jax.numpy as jnp
>>> from hyperbolix.nn_layers.hyperboloid_core import htc
>>> from hyperbolix.manifolds import Hyperboloid
>>>
>>> # Create a point on the hyperboloid
>>> manifold = Hyperboloid()
>>> x = jnp.array([1.05, 0.1, -0.2, 0.15])
>>> x = manifold.proj(x, c=1.0)
>>>
>>> # Define a linear transformation
>>> W = jax.random.normal(jax.random.PRNGKey(0), (3, 4))
>>> def linear(z):
... return z @ W.T
>>>
>>> # Apply HTC
>>> y = htc(x, linear, c_in=1.0, c_out=2.0)
>>> y.shape
(4,) # (3 spatial + 1 time)
Source code in hyperbolix/nn_layers/hyperboloid_core.py
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 | |
hyperbolix.nn_layers.hrc ¶
hrc(
x: Float[Array, "... dim_plus_1"],
f_r: Callable[[Float[Array, ...]], Float[Array, ...]],
c_in: float,
c_out: float,
eps: float = 1e-07,
) -> Float[Array, "... out_dim_plus_1"]
Hyperbolic Regularization Component.
Applies a Euclidean regularization/activation function f_r to the spatial components of hyperboloid points, then maps the result to the hyperboloid with curvature c_out.
Mathematical formula: space = sqrt(c_in/c_out) * f_r(x[..., 1:]) time = sqrt(||space||^2 + 1/c_out) output = [time, space]
When c_in = c_out = c, this reduces to: output = [sqrt(||f_r(x_s)||^2 + 1/c), f_r(x_s)] which is the pattern used by curvature-preserving hyperboloid activations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (..., dim+1)
|
Input point(s) on the hyperboloid manifold with curvature c_in. The first element is the time-like component, remaining are spatial. |
required |
f_r
|
Callable
|
Euclidean function to apply to spatial components. Can be any activation, normalization, dropout, etc. Takes spatial components and returns transformed spatial components (may change dimension). |
required |
c_in
|
float
|
Input curvature parameter (must be positive, c > 0). |
required |
c_out
|
float
|
Output curvature parameter (must be positive, c > 0). |
required |
eps
|
float
|
Small value for numerical stability (default: 1e-7). |
1e-07
|
Returns:
| Name | Type | Description |
|---|---|---|
y |
Array of shape (..., out_dim+1)
|
Output point(s) on the hyperboloid manifold with curvature c_out. |
Notes
- f_r operates only on spatial components x[..., 1:], not the time component
- The time component is reconstructed using the hyperboloid constraint: -x₀² + ||x_rest||² = -1/c_out
- This avoids expensive exp/log maps while maintaining mathematical correctness
- The spatial scaling factor sqrt(c_in/c_out) ensures proper curvature transformation
See Also
htc : Hyperbolic Transformation Component for full-point operations.
References
Hypformer paper (citation to be added)
Examples:
>>> import jax.numpy as jnp
>>> from hyperbolix.nn_layers.hyperboloid_core import hrc
>>> from hyperbolix.manifolds import Hyperboloid
>>>
>>> # Create a point on the hyperboloid
>>> manifold = Hyperboloid()
>>> x = jnp.array([1.05, 0.1, -0.2, 0.15])
>>> x = manifold.proj(x, c=1.0)
>>>
>>> # Apply HRC with ReLU (curvature-preserving)
>>> y = hrc(x, jax.nn.relu, c_in=1.0, c_out=1.0)
>>>
>>> # Apply HRC with curvature change
>>> y = hrc(x, jax.nn.relu, c_in=1.0, c_out=2.0)
>>>
>>> # Custom activation
>>> def custom_act(z):
... return jax.nn.gelu(z) * 0.5
>>> y = hrc(x, custom_act, c_in=1.0, c_out=0.5)
Source code in hyperbolix/nn_layers/hyperboloid_core.py
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 | |
Point assembly & concatenation¶
Helpers that build valid hyperboloid points from spatial/tangent data and extract convolution patches.
hyperbolix.nn_layers.spatial_to_hyperboloid ¶
spatial_to_hyperboloid(
spatial: Float[Array, "... D"],
c_in: float,
c_out: float,
eps: float = 1e-07,
) -> Float[Array, "... D_plus_1"]
Scale spatial components and reconstruct time to produce a hyperboloid point.
Extracts the common tail of hrc/htc: curvature scaling + time
reconstruction via the hyperboloid constraint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spatial
|
(Array, shape(..., D))
|
Spatial components (no time coordinate). |
required |
c_in
|
float
|
Source curvature (positive). |
required |
c_out
|
float
|
Target curvature (positive). |
required |
eps
|
float
|
Numerical stability floor (default: 1e-7). |
1e-07
|
Returns:
| Type | Description |
|---|---|
(Array, shape(..., D + 1))
|
Points on the hyperboloid with curvature |
Source code in hyperbolix/nn_layers/hyperboloid_core.py
hyperbolix.nn_layers.sinh_lift_to_hyperboloid ¶
sinh_lift_to_hyperboloid(
spatial_BO: Float[Array, "... O"],
c: float,
v_max: float,
eps: float = 1e-07,
) -> Float[Array, "... O_plus_1"]
Lift spatial pre-activations to the hyperboloid via the element-wise sinh diffeomorphism.
Shared output map of HypLinearHyperboloidPLFC (Shi et al. 2026, point-to-hyperplane)
and HypLinearHyperboloidBusemann (Chen et al. 2026, Thm. 4.2, point-to-horosphere):
both compute a per-output score, then recover a hyperboloid point by ::
y_s = sinh(clip(√c · score, ±v_max)) / √c
y_t = √(‖y_s‖² + 1/c)
The clip is an output-side overflow guard: compute_mlr / the Busemann logit only
bound an asinh/log argument, leaving the score ∝ ‖kernel_row‖ unbounded, and
the sinh exponentiates it. Hard jnp.clip (not smooth_clamp) matches the Shi
et al. 2026 reference and is value-identical for |√c·score| ≤ v_max — it only zeroes
the gradient in the already-saturated tail. Callers must _assert_v_max_safe(v_max) so
the bare jnp.sinh below cannot overflow float32.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spatial_BO
|
(Array, shape(..., O))
|
Per-output scores (the sinh argument, pre-scaling). |
required |
c
|
float
|
Curvature parameter (positive). |
required |
v_max
|
float
|
Hard clip bound on |
required |
eps
|
float
|
Numerical stability floor passed to :func: |
1e-07
|
Returns:
| Type | Description |
|---|---|
(Array, shape(..., O + 1))
|
Points on the hyperboloid with curvature |
Source code in hyperbolix/nn_layers/hyperboloid_core.py
hyperbolix.nn_layers.build_spacelike_V ¶
build_spacelike_V(
U_IO: Float[Array, "I O"],
b_O: Float[Array, O],
c: float,
eps: float = 1e-07,
) -> Float[Array, "Ai O"]
Build spacelike V matrix with Minkowski metric absorbed.
Constructs the spacelike weight vectors from Euclidean weights U and bias b
via parallel transport (Eq. 12, Klis et al. 2026). The Minkowski metric
signature diag(-1, +1, ..., +1) is absorbed into the time row so that the
forward pass reduces to a single x @ V matmul.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
U_IO
|
(Array, shape(I, O))
|
Euclidean weight matrix. I = in_spatial, O = out_spatial. |
required |
b_O
|
(Array, shape(O))
|
Bias per output neuron. |
required |
c
|
float
|
Curvature parameter (positive). |
required |
eps
|
float
|
Numerical stability floor for column norms (default: 1e-7). |
1e-07
|
Returns:
| Name | Type | Description |
|---|---|---|
V_AiO |
(Array, shape(I + 1, O))
|
Spacelike V matrix with negated time row (Minkowski metric absorbed).
Columns are v^(i) with |
References
Klis et al. "Fast and Geometrically Grounded Lorentz Neural Networks" (2026), Eq. 12.
Source code in hyperbolix/nn_layers/hyperboloid_core.py
hyperbolix.nn_layers.extract_patches ¶
extract_patches(
x: Float[Array, "batch height width in_channels"],
kernel_size: tuple[int, int],
stride: tuple[int, int],
padding: str,
pad_mode: str = "edge",
c: float | None = None,
) -> Float[
Array,
"batch out_height out_width kernel_h kernel_w in_channels",
]
Extract receptive-field patches for hyperboloid convolutions.
Shared by all hyperboloid conv layers (HypConv2DHyperboloid,
HypConv2DHyperboloidFHNN, FGGConv2D, HypConv2DHyperboloidILNN). The
patches are returned in point-major (kh, kw, C) order so that the
downstream per-point ops (hcat / log_radius_concat) can consume them as
(K, C) after flattening; this is why the channel-major output of
jax.lax.conv_general_dilated_patches is transposed.
Padding is applied manually because the conv primitive zero-pads, and zero is not a point on the hyperboloid:
pad_mode="edge"— replicate the border (default).pad_mode="origin"— fill with the manifold origin(sqrt(1/c), 0, ..., 0);cis required in this mode (matching the Shi et al. 2026 / Klis et al. 2026 reference padding).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
(Array, shape(B, H, W, in_channels))
|
Input feature map of hyperboloid points (ambient channels, time first). |
required |
kernel_size
|
tuple[int, int]
|
|
required |
stride
|
tuple[int, int]
|
|
required |
padding
|
str
|
|
required |
pad_mode
|
str
|
|
'edge'
|
c
|
float or None
|
Curvature, required only for |
None
|
Returns:
| Type | Description |
|---|---|
(Array, shape(B, out_height, out_width, kernel_h, kernel_w, in_channels))
|
Receptive-field patches in point-major order. |
Source code in hyperbolix/nn_layers/hyperboloid_core.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
Midpoints & residuals¶
hyperbolix.nn_layers.lorentz_midpoint ¶
lorentz_midpoint(
points: Float[Array, "... M A"],
weights: Float[Array, "... N M"],
c: float,
eps: float = 1e-07,
) -> Float[Array, "... N A"]
Weighted Lorentzian midpoint over M points.
Generalises :func:lorentz_residual (which handles two points) to an
arbitrary weighted combination used by full attention aggregation and
multi-head averaging.
Formula (HELM, Chen et al. 2024):
h = weights @ points (weighted sum)
mu = h / (sqrt(c) * ||h||_L)
where ||h||_L = sqrt(-<h,h>_L) and <h,h>_L = -h_0^2 + ||h_s||^2.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points
|
(Array, shape(..., M, A))
|
Points on the hyperboloid with curvature |
required |
weights
|
(Array, shape(..., N, M))
|
Combination weights (e.g. attention weights, uniform |
required |
c
|
float
|
Curvature parameter (positive). |
required |
eps
|
float
|
Numerical stability floor (default: 1e-7). |
1e-07
|
Returns:
| Type | Description |
|---|---|
(Array, shape(..., N, A))
|
Midpoints on the hyperboloid with curvature |
Source code in hyperbolix/nn_layers/hyperboloid_core.py
hyperbolix.nn_layers.lorentz_residual ¶
lorentz_residual(
x: Float[Array, "... dim_plus_1"],
y: Float[Array, "... dim_plus_1"],
w_y: float | Float[Array, ""],
c: float,
eps: float = 1e-07,
) -> Float[Array, "... dim_plus_1"]
Lorentzian residual connection (LResNet, He et al. 2025).
Computes the weighted Lorentzian midpoint of x and y, projecting back to the hyperboloid:
ave = x + w_y * y
result = ave / sqrt(c * |<ave, ave>_L|)
where _L = -a_0^2 + ||a_s||^2 is the Minkowski inner product.
.. warning::
w_y must be non-negative. For x, y on the upper sheet, any conic
combination x + w_y * y with w_y >= 0 stays future-directed
timelike, so the normalization returns a valid hyperboloid point. For
w_y < 0 the combination can turn spacelike (<ave, ave>_L > 0,
roughly w_y < -1 for nearby points) or land on the lower sheet
(ave_0 < 0) — the abs() in the normalizer then converts the
geometry violation into a "valid-looking" but wrong output instead of
raising. This is why callers must not expose w_y as an
unconstrained learnable parameter.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
(Array, shape(..., d + 1))
|
Points on hyperboloid with curvature c. |
required |
y
|
(Array, shape(..., d + 1))
|
Points on hyperboloid with curvature c (to be added with weight w_y). |
required |
w_y
|
float or scalar Array
|
Weight for the y contribution. Must be >= 0 (see warning above). |
required |
c
|
float
|
Curvature parameter (positive, c > 0). |
required |
eps
|
float
|
Numerical stability floor (default: 1e-7). |
1e-07
|
Returns:
| Type | Description |
|---|---|
(Array, shape(..., d + 1))
|
Points on hyperboloid with curvature c. |
References
He, Neil, Menglin Yang, and Rex Ying. "Lorentzian residual neural networks." Proceedings of the 31st ACM SIGKDD Conference on Knowledge Discovery and Data Mining V. 1. 2025. (Also adopted as the residual connection in HELM, Chen et al. 2024, Eq. 2.)
Source code in hyperbolix/nn_layers/hyperboloid_core.py
hyperbolix.nn_layers.lorentz_scale ¶
lorentz_scale(
m: Float[Array, "... dim_plus_1"],
gamma: float | Float[Array, ""],
c: float,
eps: float = 1e-07,
) -> Float[Array, "... dim_plus_1"]
Klein-geodesic output rescaling of a hyperboloid point (LResNet, Eq. 10).
Scales the spatial part by gamma and recomputes the time component so the
result stays on the hyperboloid::
m_s' = gamma * m_s
m_t' = sqrt(||m_s'||^2 + 1/c)
The isometry to the Klein model is phi_K(x) = x_s / x_t and Klein geodesics
are Euclidean lines through the origin, so this simply slides m along the
geodesic ray from the origin -- changing the Euclidean / geodesic norm of the
output while keeping it on the manifold. It restores expressiveness the bare
Lorentzian residual lacks: :func:lorentz_residual's output is bounded by the
larger of its two inputs, whereas gamma > 1 can push the norm beyond both.
Unlike lorentz_residual's w_y, any real gamma yields a valid
hyperboloid point (m_t' > 0 always), so a learnable gamma is safe.
gamma = 1 is the identity (for an on-manifold m, recomputing the time
coordinate returns it unchanged); the LResNet reference uses a fixed
gamma = 2 for its CIFAR configurations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
m
|
(Array, shape(..., d + 1))
|
Points on the hyperboloid with curvature |
required |
gamma
|
float or scalar Array
|
Scaling constant. Positive values slide toward ( |
required |
c
|
float
|
Curvature parameter (positive, c > 0). |
required |
eps
|
float
|
Numerical stability floor (default: 1e-7). |
1e-07
|
Returns:
| Type | Description |
|---|---|
(Array, shape(..., d + 1))
|
Rescaled points on the hyperboloid with curvature |
References
He, Neil, Menglin Yang, and Rex Ying. "Lorentzian residual neural networks."
Proceedings of the 31st ACM SIGKDD Conference on Knowledge Discovery and Data Mining V. 1. 2025.
Eq. (10) (optional output scaling). See :func:lorentz_residual for the residual
this complements.
Source code in hyperbolix/nn_layers/hyperboloid_core.py
Attention focus transform¶
hyperbolix.nn_layers.focus_transform ¶
focus_transform(
x_D: Float[Array, "... D"],
temperature: Float[Array, ""],
power: float,
eps: float = 1e-07,
) -> Float[Array, "... D"]
Norm-preserving focus function (Eq 19, Hypformer).
Applies temperature-scaled ReLU followed by element-wise power sharpening while preserving the original norm.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_D
|
(Array, shape(..., D))
|
Input spatial features. |
required |
temperature
|
scalar Array
|
Learnable temperature parameter. |
required |
power
|
float
|
Sharpening exponent ( |
required |
eps
|
float
|
Numerical stability floor (default: 1e-7). |
1e-07
|
Returns:
| Type | Description |
|---|---|
(Array, shape(..., D))
|
Focus-transformed features with |
Source code in hyperbolix/nn_layers/hyperboloid_attention.py
Poincaré statistics & midpoints¶
Reductions used by PoincareBatchNorm2D and the Poincaré VQ codebook. frechet_variance
is manifold-agnostic (mean squared geodesic distance); poincare_weighted_midpoint
is the GGBall weighted gyromidpoint (the Poincaré analog of lorentz_midpoint).
hyperbolix.nn_layers.poincare_midpoint ¶
poincare_midpoint(
x_NC: Float[Array, "N C"],
manifold: Poincare,
c: float,
eps: float = 1e-06,
) -> Float[Array, C]
Compute the (Einstein) gyromidpoint of Poincaré ball points.
Delegates to :func:poincare_weighted_midpoint (GGBall Eq. 41) with uniform
weights::
mu = (1/2) ⊗_c [ Σ λ(x_n)·x_n / Σ (λ(x_n) - 1) ]
NOTE: an earlier version computed Σ(λ²·x) / Σ(λ²), which is not a
midpoint (it is not equidistant for two points and is biased toward points
near the ball boundary, where λ explodes).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_NC
|
(Array, shape(N, C))
|
Points on the Poincaré ball. |
required |
manifold
|
Poincare
|
Poincaré manifold instance. |
required |
c
|
float
|
Curvature (positive). |
required |
eps
|
float
|
Numerical stability floor (default: 1e-6). |
1e-06
|
Returns:
| Type | Description |
|---|---|
(Array, shape(C))
|
Gyromidpoint on the Poincaré ball. |
Source code in hyperbolix/nn_layers/poincare_batchnorm.py
hyperbolix.nn_layers.poincare_weighted_midpoint ¶
poincare_weighted_midpoint(
points_MC: Float[Array, "M C"],
weights_NM: Float[Array, "N M"],
manifold: Poincare,
c: float,
eps: float = 1e-06,
) -> Float[Array, "N C"]
Weighted Poincaré gyromidpoint over M points (GGBall Eq. 41).
Generalises :func:poincare_midpoint (the unweighted Einstein midpoint of N
points) to N weighted midpoints, mirroring the hyperboloid
:func:hyperbolix.nn_layers.lorentz_midpoint. For weight-vector w_n over
the M points::
mu_n = (1/2) ⊗_c [ Σ_m w_nm · λ(x_m) · x_m / Σ_m w_nm · (λ(x_m) - 1) ]
where λ(x) = 2 / (1 - c‖x‖²) is the conformal factor and ⊗_c is
Möbius scalar multiplication. The ½ ⊗_c half-scaling is exactly what turns
the conformal-weighted Euclidean average into the gyro-midpoint on the ball
(for N copies of a single point it returns that point — see the algebra in
the tests).
This is the codebook-centroid operator of the hyperbolic-EMA VQ update
(GGBall, Bu et al. 2026): pass the assigned encoder points as points and
the transposed one-hot assignment matrix (num_codes, N) as weights to
get one midpoint per code.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
points_MC
|
(Array, shape(M, C))
|
Points on the Poincaré ball with curvature |
required |
weights_NM
|
(Array, shape(N, M))
|
Combination weights, one row per output midpoint. Typically non-negative
(assignment / attention weights). Because |
required |
manifold
|
Poincare
|
Poincaré manifold instance (supplies |
required |
c
|
float
|
Curvature (positive). |
required |
eps
|
float
|
Denominator floor guarding the empty-row 0/0 (default: 1e-6). |
1e-06
|
Returns:
| Type | Description |
|---|---|
(Array, shape(N, C))
|
Weighted gyromidpoints on the Poincaré ball. |
Source code in hyperbolix/nn_layers/poincare_batchnorm.py
hyperbolix.nn_layers.frechet_variance ¶
frechet_variance(
x_NC: Float[Array, "N C"],
mean_C: Float[Array, C],
manifold: Poincare,
c: float,
) -> Float[Array, ""]
Compute Fréchet variance: mean squared geodesic distance to mean.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x_NC
|
(Array, shape(N, C))
|
Points on the Poincaré ball. |
required |
mean_C
|
(Array, shape(C))
|
Mean point on the Poincaré ball. |
required |
manifold
|
Poincare
|
Poincaré manifold instance. |
required |
c
|
float
|
Curvature (positive). |
required |
Returns:
| Type | Description |
|---|---|
(Array, scalar)
|
Mean of squared geodesic distances. |
Source code in hyperbolix/nn_layers/poincare_batchnorm.py
Example¶
import jax, jax.numpy as jnp
from hyperbolix.nn_layers import lorentz_residual
from hyperbolix.manifolds import Hyperboloid
hyperboloid, c, d = Hyperboloid(), 1.0, 6
x = jax.random.normal(jax.random.PRNGKey(0), (8, d + 1))
y = jax.random.normal(jax.random.PRNGKey(1), (8, d + 1))
x = jax.vmap(hyperboloid.proj, in_axes=(0, None))(x, c)
y = jax.vmap(hyperboloid.proj, in_axes=(0, None))(y, c)
result = lorentz_residual(x, y, w_y=0.3, c=c) # weighted Lorentzian midpoint (skip connection)
print(result.shape) # (8, 7)