Skip to content

Make combiner internals array-API compatible - #988

Merged
mwcraig merged 4 commits into
astropy:mainfrom
mwcraig:fix-combiner-strict-internals
Aug 24, 2026
Merged

Make combiner internals array-API compatible#988
mwcraig merged 4 commits into
astropy:mainfrom
mwcraig:fix-combiner-strict-internals

Conversation

@mwcraig

@mwcraig mwcraig commented Aug 23, 2026

Copy link
Copy Markdown
Member

Part of #971. This addresses three unnumbered checklist items from the array-API tracker, all in the combiner internals, plus the immediately adjacent numpy-isms those fixes exposed in the same code paths.

The fixes

  1. Combiner._get_nan_substituted_data used mask.any() — a numpy-only array method. Now xp.any(...) in the combiner's namespace. Fixing this exposed the next layer in the same functions, which is also fixed here: .sum(axis=0) method calls (now xp.count_nonzero, since the array API also disallows summing booleans), len(<array>) (now .shape[0]; __len__ is not required by the standard), a list passed to xp.reshape (now a tuple), and integer counts fed to xp.sqrt/uncertainty scaling (now cast to float64, matching the promotion numpy does implicitly).
  2. Builtin bool passed as a dtype in the _CCDDataWrapperForArrayAPI mask setters and in test bodies that build masks in the testing namespace — strict namespaces only accept their own dtype objects. Now xp.bool.
  3. combine() memory sizing used .nbytes, which is not in the array API standard. A new _array_size_in_bytes() helper computes size from array_api_compat.size(arr) and the dtype bit width from xp.finfo/xp.iinfo (booleans counted as 1 byte, complex as two components). The stale backend_xfail on test_combine_scale_callable_returning_backend_scalar is removed and a direct unit test of _calculate_size_of_image is added.

Strict suite: 99 failed / 348 passed → 85 failed / 364 passed

(CCDPROC_ARRAY_LIBRARY=array-api-strict, on top of #987.)

Causes eliminated: the 28 .any() failures, the 9 builtin-bool dtype failures, the 2 .nbytes failures, and the intermediate layers behind them (.sum method, len(), list reshape, int→float promotion). One previously xfailed test now passes and its marker is removed; three test_cosmicray parametrizations now xpass as a side effect of the wrapper mask-setter fix (their marker still guards other parametrizations, so it stays).

Remaining failures are known separate issues, left for follow-up: device handling (#946, now the largest bucket at 31 since more tests reach the device checks), xp.median in core.py (4), fancy-index __setitem__ in clip_extrema (4), sigma clipping via astropy.stats (#929), combine() file-input handling, CCDData arithmetic converting arrays inside astropy.nddata (the "Could not convert Array" bucket, hit by the scaling tests), and numpy-isms in test bodies (.mean() method calls, mixed int/float test arithmetic, dtype comparisons).

test_combiner.py passes fully on numpy (89 passed) and dask (93 passed); the full numpy suite is green (501 passed).

🤖 Generated with Claude Code

https://claude.ai/code/session_01RQMJZUaaxfqGDk41GLSaFK

mwcraig and others added 2 commits August 23, 2026 17:38
Fix the strict-backend bugs in the Combiner internals and the
memory sizing used by combine():

- Replace the numpy-only mask.any() call in _get_nan_substituted_data
  with xp.any(), and count masked entries with xp.count_nonzero()
  instead of the .sum() array method (sums of booleans are not allowed
  under the array API in any case).
- Replace len() on stacked data arrays with .shape[0] -- the array API
  does not require arrays to implement __len__ -- and cast the
  resulting integer counts to float64 before xp.sqrt() and the
  uncertainty scaling, matching the int-to-float promotion numpy does
  implicitly and strict namespaces refuse.
- Pass the weights reshape target as a tuple; strict namespaces reject
  a list.
- Use xp.bool instead of the builtin bool for mask dtypes in the
  _CCDDataWrapperForArrayAPI mask setters and in tests that build
  masks in the testing namespace; strict namespaces only accept their
  own dtype objects.
- Size images for the mem_limit calculation in combine() without
  .nbytes, which is not part of the array API standard: a new
  _array_size_in_bytes() helper computes the size from the element
  count and the dtype bit width reported by xp.finfo()/xp.iinfo().
  The backend_xfail guarding test_combine_scale_callable_* is removed
  now that combine() no longer fails on .nbytes, and a direct unit
  test covers the new sizing.

With CCDPROC_ARRAY_LIBRARY=array-api-strict the test suite goes from
99 to 85 failures (348 to 364 passes); the remaining failures are
known separate issues (device handling, xp.median, clip_extrema fancy
indexing, sigma clipping via astropy.stats) or numpy-isms in test
bodies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RQMJZUaaxfqGDk41GLSaFK
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RQMJZUaaxfqGDk41GLSaFK
@codecov

codecov Bot commented Aug 23, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.42%. Comparing base (702866c) to head (3e644d8).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #988      +/-   ##
==========================================
+ Coverage   97.29%   97.42%   +0.13%     
==========================================
  Files           9        9              
  Lines        1738     1749      +11     
==========================================
+ Hits         1691     1704      +13     
+ Misses         47       45       -2     
Flag Coverage Δ
dask 96.55% <100.00%> (+0.13%) ⬆️
jax 96.61% <96.96%> (+0.07%) ⬆️
numpy 97.31% <100.00%> (+0.13%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Codecov flagged the complex path as the one uncovered line in the
patch; no combiner test feeds the size helper a complex array.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RQMJZUaaxfqGDk41GLSaFK
@mwcraig
mwcraig requested a balanced review from Copilot August 24, 2026 14:37
Comment thread ccdproc/combiner.py
Comment thread CHANGES.rst Outdated
the array API standard and use them in ``Combiner.average_combine`` and
``Combiner.sum_combine`` when the selected array namespace has no
``nansum``/``nanmean``/``nanstd``, instead of raising ``RuntimeError``. [#986]
- Make the ``Combiner`` combination internals array-API compatible: masked

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shorten this

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — the mechanism list belongs in the PR, not the changelog. Proposed replacement for the whole entry:

- Make the ``Combiner`` combination internals array-API compatible and size
  images for ``mem_limit`` from the element count and dtype width instead of
  the non-standard ``nbytes``. [#988]

Happy to push that.

Written by Claude at @mwcraig's direction.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed in 3e644d8.

Written by Claude at @mwcraig's direction.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Makes Combiner internals compatible with strict Array API backends.

Changes:

  • Replaces NumPy-specific array methods and dtype usage.
  • Adds portable array memory-size calculation.
  • Expands tests and updates the changelog.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
CHANGES.rst Documents the compatibility fixes.
ccdproc/combiner.py Updates reductions, sizing, reshaping, and uncertainty scaling.
ccdproc/_ccddata_wrapper_for_array_api.py Uses namespace-native boolean dtypes.
ccdproc/tests/test_combiner.py Updates backend-compatible tests and adds memory-size coverage.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

The mechanism list belongs in the PR description, not the changelog.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RQMJZUaaxfqGDk41GLSaFK
@mwcraig
mwcraig merged commit 676dc0e into astropy:main Aug 24, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants