Skip to content

Design review report #25

@kdw503

Description

@kdw503

Design Review: RegisterCore.jl
Date: 2026-04-28

Overview

RegisterCore provides NumDenom{T} — a packed (num, denom) pair that
behaves as a 2-vector under interpolation arithmetic without forming
the ratio prematurely — and MismatchArray, a type alias for
CenterIndexedArray{NumDenom}. The package also contains a shot-noise
image preprocessor (PreprocessSNF) and bandpass utilities.


Likely Design Issues

  1. ColonFun is exported but is purely internal.
    A zero-field singleton used inside paddedview/trimmedview for
    type-stable colon generation. No docstring, no external use case,
    name is not meaningful to a user. Looks like an accidental export.
    Fix: remove from the export list.

  2. PreprocessSNF is mutable struct but has no mutation API.
    Fields: bias::Float32, sigmalp::Vector{Float32}, sigmahp::Vector{Float32}.
    No mutation pattern is documented or used. The vector fields are
    already mutable via their elements regardless of struct mutability.
    The mutable keyword appears to be an oversight; struct would be correct.

  3. copyto! and paddedview use error(...) rather than typed exceptions.
    Every other size-mismatch guard in the package throws DimensionMismatch.
    These two use the untyped error(...), preventing callers from catching
    specific error types. Inconsistent with the package's own conventions.

  4. mismatcharrays is exported despite the comment "mostly used just for testing."
    Exporting a testing helper adds permanent API surface. Should be removed
    from export (callers can use RegisterCore.mismatcharrays if needed).


Design Questions

  1. Does the image-preprocessing subsystem belong in RegisterCore?
    highpass, PreprocessSNF, paddedview, trimmedview, preprocess, and
    sqrt_subtract_bias implement shot-noise standardization and bandpass
    filtering — transforming images before mismatch is computed. The rest
    of the package is about the algebraic structure of mismatch data.
    Was the preprocessing added here because no better home existed, or is
    it intentionally colocated with the mismatch types?

  2. Is MismatchArray as a type alias the intended permanent design?
    const MismatchArray{ND,N,A} = CenterIndexedArray{ND,N,A} is transparent
    to the type system — not a distinct type for dispatch. Methods annotated
    ::MismatchArray also match any CenterIndexedArray{NumDenom,...} created
    by other means. Was this chosen to keep coupling to CenterIndexedArrays
    explicit, or would a distinct type be preferable for domain clarity?

  3. Is the second method of indmin_mismatch still needed?
    indmin_mismatch(r::CenterIndexedArray{T<:Number,N}) finds the interior
    argmin of a pre-divided numeric array — essentially argmin restricted to
    interior elements. Was this added for a specific calling convention that
    has since been superseded?

  4. Should separate(::AbstractArray{<:MismatchArray}) be exported?
    The caller could write map(separate, mma) with nearly identical result.
    Was this exported for performance or just for convenience?

  5. Why does Base.round take the form round(::Type{NumDenom{T}}, p)?
    The public Julia signature for rounding is round(x::NumDenom, r::RoundingMode=...).
    The round(::Type{T}, x) form is an internal convention used by some
    interpolation backends. Was this defined to satisfy a specific
    Interpolations.jl or ImageFiltering.jl internal requirement?


Observations

  • NumDenom.show produces valid Julia constructor syntax (NumDenom(1.0,2.0)),
    so copy-paste reconstruction from the REPL works.

  • Unary negation (-nd) for NumDenom is not defined — only binary (-).
    A user writing (-nd) would get a MethodError, which is mildly surprising
    given that +, -, *, / are all present.

  • preprocess(pp, A) appears to be effectively public (called by the functor,
    referenced in the PreprocessSNF docstring). Should be annotated public.

  • The @require ImageMetadata extension in init predates Julia 1.9's
    package extension system (ext/). Routine maintenance candidate.

  • ratio(r::Real, thresh, fillval=NaN) = r is a well-considered composability
    convenience enabling uniform broadcasting over mixed arrays.


Overall

RegisterCore has a clear and principled algebraic core: NumDenom encodes
the discipline of not forming the mismatch ratio prematurely, enforced
consistently. The main structural tension is scope — a type-algebra library
bundled with a signal-processing preprocessor only loosely related at the
type level. The two highest-leverage changes would be: (1) remove ColonFun
from exports (clear accidental leak) and (2) decide whether PreprocessSNF/
highpass/paddedview/trimmedview belong here or in a sibling RegisterPreprocess
package, since that boundary question shapes several other findings.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions