HealPixDown — HEALPix resolution reduction#
Module healpix_analyse.down
Class HealPixDown
Inherits torch.nn.Module
Overview#
HealPixDown reduces the HEALPix resolution by a factor of 2:
Two strategies are available:
"smooth"— Gaussian-weighted average over a disc. Implemented as a sparse linear operatorM @ x(differentiable, usable in autograd)."maxpool"— Non-linear maximum over the 4 direct NESTED children of each coarse pixel. Faster, no gradient.
Both modes work on the full sphere or on a partial-sky subset defined by
cell_ids.
Input shapes [N] and [B, N] are both accepted; numpy arrays and torch
tensors are both accepted, and the return type mirrors the input type.
Constructor#
HealPixDown(
level,
mode = "smooth",
radius_deg = None,
sigma_deg = None,
weight_norm = "l1",
cell_ids = None,
device = None,
dtype = torch.float32,
)
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
— |
Input Grid4Earth/HEALPix level, integer ≥ 1. Internally, |
|
|
|
Downsampling strategy: |
|
|
auto |
Angular radius of the Gaussian disc (degrees). Default ≈ 3 × pixel size. Used only with |
|
|
auto |
Gaussian sigma (degrees). Default = |
|
|
|
Per-output-pixel weight normalisation in |
|
array-like or |
|
Input pixel indices (NESTED ordering) for partial-sky operation. |
|
device or |
auto |
Torch device. Defaults to CUDA if available, else CPU. |
|
|
|
Dtype for the sparse matrix values. |
Forward#
y, cell_ids_out = down(x)
Argument |
Type |
Shape |
Description |
|---|---|---|---|
|
numpy or torch |
|
Input map(s) at fine resolution |
Return |
Type |
Shape |
Description |
|---|---|---|---|
|
same as |
|
Downsampled map(s) at coarse resolution |
|
|
|
NESTED pixel indices of the output pixels at |
Modes#
"smooth" — Gaussian-weighted average#
The output for coarse pixel p_out is:
where the weights are:
and γ is the haversine angular distance between the centers of the
fine and coarse pixels.
The sparse matrix M is built once at construction and stored as a
buffer — forward only applies it.
weight_norm options:
Value |
Effect |
|---|---|
|
Sum of weights = 1. A constant input map returns a constant output. Recommended for standard downsampling. |
|
Sum of squares = 1. Preserves local signal energy rather than amplitude. |
|
Raw Gaussian weights (sum < 1 near boundaries). |
"maxpool" — Maximum over NESTED children#
In NESTED ordering, the 4 children of coarse pixel p are always
4p, 4p+1, 4p+2, 4p+3. The output is:
This is non-differentiable and does not support partial sky smoothly (missing children are replaced by the first available child).
Partial-sky operation#
When cell_ids is provided, the operator works on a subset of the
sphere. The coarse output cell ids are automatically derived as the
unique parents of the input pixels in NESTED ordering:
cell_ids_out = unique(cell_ids_in // 4)
Some coarse pixels at the patch boundary may have fewer than 4 children
available. In "smooth" mode, the Gaussian weights of absent fine
pixels are set to zero and the remaining weights are renormalized.
Examples#
Full sphere#
import numpy as np
from healpix_analyse.down import HealPixDown
level = 6
nside = 2**level
down = HealPixDown(level=level) # "smooth", l1
# Single map
x = np.random.randn(12 * nside**2) # [N]
y, ids_out = down(x)
print(y.shape) # (12288,) = 12 * 32²
# Batch
x_batch = np.random.randn(8, 12 * nside**2) # [B, N]
y_batch, _ = down(x_batch)
print(y_batch.shape) # (8, 12288)
Partial sky#
import numpy as np
from healpix_geo import nested
from healpix_analyse.down import HealPixDown
level = 6 # nside = 2**6
# the NESTED cells within 20 degrees of (lon, lat) = (0, 0)
patch = nested.cone_coverage((0.0, 0.0), 20.0, level)
patch = np.asarray(getattr(patch, "data", patch), dtype=np.int64).reshape(-1)
down = HealPixDown(level=level, cell_ids=patch)
x_patch = np.random.randn(len(patch))
y_patch, coarse_ids = down(x_patch)
print(f"Fine pixels: {len(patch)}")
print(f"Coarse pixels: {len(coarse_ids)}") # ≈ len(patch) / 4
Torch tensor, GPU#
import torch
from healpix_analyse.down import HealPixDown
level = 7
nside = 2**level
down = HealPixDown(level=level, device="cuda", dtype=torch.float32)
x = torch.randn(4, 12 * nside**2, device="cuda") # [B, N]
y, ids_out = down(x)
print(y.shape, y.device) # (4, 49152) cuda:0
Maxpool mode#
from healpix_analyse.down import HealPixDown
import numpy as np
down_max = HealPixDown(level=6, mode="maxpool")
x = np.random.randn(12 * 64**2)
y, _ = down_max(x)
Custom Gaussian scale#
# Wider disc, sharper Gaussian — more blurring
down_wide = HealPixDown(
level=6,
mode="smooth",
radius_deg=2.0,
sigma_deg=0.5,
weight_norm="l1",
)
Use in a U-Net encoder#
from healpix_analyse.convol import HealPixConv
from healpix_analyse.down import HealPixDown
import torch
level = 6
nside = 2**level
conv1 = HealPixConv(level, in_channels=1, out_channels=32, use_norm=True)
down1 = HealPixDown(level, mode="smooth")
conv2 = HealPixConv(level - 1, in_channels=32, out_channels=64, use_norm=True)
down2 = HealPixDown(level - 1, mode="smooth")
x = torch.randn(4, 1, 12 * nside**2) # [B, 1, N]
f1 = conv1(x) # [B, 32, N]
f1_down, _ = down1(f1) # [B, 32, N/4]
f2 = conv2(f1_down) # [B, 64, N/4]
f2_down, _ = down2(f2) # [B, 64, N/16]
Properties#
Attribute |
Description |
|---|---|
|
Fine input Grid4Earth/HEALPix level. |
|
Coarse output level ( |
|
Fine input resolution. |
|
Coarse output resolution ( |
|
Number of input pixels (full sphere or |
|
Number of output pixels. |
|
|
|
|
|
Output pixel indices (coarse NESTED ids). |
Pairing with HealPixUp#
HealPixDown(mode="smooth") and HealPixUp are designed as a matched
pair. The upsampling operator is the adjoint (transpose) of the
smooth downsampling matrix, with the same weight_norm, radius_deg,
and sigma_deg. Use the same values in both constructors:
from healpix_analyse.down import HealPixDown
from healpix_analyse.up import HealPixUp
level = 6
nside = 2**level
down = HealPixDown(level=level, weight_norm="l1")
up = HealPixUp(level=level - 1, weight_norm="l1", up_norm="col_l1")
x = torch.randn(4, 12 * nside**2)
y, _ = down(x) # [4, N/4]
x_up, _ = up(y) # [4, N] ← approximate round-trip