Convolutional Layers¶
2D hyperbolic convolutions and pooling for the Hyperboloid, Poincaré, and Proper Velocity models. For which conv to pick see the NN Layers guide.
Dimensional growth (Hyperboloid HCat convolutions)
HypConv2DHyperboloid / HypConv2DHyperboloidFHNN / FGGConv2D increase
dimensionality via the HCat operation: input d+1 → output (d×N)+1 where
N = kernel_height × kernel_width. A 3×3 kernel turns a 3D input into 28D. Use
small kernels or add dimensionality reduction. Poincaré, PV, and the HRC-based
LorentzConv2D preserve dimension instead.
Hyperboloid¶
HypConv2DHyperboloid (the robust default) uses HCat patch extraction + Lorentz FC
(Bdeir et al. 2023). HypConv2DHyperboloidILNN is the intrinsic Lorentz conv (Shi
et al. 2026): LogCat (log-radius-preserving concatenation) + PLFC, with origin
padding. LorentzConv2D is the dimension-preserving HRC-based variant (faster, lower
accuracy — legacy/benchmarking).
hyperbolix.nn_layers.HypConv2DHyperboloid ¶
HypConv2DHyperboloid(
manifold_module: Hyperboloid,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int],
*,
rngs: Rngs,
stride: int | tuple[int, int] = 1,
padding: str = "SAME",
input_space: str = "manifold",
reset_params: str = "default",
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Hyperbolic 2D Convolutional Layer for Hyperboloid model.
This layer implements fully hyperbolic convolution as described in "Fully Hyperbolic Convolutional Neural Networks for Computer Vision".
Computation steps: 1) Extract receptive field (kernel_size x kernel_size) of hyperbolic points 2) Apply HCat (Lorentz direct concatenation) to combine receptive field points 3) Pass through hyperbolic linear layer (LFC)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifold_module
|
object
|
Class-based Hyperboloid manifold instance |
required |
in_channels
|
int
|
Number of input channels |
required |
out_channels
|
int
|
Number of output channels |
required |
kernel_size
|
int or tuple[int, int]
|
Size of the convolutional kernel |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization |
required |
stride
|
int or tuple[int, int]
|
Stride of the convolution (default: 1) |
1
|
padding
|
str
|
Padding mode, either 'SAME' or 'VALID' (default: 'SAME') |
'SAME'
|
input_space
|
str
|
Type of the input tensor, either 'tangent' or 'manifold' (default: 'manifold'). Note: This is a static configuration - changing it after initialization requires recompilation. |
'manifold'
|
reset_params
|
str
|
Kernel initialization scheme (default: |
'default'
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32).
Compute precision of manifold operations is set by |
float32
|
Notes
JIT Compatibility: This layer is designed to work with nnx.jit. Configuration parameters (padding, input_space) are treated as static and will be baked into the compiled function.
References
Ahmad Bdeir, Kristian Schwethelm, and Niels Landwehr. "Fully hyperbolic convolutional neural networks for computer vision." arXiv preprint arXiv:2303.15919 (2023).
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
__call__ ¶
__call__(
x: Float[Array, "batch height width in_channels"],
c: float = 1.0,
) -> Float[
Array, "batch out_height out_width out_channels"
]
Forward pass through the hyperbolic convolutional layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, height, width, in_channels)
|
Input feature map where each pixel is a point on the Hyperboloid manifold |
required |
c
|
float
|
Manifold curvature (default: 1.0) |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
out |
Array of shape (batch, out_height, out_width, out_channels)
|
Output feature map on the Hyperboloid manifold |
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
hyperbolix.nn_layers.HypConv2DHyperboloidFHNN ¶
HypConv2DHyperboloidFHNN(
manifold_module: Hyperboloid,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int],
*,
rngs: Rngs,
stride: int | tuple[int, int] = 1,
padding: str = "SAME",
input_space: str = "manifold",
init_scale: float = 2.3,
eps: float = 1e-05,
activation: Callable[[Array], Array] | None = None,
dropout_rate: float | None = None,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Fully Hyperbolic Neural Networks 2D convolutional layer (Chen et al. 2021).
Uses HCat (Lorentz direct concatenation) to combine receptive field points, then applies the FHNN linear transform (time-primary sigmoid parameterization) for channel mixing.
Computation steps: 1) Map input to manifold via expmap_0 if input_space="tangent" 2) Extract receptive field (kernel_size x kernel_size) of hyperbolic points 3) Apply HCat (Lorentz direct concatenation) to combine receptive field points 4) Pass through FHNN linear (sigmoid time + spatial rescaling)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifold_module
|
Hyperboloid
|
Class-based Hyperboloid manifold instance |
required |
in_channels
|
int
|
Number of input channels (ambient dimension, including time component) |
required |
out_channels
|
int
|
Number of output channels (ambient dimension, including time component) |
required |
kernel_size
|
int or tuple[int, int]
|
Size of the convolutional kernel |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization |
required |
stride
|
int or tuple[int, int]
|
Stride of the convolution (default: 1) |
1
|
padding
|
str
|
Padding mode, either 'SAME' or 'VALID' (default: 'SAME') |
'SAME'
|
input_space
|
str
|
Type of the input tensor, either 'tangent' or 'manifold' (default: 'manifold'). Note: This is a static configuration - changing it after initialization requires recompilation. |
'manifold'
|
init_scale
|
float
|
Initial value for the learnable sigmoid scale (default: 2.3) |
2.3
|
eps
|
float
|
Numerical stability epsilon (default: 1e-5) |
1e-05
|
activation
|
callable or None
|
Activation function to apply before the linear transformation (default: None). |
None
|
dropout_rate
|
float or None
|
Dropout rate applied before the linear transformation (default: None). |
None
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32).
Compute precision of manifold operations is set by |
float32
|
Notes
JIT Compatibility: This layer is designed to work with nnx.jit. Configuration parameters (padding, input_space, activation) are treated as static and baked into the compiled function.
See Also
HypConv2DHyperboloid : Equivalent convolution using FHCNN linear instead of FHNN. HypLinearHyperboloidFHNN : The underlying FHNN linear layer.
References
Weize Chen, et al. "Fully hyperbolic neural networks." arXiv preprint arXiv:2105.14686 (2021).
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
__call__ ¶
__call__(
x: Float[Array, "batch height width in_channels"],
c: float = 1.0,
deterministic: bool = True,
) -> Float[
Array, "batch out_height out_width out_channels"
]
Forward pass through the FHNN hyperbolic convolutional layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, height, width, in_channels)
|
Input feature map where each pixel is a point on the Hyperboloid manifold |
required |
c
|
float
|
Manifold curvature (default: 1.0) |
1.0
|
deterministic
|
bool
|
If True, dropout is disabled (default: True). |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
out |
Array of shape (batch, out_height, out_width, out_channels)
|
Output feature map on the Hyperboloid manifold |
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
hyperbolix.nn_layers.HypConv2DHyperboloidILNN ¶
HypConv2DHyperboloidILNN(
manifold_module: Hyperboloid,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int],
*,
rngs: Rngs,
stride: int | tuple[int, int] = 1,
padding: str = "SAME",
pad_mode: str = "origin",
input_space: str = "manifold",
clamping_factor: float = 1.0,
smoothing_factor: float = 50.0,
v_max: float = 10.0,
use_gyro_bias: bool = False,
kernel_init_std: float = 0.02,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Intrinsic Lorentz Neural Network (ILNN) 2D convolutional layer (Hyperboloid model).
Implements the Lorentz convolution of Shi et al. 2026, Eq. (11):
y = PLFC(LogCat({x_patch})). Receptive-field points are combined with the
log-radius-preserving concatenation (LogCat) and mixed across channels by the
PLFC linear transform (MLR scores -> sinh diffeomorphism -> time
reconstruction). This extends the HNN++ linearized-kernel convolution
(Shimizu et al. 2020) by replacing the Euclidean FC and naive concatenation
with their intrinsic Lorentz counterparts. Formerly named
HypConv2DHyperboloidPP.
Computation steps:
1) Extract receptive field (kernel_size x kernel_size) of hyperbolic points,
padding with the manifold origin if needed
2) Apply LogCat (log-radius-preserving concatenation) to combine receptive field points
3) Pass through PLFC linear (MLR scores -> guard -> sinh -> time reconstruction)
4) Optionally add an intrinsic gyro-bias y <- y (+) exp_0([0, b])
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifold_module
|
Hyperboloid
|
Class-based Hyperboloid manifold instance |
required |
in_channels
|
int
|
Number of input channels (ambient dimension, including time component) |
required |
out_channels
|
int
|
Number of output channels (ambient dimension, including time component) |
required |
kernel_size
|
int or tuple[int, int]
|
Size of the convolutional kernel |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization |
required |
stride
|
int or tuple[int, int]
|
Stride of the convolution (default: 1) |
1
|
padding
|
str
|
Padding mode, either 'SAME' or 'VALID' (default: 'SAME') |
'SAME'
|
pad_mode
|
str
|
How to fill padding pixels for 'SAME' padding: |
'origin'
|
input_space
|
str
|
Type of the input tensor, either 'tangent' or 'manifold' (default: 'manifold'). Note: This is a static configuration - changing it after initialization requires recompilation. |
'manifold'
|
clamping_factor
|
float
|
Clamping factor for the multinomial linear regression output (default: 1.0) |
1.0
|
smoothing_factor
|
float
|
Smoothing factor for the multinomial linear regression output (default: 50.0) |
50.0
|
v_max
|
float
|
Output-side guard: the sinh argument |
10.0
|
use_gyro_bias
|
bool
|
If True, add a learnable intrinsic bias via Lorentz gyroaddition,
|
False
|
kernel_init_std
|
float
|
Standard deviation of the normal kernel init (default: 0.02, the Shi et al.
2026 PLFC reference value; the reference conv defers to the PLFC init).
Use 1.0 to recover the previous HNN++-style init (Shimizu et al. 2020);
note that large kernels push the pre-guard scores into the Low end: in BN-free residual stacks (no GyroBN between blocks), every
block becomes a strict contraction below |
0.02
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32).
Compute precision of manifold operations is set by |
float32
|
Notes
JIT Compatibility: This layer is designed to work with nnx.jit. Configuration parameters (padding, pad_mode, input_space, clamping_factor, smoothing_factor, v_max) are treated as static and baked into the compiled function.
See Also
HypLinearHyperboloidPLFC : The underlying PLFC linear layer. hyperbolix.manifolds.hyperboloid.Hyperboloid.log_radius_concat : The LogCat operation.
References
Xianglong Shi, Ziheng Chen, Yunhan Jiang, and Nicu Sebe. "Intrinsic Lorentz Neural Network." ICLR 2026, arXiv:2602.23981 (Sec. 4.3: Lorentz convolution, log-radius concatenation). Shimizu Ryohei, Yusuke Mukuta, and Tatsuya Harada. "Hyperbolic neural networks++." arXiv preprint arXiv:2006.08210 (2020).
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
__call__ ¶
__call__(
x: Float[Array, "batch height width in_channels"],
c: float = 1.0,
) -> Float[
Array, "batch out_height out_width out_channels"
]
Forward pass through the HNN++ hyperboloid convolutional layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, height, width, in_channels)
|
Input feature map where each pixel is a point on the Hyperboloid manifold |
required |
c
|
float
|
Manifold curvature (default: 1.0) |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
out |
Array of shape (batch, out_height, out_width, out_channels)
|
Output feature map on the Hyperboloid manifold |
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
hyperbolix.nn_layers.FGGConv2D ¶
FGGConv2D(
manifold_module: Hyperboloid,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int],
*,
rngs: Rngs,
stride: int | tuple[int, int] = 1,
padding: str = "SAME",
pad_mode: str = "origin",
activation: Callable | None = None,
reset_params: str = "fan_out",
use_weight_norm: bool = False,
init_bias: float = 0.0,
gain: float = 1.0,
eps: float = 1e-07,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Fast and Geometrically Grounded Lorentz 2D convolutional layer.
Uses HCat (Lorentz direct concatenation) to combine receptive field points, then applies FGGLinear for the channel mixing. This matches the reference implementation pattern from Klis et al. 2026.
Computation steps: 1) Extract receptive field patches, pad with manifold origin if needed 2) Apply HCat (Lorentz direct concatenation) to combine patch points 3) Pass through FGGLinear for channel transformation
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifold_module
|
Hyperboloid
|
Class-based Hyperboloid manifold instance. |
required |
in_channels
|
int
|
Input ambient channels (D_in + 1), including time component. |
required |
out_channels
|
int
|
Output ambient channels (D_out + 1), including time component. |
required |
kernel_size
|
int or tuple[int, int]
|
Size of the convolutional kernel. |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization. |
required |
stride
|
int or tuple[int, int]
|
Stride of the convolution (default: 1). |
1
|
padding
|
str
|
Padding mode: |
'SAME'
|
pad_mode
|
str
|
How to fill padding pixels: |
'origin'
|
activation
|
Callable or None
|
Euclidean activation for the FGGLinear (default: None). |
None
|
reset_params
|
str
|
Weight init for FGGLinear: |
'fan_out'
|
use_weight_norm
|
bool
|
Weight normalization in FGGLinear (default: False). Note |
False
|
init_bias
|
float
|
Initial bias for FGGLinear (default: 0.0). A zero bias removes the
|
0.0
|
gain
|
float
|
Multiplier on the random init (default: 1.0). With |
1.0
|
eps
|
float
|
Numerical stability floor (default: 1e-7). |
1e-07
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32).
Compute precision of manifold operations is set by |
float32
|
References
Klis et al. "Fast and Geometrically Grounded Lorentz Neural Networks" (2026).
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
__call__ ¶
__call__(
x: Float[Array, "batch height width in_channels"],
c: float = 1.0,
) -> Float[
Array, "batch out_height out_width out_channels"
]
Forward pass through the FGG convolutional layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
(Array, shape(B, H, W, in_channels))
|
Input feature map on the hyperboloid. |
required |
c
|
float
|
Curvature parameter (default: 1.0). |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
out |
Array, shape (B, H', W', out_channels)
|
Output feature map on the hyperboloid. |
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
hyperbolix.nn_layers.LorentzConv2D ¶
LorentzConv2D(
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int],
*,
rngs: Rngs,
stride: int | tuple[int, int] = 1,
padding: str = "SAME",
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Lorentz 2D Convolutional Layer using the Hyperbolic Layer (HL) approach.
This layer applies convolution to the space-like components of Lorentzian vectors and reconstructs the time-like component to maintain the manifold constraint. This is equivalent to an HRC (Hyperbolic Regularization Component) wrapper around a standard Conv2D.
Computation steps: 1) Extract space-like components x_s from input x = [x_t, x_s]^T 2) Apply Euclidean convolution: y_s = Conv2D(x_s) 3) Reconstruct time component: y_t = sqrt(||y_s||^2 + 1/c) 4) Return y = [y_t, y_s]^T
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_channels
|
int
|
Number of input channels (ambient dimension, including time component) |
required |
out_channels
|
int
|
Number of output channels (ambient dimension, including time component) |
required |
kernel_size
|
int or tuple[int, int]
|
Size of the convolutional kernel |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization |
required |
stride
|
int or tuple[int, int]
|
Stride of the convolution (default: 1) |
1
|
padding
|
str or int or tuple
|
Padding mode: 'SAME', 'VALID', or explicit padding (default: 'SAME') |
'SAME'
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32).
Compute precision of manifold operations is set by the manifold's |
float32
|
Notes
This implementation follows the Hyperbolic Layer (HL) approach from "Fully Hyperbolic Convolutional Neural Networks for Computer Vision".
The layer operates only on space-like components, making it more computationally efficient than the HCat-based approach (HypConv2DHyperboloid), though it doesn't perform true hyperbolic convolution. Instead, it applies Euclidean operations to spatial components and reconstructs the time component.
See Also
hypformer.hrc : Core HRC function this layer is based on HypConv2DHyperboloid : Full hyperbolic convolution using HCat concatenation
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.
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
__call__ ¶
__call__(
x: Float[Array, "batch height width in_channels"],
c: float = 1.0,
) -> Float[
Array, "batch out_height out_width out_channels"
]
Forward pass through the Lorentz convolutional layer.
This layer is a specific instance of the Hyperbolic Regularization Component (HRC) where the regularization function f_r is a 2D convolution. The HRC pattern: 1. Extracts space components 2. Applies Euclidean convolution 3. Reconstructs time component using Lorentz constraint
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, height, width, in_channels)
|
Input feature map where x[..., 0] is time component and x[..., 1:] are space components on the Lorentz manifold |
required |
c
|
float
|
Manifold curvature parameter (default: 1.0) |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
out |
Array of shape (batch, out_height, out_width, out_channels)
|
Output feature map on the Lorentz manifold |
Notes
This implementation uses the HRC function from hypformer.py, demonstrating that LorentzConv2D (from LResNet) and HRC (from Hypformer) are mathematically equivalent approaches to adapting Euclidean operations for hyperbolic geometry.
Source code in hyperbolix/nn_layers/hyperboloid_conv.py
Poincaré¶
HypConv2DPoincare extracts patches, applies beta-concatenation (HNN++, Shimizu
et al. 2020) over the receptive field, then a HypLinearPoincarePP layer. Dimension
is preserved (K² × C_in → C_out); I/O is in tangent space by default.
hyperbolix.nn_layers.HypConv2DPoincare ¶
HypConv2DPoincare(
manifold_module: Poincare,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int],
*,
rngs: Rngs,
stride: int | tuple[int, int] = 1,
padding: str = "SAME",
input_space: str = "tangent",
id_init: bool = True,
clamping_factor: float = 1.0,
smoothing_factor: float = 50.0,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Hyperbolic 2D Convolutional Layer for Poincaré ball model.
This layer implements hyperbolic convolution following the Poincaré ResNet (van Spengler et al. 2023) approach: beta-scaling in tangent space, patch extraction, expmap to manifold, HNN++ fully-connected, logmap back to tangent space.
The layer operates in tangent space internally and returns tangent-space output, matching the reference implementation. This design avoids the numerically unstable logmap_0 round-trips that cause NaN gradients when points approach the Poincaré ball boundary.
Computation steps: 1) Map to tangent space if input is on manifold 2) Scale tangent vectors by beta function ratio (beta-concatenation scaling) 3) Extract patches via im2col (zero-padding in tangent space) 4) Map concatenated patch vectors to manifold via expmap_0 5) Apply HNN++ fully-connected layer 6) Map back to tangent space via logmap_0
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifold_module
|
Poincare
|
Class-based Poincaré manifold instance |
required |
in_channels
|
int
|
Number of input channels (Poincaré ball dimension per pixel) |
required |
out_channels
|
int
|
Number of output channels (Poincaré ball dimension per pixel) |
required |
kernel_size
|
int or tuple[int, int]
|
Size of the convolutional kernel |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization |
required |
stride
|
int or tuple[int, int]
|
Stride of the convolution (default: 1) |
1
|
padding
|
str
|
Padding mode, either 'SAME' or 'VALID' (default: 'SAME') |
'SAME'
|
input_space
|
str
|
Type of the input tensor, either 'tangent' or 'manifold' (default: 'tangent'). Note: This is a static configuration - changing it after initialization requires recompilation. |
'tangent'
|
id_init
|
bool
|
If True, use identity initialization (1/2 * I) for the linear sublayer weights, matching the reference Poincaré ResNet implementation (default: True). The 1/2 factor compensates for the factor of 2 inside the HNN++ distance formula. |
True
|
clamping_factor
|
float
|
Clamping factor for the HNN++ linear layer output (default: 1.0) |
1.0
|
smoothing_factor
|
float
|
Smoothing factor for the HNN++ linear layer output (default: 50.0) |
50.0
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32).
Compute precision of manifold operations is set by |
float32
|
Notes
Output Space: This layer always returns tangent-space output (matching the reference). Between conv layers, use standard activations (e.g., jax.nn.relu) directly on the tangent-space features. Use expmap_0 to map back to the manifold only when needed (e.g., before the classification head).
JIT Compatibility: This layer is designed to work with nnx.jit. Configuration parameters (padding, input_space) are treated as static and will be baked into the compiled function.
Dimension math: - beta-scaling + patch extraction: (H, W, C_in) → (oh, ow, K^2 * C_in) - HNN++ linear: in_dim = K^2 * C_in, out_dim = C_out
References
Shimizu et al. "Hyperbolic neural networks++." arXiv:2006.08210 (2020). van Spengler et al. "Poincaré ResNet." ICML 2023.
Source code in hyperbolix/nn_layers/poincare_conv.py
__call__ ¶
__call__(
x: Float[Array, "batch height width in_channels"],
c: float = 1.0,
) -> Float[
Array, "batch out_height out_width out_channels"
]
Forward pass through the Poincaré convolutional layer.
Follows the reference computation flow: tangent-space beta-scaling, patch extraction, expmap_0, HNN++ FC, logmap_0.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, height, width, in_channels)
|
Input feature map. Can be tangent-space or manifold points depending on input_space setting. |
required |
c
|
float
|
Manifold curvature (default: 1.0) |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
out |
Array of shape (batch, out_height, out_width, out_channels)
|
Output feature map in tangent space at the origin. Use standard activations (e.g., jax.nn.relu) between layers. |
Source code in hyperbolix/nn_layers/poincare_conv.py
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | |
Proper Velocity¶
HypConv2DPV (Chen et al. 2026, Sec 5.3). Because PV geometry is unconstrained
\(\mathbb{R}^n\), patch concatenation coincides with Euclidean concatenation — no
beta-scaling step and no dimension growth. Outputs live on the PV manifold, so
Euclidean activations apply directly between conv layers.
hyperbolix.nn_layers.HypConv2DPV ¶
HypConv2DPV(
manifold_module: ProperVelocity,
in_channels: int,
out_channels: int,
kernel_size: int | tuple[int, int],
*,
rngs: Rngs,
stride: int | tuple[int, int] = 1,
padding: str = "SAME",
input_space: str = "manifold",
inner_activation: Callable[[Array], Array]
| None = None,
clamping_factor: float = 1.0,
smoothing_factor: float = 50.0,
kernel_init_std: float | None = None,
param_dtype: DTypeLike = jnp.float32,
)
Bases: Module
Hyperbolic 2D Convolutional Layer for the Proper Velocity model.
Implements Chen et al. 2026, Sec 5.3. Because PV's geometry is unconstrained
(ℝⁿ), patch concatenation coincides with Euclidean concatenation — no
beta-scaling step is required (unlike HypConv2DPoincare). The layer
returns points on the PV manifold; between conv layers, activations can be
applied directly in PV space (paper Sec 5.3 "Activation").
Computation steps:
1) Optionally lift tangent-space input to the PV manifold via expmap_0.
2) Extract patches with zero-padding via jax.lax.conv_general_dilated_patches.
3) Flatten batch x spatial dimensions.
4) Apply the PV FC forward (_pv_fc_forward, shared with HypLinearPV).
5) Reshape back to spatial output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifold_module
|
ProperVelocity
|
Class-based Proper Velocity manifold instance. |
required |
in_channels
|
int
|
Number of input channels (PV dimension per pixel). |
required |
out_channels
|
int
|
Number of output channels (PV dimension per pixel). |
required |
kernel_size
|
int or tuple[int, int]
|
Size of the convolutional kernel. |
required |
rngs
|
Rngs
|
Random number generators for parameter initialization. |
required |
stride
|
int or tuple[int, int]
|
Stride of the convolution (default: 1). |
1
|
padding
|
str
|
Padding mode, either 'SAME' or 'VALID' (default: 'SAME'). |
'SAME'
|
input_space
|
str
|
Type of the input tensor, either 'tangent' or 'manifold' (default: 'manifold'). Note: This is a static configuration — changing it after initialization requires recompilation. |
'manifold'
|
inner_activation
|
Callable[[Array], Array] | None
|
Optional activation applied inside the outer sinh (paper Eq. 23). Default None. |
None
|
clamping_factor
|
float
|
Clamping factor for the PV MLR output (default: 1.0). |
1.0
|
smoothing_factor
|
float
|
Smoothing factor for the PV MLR output (default: 50.0). |
50.0
|
kernel_init_std
|
float | None
|
Standard deviation for the Gaussian kernel init. If |
None
|
param_dtype
|
DTypeLike
|
Storage dtype of the trainable parameters (default: jnp.float32).
Compute precision of manifold operations is set by |
float32
|
Notes
Output Space:
Unlike HypConv2DPoincare (which returns tangent space), this layer
returns points on the PV manifold. Between conv layers, apply activations
directly on the PV features — no expmap_0/logmap_0 round-trips needed.
JIT Compatibility: Configuration parameters (padding, input_space, inner_activation, clamping_factor, smoothing_factor) are treated as static and are baked into the compiled function.
Dimension math: - patch extraction: (H, W, C_in) → (oh, ow, K²·C_in) - PV FC: in_dim = K²·C_in, out_dim = C_out
References
Chen et al. "Proper Velocity Neural Networks." ICLR 2026.
Source code in hyperbolix/nn_layers/pv_conv.py
__call__ ¶
__call__(
x: Float[Array, "batch height width in_channels"],
c: float = 1.0,
) -> Float[
Array, "batch out_height out_width out_channels"
]
Forward pass through the PV convolutional layer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Array of shape (batch, height, width, in_channels)
|
Input feature map. PV manifold points or tangent-space vectors at the
origin, depending on |
required |
c
|
float
|
Manifold curvature (default: 1.0). |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
out |
Array of shape (batch, out_height, out_width, out_channels)
|
Output feature map on the PV manifold. |
Source code in hyperbolix/nn_layers/pv_conv.py
Pooling¶
hyperbolix.nn_layers.hyp_avg_pool2d ¶
hyp_avg_pool2d(
x: Float[Array, "... H W dim_plus_1"],
c: float,
eps: float = 1e-07,
) -> Float[Array, "... dim_plus_1"]
Global average pooling over 2D spatial dimensions on the hyperboloid.
Averages the spatial components over the height and width dimensions,
then reconstructs the time component via the hyperboloid constraint.
This is the HRC pattern with f_r = mean_over_spatial:
.. math::
\text{space} = \text{mean}_{H,W}(x[..., 1:])
x_0 = \sqrt{\|\text{space}\|^2 + 1/c}
\text{output} = [x_0, \text{space}]
The function expects the NHWC layout used throughout hyperbolix:
(..., H, W, ambient_dim) where ambient_dim = spatial_dim + 1.
This operation is hyperboloid-only because it relies on the time/spatial
decomposition x[..., 0] (time) and x[..., 1:] (spatial). Poincaré
ball points do not have a time component. To pool Poincaré features, first
convert to hyperboloid via
:func:~hyperbolix.manifolds.isometry_mappings.poincare_to_hyperboloid,
pool, then convert back.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
(Array, shape(..., H, W, dim + 1))
|
Hyperboloid feature map with curvature |
required |
c
|
float
|
Curvature parameter (positive, c > 0). |
required |
eps
|
float
|
Numerical stability floor for the time reconstruction (default: 1e-7). |
1e-07
|
Returns:
| Type | Description |
|---|---|
(Array, shape(..., dim + 1))
|
Pooled hyperboloid points with curvature |
See Also
spatial_to_hyperboloid : Low-level curvature scaling + time reconstruction. hrc : General HRC wrapper for arbitrary Euclidean functions. lorentz_midpoint : Weighted Lorentzian midpoint (geometrically exact aggregation).
Examples:
>>> import jax
>>> import jax.numpy as jnp
>>> from hyperbolix.manifolds import Hyperboloid
>>> from hyperbolix.nn_layers.hyperboloid_core import hyp_avg_pool2d
>>>
>>> hyperboloid = Hyperboloid(dtype=jnp.float32)
>>> key = jax.random.PRNGKey(0)
>>> v = jax.random.normal(key, (4, 7, 7, 64), dtype=jnp.float32) * 0.1
>>> x = jax.vmap(jax.vmap(jax.vmap(
... hyperboloid.expmap_0, in_axes=(0, None)
... ), in_axes=(0, None)), in_axes=(0, None))(v, 1.0)
>>> x.shape
(4, 7, 7, 65)
>>> y = hyp_avg_pool2d(x, c=1.0)
>>> y.shape
(4, 65)
Source code in hyperbolix/nn_layers/hyperboloid_core.py
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | |
Example¶
import jax, jax.numpy as jnp
from flax import nnx
from hyperbolix.nn_layers import HypConv2DHyperboloid
from hyperbolix.manifolds import Hyperboloid
conv = HypConv2DHyperboloid(
manifold_module=Hyperboloid(), in_channels=16, out_channels=32,
kernel_size=(3, 3), stride=(1, 1), rngs=nnx.Rngs(0),
)
x = jax.random.normal(jax.random.PRNGKey(1), (8, 28, 28, 16))
x_amb = jnp.concatenate([jnp.sqrt(jnp.sum(x**2, -1, keepdims=True) + 1.0), x], -1) # (8,28,28,17)
output = conv(x_amb, c=1.0)
print(output.shape) # (8, 28, 28, 32*9+1) — dimension grows via HCat