Skip to content

fix: convert em_rates to backend array in Identity.render (#124)#140

Open
parallelArchitect wants to merge 8 commits into
tlambert03:mainfrom
parallelArchitect:fix-identity-modality-gpu-backend
Open

fix: convert em_rates to backend array in Identity.render (#124)#140
parallelArchitect wants to merge 8 commits into
tlambert03:mainfrom
parallelArchitect:fix-identity-modality-gpu-backend

Conversation

@parallelArchitect

Copy link
Copy Markdown
Contributor

Identity.render() declared **kwargs instead of an explicit xp parameter, so xp was never in scope to convert em_rates to the active backend. truth is converted via xp in the ground_truth() path; em_rates from filtered_emission_rates() was not, causing a TypeError when multiplying a backend array (cupy) against an unconverted numpy array.

Fix mirrors the working solution from the issue reporter's fork: em_rates.copy(data=xp.asarray(...)) preserves the xarray wrapper so .coords access below still works. Verified against both cupy and numpy backends, using synthetic MatsLines data and real COSEM segmentation data (jrc_hela-3, er-mem_pred).

Re: the simpler version discussed in the issue thread
(xp.asarray(em_rates.sum(Axis.W).data) without .copy()) — that
returns a raw array with no .coords attribute, which would break
the em_rates.coords[Axis.C] reference a few lines down in the
return statement. That's likely why it wasn't testable as-is.

…#124)

Identity.render() declared **kwargs instead of an explicit xp parameter,
so xp was never in scope to convert em_rates to the active backend.
truth is converted via xp in the ground_truth() path; em_rates from
filtered_emission_rates() was not, causing a TypeError when multiplying
a backend array (cupy) against an unconverted numpy array.

Fix mirrors the working solution from the issue reporter's fork:
em_rates.copy(data=xp.asarray(...)) preserves the xarray wrapper so
.coords access below still works. Verified against both cupy and numpy
backends, using synthetic MatsLines data and real COSEM segmentation
data (jrc_hela-3, er-mem_pred).
@codecov

codecov Bot commented Jun 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.59%. Comparing base (3aedf76) to head (cf0269f).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #140      +/-   ##
==========================================
+ Coverage   85.48%   85.59%   +0.11%     
==========================================
  Files          46       46              
  Lines        2963     2965       +2     
==========================================
+ Hits         2533     2538       +5     
+ Misses        430      427       -3     

☔ 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.

xp as a keyword-only param via **kwargs violated Liskov substitution
against _PSFModality.render()'s signature. Matches base class exactly:
truth, em_rates, objective_lens, settings, xp as named params.

Re-verified GPU (cupy) and CPU (numpy) backends after the signature
change.
Covers Identity.render() backend conversion across numpy and cupy
(parametrized via the existing np_backend fixture). Scoped narrowly
to optical_image() — the boundary of what tlambert03#124 actually touches —
to avoid an unrelated, pre-existing failure in the detector
simulation step that surfaces when running the full pipeline.
@parallelArchitect

Copy link
Copy Markdown
Contributor Author

The 11 failing CI jobs here are unrelated to this fix — all of them
fail on the same root cause: the COSEM API now returns format: "zarr3"
for some images, which the pydantic ImageFormat literal doesn't accept.
That breaks test_cosem_dataset, test_cosem_image, test_cosem_view,
test_cosem_manage, and test_examples[cosem.py], regardless of any
change in this PR — it would fail identically on main right now.

Filed and fixed separately in #141 (adds "zarr3" to the ImageFormat
literal), kept split out since it's an unrelated bug, hit only while
validating this fix against real COSEM data. This branch should pass
clean once #141 merges and this rebases onto main.

Coverage gap from earlier is also closed — added a regression test
(test_identity_modality_backend_conversion) covering both numpy and
cupy backends.

@tlambert03

Copy link
Copy Markdown
Owner

thanks for digging into this too! I'm working on unpinning numpy and solidifying the pinnings for 3.12-3.14, so the dev notes (which are much appreciated!) might not be relevant shortly. Will come back to this tomorrow or the next day either way

@parallelArchitect

Copy link
Copy Markdown
Contributor Author

Thanks for the context on the numpy unpinning — good to know the dev notes may have a short shelf life. Happy to rebase or update anything once the pinning work settles. Looking forward to your feedback on #140.

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