Skip to content

Initialize model _parameters array at construction (fix flaky Gaussian1D pickling) - #20060

Open
Mohit-Ak wants to merge 2 commits into
astropy:mainfrom
Mohit-Ak:fix/gaussian1d-pickle-init-parameters
Open

Mohit-Ak wants to merge 2 commits into
astropy:mainfrom
Mohit-Ak:fix/gaussian1d-pickle-init-parameters

Conversation

@Mohit-Ak

Copy link
Copy Markdown
Contributor

What this fixes

Fixes #18598.

A freshly constructed Model allocated its _parameters array with np.empty()
in _initialize_slices() and only populated it lazily, on the first access of the
parameters property. Until that first access, _parameters held uninitialized
memory
.

That is harmless for the normal fitting workflow (which always touches
.parameters), but it makes any operation that reads the raw _parameters array
directly non-deterministic. The clearest victim is pickle-recovery testing:
astropy.tests.helper.check_pickling_recovery() compares the original and
unpickled objects attribute-by-attribute with strict equality. When the
uninitialized memory happened to contain a NaN, the comparison
original._parameters == unpickled._parameters produced False (since
NaN != NaN), raising AssertionError: Value of _parameters changed by pickling.

This is exactly the intermittent failure that led to Gaussian1DKernel being
disabled from convolution/tests/test_pickle.py in #18596 — Gaussian1DKernel
wraps a Gaussian1D, so the modeling-level flakiness surfaced there. As #18598
notes, fixing Gaussian1D fixes the kernel automatically.

The fix

Populate _parameters from the actual parameter values immediately after it is
allocated, so a pristine model never holds uninitialized memory:

self._parameters = np.empty(total_size, dtype=np.float64)
self._parameters_to_array()   # fill with real values right away

_parameters_to_array() only needs the _param_metrics slices (already built
earlier in _initialize_slices) and the parameter values, so it is safe to call
here. After the change, a fresh Gaussian1D() has _parameters == [1., 0., 1.]
(its real defaults) instead of garbage.

The disabled Gaussian1DKernel case in convolution/tests/test_pickle.py is
re-enabled (this is the Revert #18596 item on the issue checklist).

How it was tested

Reproduced the failure first (RED). With a fresh model whose uninitialized
_parameters was forced to contain NaN, check_pickling_recovery() failed with
the reported signature:

check_pickling_recovery: FAILED (RED) -> Value of _parameters changed by pickling

Reverting the one-line fix in the working tree and running the new
test_fresh_model_parameters_are_initialized shows the raw array actually holding
uninitialized garbage rather than the parameter values:

ACTUAL:  array([ 1.797693e+308, -1.797693e+308, 2.0, 2.22e-16, 2.22e-308, 4.94e-324])
DESIRED: array([1., 0., 0., 1., 1., 0.])

With the fix applied (GREEN), the full suites pass:

  • pytest astropy/modeling/tests/ → 1690 passed, 1118 skipped (scipy-gated)
  • pytest astropy/convolution/tests/ → 1560 passed, 36 skipped, 8 xfailed
  • combined modeling + convolution → 3250 passed, 0 failed

ruff check and ruff format --check (pinned v0.15.20) are clean on all three
changed files.

Two regression tests are added in modeling/tests/test_pickle.py:
test_fresh_model_parameters_are_initialized (asserts a pristine model's raw
_parameters matches its real values and contains no NaN) and
test_gaussian1d_strict_pickling_recovery (exercises the strict
check_pickling_recovery path across all pickle protocols).

A freshly constructed Model allocated its _parameters array with
np.empty() and only populated it on the first access of the
parameters property. Until then, _parameters held uninitialized
memory. When that memory happened to contain a NaN, strict-equality
operations that read the raw array directly -- such as
check_pickling_recovery() comparing a pristine model against its
unpickled copy -- failed non-deterministically. This was the root
cause of the flaky Gaussian1DKernel pickle test disabled in astropy#18596.

Populate _parameters from the actual parameter values immediately
after allocation, re-enable the Gaussian1DKernel pickle test, and add
regression tests covering both the initialization invariant and a
strict pickle round-trip.

Fixes astropy#18598
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.

@pllim pllim added this to the v8.1.0 milestone Jul 13, 2026
@Mohit-Ak
Mohit-Ak marked this pull request as ready for review August 10, 2026 14:12
@Mohit-Ak
Mohit-Ak requested review from a team and larrybradley as code owners August 10, 2026 14:12

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG: Fix Gaussian1D pickle behavior (affects Gaussian1DKernel)

2 participants