healpix_analyse.validation ========================== .. py:module:: healpix_analyse.validation .. autoapi-nested-parse:: A direct, non-pyramidal spherical convolution used only as a validation and calibration oracle for :mod:`healpix_analyse.kernel_pyramid` / :mod:`healpix_analyse.pyramid_conv`. This module is deliberately **independent** of :class:`~healpix_analyse.convol.HealPixConv`: it does not reuse its bilinear stencil-binding or per-pixel gauge-rotation machinery. Instead it locates true pixel centres with ``healpix_geo``, searches neighbours with a KD-tree on unit vectors, and evaluates a kernel against the *exact* great-circle angular distance. This way, comparing the pyramidal path against this module checks the pyramidal path itself (including ``HealPixConv``'s own approximations), rather than the pyramidal method validating itself against a copy of its own machinery. Scope and honest limitations ----------------------------- - Only **isotropic** kernel profiles (``kernel(rho_pix, phi) -> weight`` that does not depend on ``phi``) are supported here. Reproducing an independent oracle for the anisotropic, gauge-dependent case would require re-implementing ``HealPixConv``'s own rotation-matrix conventions, which would no longer be an independent check; anisotropic kernels are validated qualitatively instead (see the validation notes), not against this module. - This is a plain Python/NumPy implementation with an explicit neighbour loop, intended for correctness on the modest map sizes used in tests and calibration -- not a fast or GPU-capable production path. It intentionally trades speed for being simple enough to audit by eye. - Distances are true geodesic (great-circle) angles on the given ``ellipsoid`` (use ``"sphere"`` to match :mod:`healpix_analyse.kernel_pyramid`'s own default), converted to the same "pixel units" convention (``rho_pix = rho_rad / alpha_pix``) used throughout :mod:`healpix_analyse.kernel_pyramid`, so the *same* kernel callables can be passed to both without rescaling. Functions --------- .. autoapisummary:: healpix_analyse.validation.smooth_test_field healpix_analyse.validation.direct_spherical_convolution healpix_analyse.validation.direct_reference_operator_factory Module Contents --------------- .. py:function:: smooth_test_field(cell_ids: numpy.ndarray, level: int, ellipsoid: str = 'sphere') -> numpy.ndarray A fixed, spatially-smooth (low-degree, non-bandlimited-exactly-zero but slowly varying) synthetic map used as a validation test field. Not a substitute for white-noise/random tests -- see the module docstring: HealPixConv's stencil binds data by *interpolation*, which closely reproduces a continuous kernel's action on smooth content but measurably departs from a nearest-pixel quadrature reference on single-pixel-scale (white-noise) content. Both regimes are real and are reported separately rather than folded into one number. .. py:function:: direct_spherical_convolution(x: numpy.ndarray, cell_ids: numpy.ndarray, level: int, kernel: IsoKernelFn, *, weights: Optional[numpy.ndarray] = None, kernel_sz: int = 5, ellipsoid: str = 'sphere', eps: float = 1e-08, restore_mask: bool = True, normalize: bool = True) -> numpy.ndarray Direct (brute-force, non-pyramidal) spherical convolution. For every pixel ``i``, sums ``kernel(rho_pix, 0) * m_j * x_j`` over neighbours ``j`` within the kernel's compact support (matching ``compact_kernel_sz`` in pixel units). ``normalize=True`` (default) additionally divides by the summed weight ``sum(kernel * m)`` -- the normalized-convolution convention used by :class:`~healpix_analyse.pyramid_conv.HealPixPyramidConv` in its ``"normalized"`` mode, appropriate when comparing against a *masked* result. ``normalize=False`` returns the **raw** weighted sum with no division, matching :class:`~healpix_analyse.convol.HealPixConv`'s own literal tap-weighted-sum semantics (``HealPixConv`` never normalizes its kernel internally) -- use this when validating a single :class:`~healpix_analyse.kernel_pyramid.HealPixKernelPyramid` band directly, or when calibrating raw kernel taps, so both sides of the comparison mean the same operator. ``weights`` defaults to a 0/1 mask from ``isfinite(x)``. Returns a ``[len(cell_ids)]`` array. When ``normalize=True``, pixels with no supported neighbours (summed weight below ``eps``) are NaN if ``restore_mask`` else 0; ``normalize=False`` never produces NaN/fill values on its own (missing values are simply excluded via ``m``). .. py:function:: direct_reference_operator_factory(kernel: IsoKernelFn, *, kernel_sz: int = 5, ellipsoid: str = 'sphere', weights: Optional[numpy.ndarray] = None, normalize: bool = False) Build a ``reference_fn(cell_ids, level) -> operator`` usable with :meth:`healpix_analyse.kernel_pyramid.HealPixKernelPyramid.calibrate`, wrapping :func:`direct_spherical_convolution` with a fixed kernel. ``normalize=False`` (default) matches ``HealPixConv``'s raw, unnormalized tap-weighted-sum convention, which is what :meth:`~healpix_analyse.kernel_pyramid.HealPixKernelPyramid.calibrate` fits its raw kernel taps against; pass ``normalize=True`` only if the calibration target is itself meant to be a normalized operator.