Skip to content

anypinn.lib.encodings

Built-in input encodings for spatial/periodic signals.

FourierEncoding

Bases: Module

Sinusoidal positional encoding for periodic or high-frequency signals.

For input \(\mathbf{x} \in \mathbb{R}^{n \times d}\) and num_frequencies \(K\), the encoding is:

\[ \gamma(\mathbf{x}) = [\mathbf{x},\, \sin(\mathbf{x}),\, \cos(\mathbf{x}),\, \sin(2\mathbf{x}),\, \cos(2\mathbf{x}),\, \ldots,\, \sin(K\mathbf{x}),\, \cos(K\mathbf{x})] \]

producing shape \((n,\, d\,(1 + 2K))\) when include_input=True, or \((n,\, 2dK)\) when include_input=False.

Parameters:

Name Type Description Default
num_frequencies int

Number of frequency bands \(K \geq 1\).

6
include_input bool

Prepend original coordinates to the encoded output.

True
Source code in src/anypinn/lib/encodings.py
class FourierEncoding(nn.Module):
    """Sinusoidal positional encoding for periodic or high-frequency signals.

    For input $\\mathbf{x} \\in \\mathbb{R}^{n \\times d}$ and
    `num_frequencies` $K$, the encoding is:

    $$
    \\gamma(\\mathbf{x}) = [\\mathbf{x},\\,
        \\sin(\\mathbf{x}),\\, \\cos(\\mathbf{x}),\\,
        \\sin(2\\mathbf{x}),\\, \\cos(2\\mathbf{x}),\\,
        \\ldots,\\,
        \\sin(K\\mathbf{x}),\\, \\cos(K\\mathbf{x})]
    $$

    producing shape $(n,\\, d\\,(1 + 2K))$ when `include_input=True`,
    or $(n,\\, 2dK)$ when `include_input=False`.

    Args:
        num_frequencies: Number of frequency bands $K \\geq 1$.
        include_input:   Prepend original coordinates to the encoded output.
    """

    def __init__(self, num_frequencies: int = 6, include_input: bool = True) -> None:
        if num_frequencies < 1:
            raise ValueError(f"num_frequencies must be >= 1, got {num_frequencies}.")
        super().__init__()
        self.num_frequencies = num_frequencies
        self.include_input = include_input

    def out_dim(self, in_dim: int) -> int:
        """Compute output dimension given input dimension."""
        factor = 1 + 2 * self.num_frequencies if self.include_input else 2 * self.num_frequencies
        return in_dim * factor

    def forward(self, x: Tensor) -> Tensor:
        parts = [x] if self.include_input else []
        for k in range(1, self.num_frequencies + 1):
            parts.append(torch.sin(k * x))
            parts.append(torch.cos(k * x))
        return torch.cat(parts, dim=-1)

include_input = include_input instance-attribute

num_frequencies = num_frequencies instance-attribute

__init__(num_frequencies: int = 6, include_input: bool = True) -> None

Source code in src/anypinn/lib/encodings.py
def __init__(self, num_frequencies: int = 6, include_input: bool = True) -> None:
    if num_frequencies < 1:
        raise ValueError(f"num_frequencies must be >= 1, got {num_frequencies}.")
    super().__init__()
    self.num_frequencies = num_frequencies
    self.include_input = include_input

forward(x: Tensor) -> Tensor

Source code in src/anypinn/lib/encodings.py
def forward(self, x: Tensor) -> Tensor:
    parts = [x] if self.include_input else []
    for k in range(1, self.num_frequencies + 1):
        parts.append(torch.sin(k * x))
        parts.append(torch.cos(k * x))
    return torch.cat(parts, dim=-1)

out_dim(in_dim: int) -> int

Compute output dimension given input dimension.

Source code in src/anypinn/lib/encodings.py
def out_dim(self, in_dim: int) -> int:
    """Compute output dimension given input dimension."""
    factor = 1 + 2 * self.num_frequencies if self.include_input else 2 * self.num_frequencies
    return in_dim * factor

RandomFourierFeatures

Bases: Module

Random Fourier Features (Rahimi & Recht, 2007) for RBF kernel approximation.

Draws a fixed random matrix \(\mathbf{B} \sim \mathcal{N}(0, \sigma^2)\) of shape \((d_{\text{in}},\, m)\) and maps \(\mathbf{x} \in \mathbb{R}^{n \times d_{\text{in}}}\) to:

