Source code for astronomix._stencil_operations._stencil_operations
"""
Convenience functions for operations that combine multiple elements
of an array based on some stencil, e.g. b_i <- a_{i + 1} + a_{i - 1}.
Allows for code "closer to the math".
"""
# general
from functools import partial
# typing
from typing import Tuple, Union
from beartype import beartype as typechecker
from jaxtyping import Array, Float, jaxtyped
# jax
import jax
import jax.numpy as jnp
# @jaxtyped(typechecker=typechecker)
[docs]
@partial(jax.jit, static_argnames=["shift", "axis"])
def custom_roll(input_array: jnp.ndarray, shift: int, axis: int) -> jnp.ndarray:
"""Periodic roll of ``input_array`` by ``shift`` along ``axis``.
Equivalent to ``jnp.roll`` but expressed via two static slices and a
concatenate, which keeps ``shift`` / ``axis`` compile-time constants so the
stencil helpers built on top of it fuse cleanly.
Args:
input_array: The array to roll.
shift: The (signed) number of positions to roll by.
axis: The axis along which to roll.
Returns:
The rolled array.
"""
i = (-shift) % input_array.shape[axis]
return jax.lax.concatenate(
[
jax.lax.slice_in_dim(input_array, i, input_array.shape[axis], axis=axis),
jax.lax.slice_in_dim(input_array, 0, i, axis=axis),
],
dimension=axis,
)
def _shift(input_array: jnp.ndarray, shift: int, axis: int) -> jnp.ndarray:
"""Shift ``input_array`` by ``shift`` along ``axis``.
A thin indirection over :func:`custom_roll`: the shift is currently periodic,
but routing every stencil through this single entry point leaves room to
support other boundary conditions later without touching call sites.
"""
return custom_roll(input_array, shift, axis)
# @jaxtyped(typechecker=typechecker)
@partial(jax.jit, static_argnames=["indices", "axis"])
def _stencil_add(
input_array: jnp.ndarray,
indices: Tuple[int, ...],
factors: Tuple[Union[float, Float[Array, ""]], ...],
axis: int,
) -> jnp.ndarray:
"""
Combines elements of an array additively
output_i <- sum_j factors_j * input_array_{i + indices_j}
Args:
input_array: The array to operate on.
indices: output_i <- sum_j factors_j * input_array_{i + indices_j}
factors: output_i <- sum_j factors_j * input_array_{i + indices_j}
axis: The axis along which to operate.
Returns:
output_i <- sum_j factors_j * input_array_{i + indices_j}
"""
output = sum(
factor * custom_roll(input_array, -index, axis=axis)
for factor, index in zip(factors, indices)
)
return output