\[ \phi(\mathbf{x}) = \frac{1}{\sqrt{m}} [\cos(\mathbf{x}\mathbf{B}),\; \sin(\mathbf{x}\mathbf{B})] \in \mathbb{R}^{n \times 2m} \]

\(\mathbf{B}\) is registered as a buffer and moves with the module across devices.

Parameters:

Name Type Description Default
in_dim int

Spatial dimension \(d_{\text{in}}\) of the input.

required
num_features int

Number of random features \(m\) (output dimension \(= 2m\)).

256
scale float

Standard deviation \(\sigma\) of the frequency distribution. Higher values capture higher-frequency variation. Default: 1.0.

1.0
seed int | None

Optional seed for reproducible frequency sampling.

None
Source code in src/anypinn/lib/encodings.py
class RandomFourierFeatures(nn.Module):
    """Random Fourier Features (Rahimi & Recht, 2007) for RBF kernel approximation.

    Draws a fixed random matrix $\\mathbf{B} \\sim \\mathcal{N}(0, \\sigma^2)$
    of shape $(d_{\\text{in}},\\, m)$ and maps
    $\\mathbf{x} \\in \\mathbb{R}^{n \\times d_{\\text{in}}}$ to:

    $$
    \\phi(\\mathbf{x}) = \\frac{1}{\\sqrt{m}}
        [\\cos(\\mathbf{x}\\mathbf{B}),\\; \\sin(\\mathbf{x}\\mathbf{B})]
        \\in \\mathbb{R}^{n \\times 2m}
    $$

    $\\mathbf{B}$ is registered as a buffer and moves with the module across devices.

    Args:
        in_dim:       Spatial dimension $d_{\\text{in}}$ of the input.
        num_features: Number of random features $m$
                      (output dimension $= 2m$).
        scale:        Standard deviation $\\sigma$ of the frequency distribution.
                      Higher values capture higher-frequency variation. Default: 1.0.
        seed:         Optional seed for reproducible frequency sampling.
    """

    def __init__(
        self,
        in_dim: int,
        num_features: int = 256,
        scale: float = 1.0,
        seed: int | None = None,
    ) -> None:
        if in_dim < 1:
            raise ValueError(f"in_dim must be >= 1, got {in_dim}.")
        if num_features < 1:
            raise ValueError(f"num_features must be >= 1, got {num_features}.")
        if scale <= 0.0:
            raise ValueError(f"scale must be > 0, got {scale}.")
        super().__init__()
        gen = torch.Generator()
        if seed is not None:
            gen.manual_seed(seed)
        B = torch.randn(in_dim, num_features, generator=gen) * scale
        self.register_buffer("B", B)
        self.num_features = num_features

    @property
    def out_dim(self) -> int:
        """Output dimension (always 2 * num_features)."""
        return 2 * self.num_features

    def forward(self, x: Tensor) -> Tensor:
        proj = x @ self.B  # type: ignore[operator]  # (n, num_features)
        return torch.cat([torch.cos(proj), torch.sin(proj)], dim=-1) / (self.num_features**0.5)

num_features = num_features instance-attribute

out_dim: int property

Output dimension (always 2 * num_features).

__init__(in_dim: int, num_features: int = 256, scale: float = 1.0, seed: int | None = None) -> None

Source code in src/anypinn/lib/encodings.py
def __init__(
    self,
    in_dim: int,
    num_features: int = 256,
    scale: float = 1.0,
    seed: int | None = None,
) -> None:
    if in_dim < 1:
        raise ValueError(f"in_dim must be >= 1, got {in_dim}.")
    if num_features < 1:
        raise ValueError(f"num_features must be >= 1, got {num_features}.")
    if scale <= 0.0:
        raise ValueError(f"scale must be > 0, got {scale}.")
    super().__init__()
    gen = torch.Generator()
    if seed is not None:
        gen.manual_seed(seed)
    B = torch.randn(in_dim, num_features, generator=gen) * scale
    self.register_buffer("B", B)
    self.num_features = num_features

forward(x: Tensor) -> Tensor

Source code in src/anypinn/lib/encodings.py
def forward(self, x: Tensor) -> Tensor:
    proj = x @ self.B  # type: ignore[operator]  # (n, num_features)
    return torch.cat([torch.cos(proj), torch.sin(proj)], dim=-1) / (self.num_features**0.5)