Stop project_volume from corrupting the caller's volume - #30
Merged
Merged
Conversation
For kind="max"/"min" the reduction has to keep the zero-padded path entries out of the result, and it did that by writing the dtype's extreme value into volume.flat[0] and never restoring it. That modified the caller's array; with hemisphere="both" the second pass wrote through the np.flip view, so volume[0, 0, -1] was clobbered as well. Substitute the sentinel into the gathered copy instead, in both Isocortex2dProjector._project_volume_to_view and IsocortexEntireProjector.project_volume. paths == 0 is exactly the set of entries that used to read the overwritten voxel, so the reduction is unchanged; read-only volumes now project as well. Measured at streamline scale (250k paths x 200), results are identical, peak memory is unchanged and the max path is ~3% slower. Fixes #20 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
gouwens
marked this pull request as ready for review
August 31, 2026 21:01
ablot
added a commit
to znamlab/ccf_streamlines
that referenced
this pull request
Aug 31, 2026
Brings lab up to date with main: uv conversion, src/ layout, mini-CCF test fixtures and CI, and merged upstream PRs AllenInstitute#28/AllenInstitute#29/AllenInstitute#30. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #20.
kind="max"/"min"has to keep the zero-padded entries ofpathsout of the reduction. It did that by writing the dtype's extreme value intovolume.flat[0]and never restoring it, so the caller's array came back modified — and withhemisphere="both"the second pass wrote through thenp.flip(volume, axis=2)view, clobberingvolume[0, 0, -1]too.The sentinel is now substituted into the gathered copy:
self.paths == 0is exactly the set of entries that previously read the overwritten voxel, so the reduction is unchanged. BothIsocortex2dProjector._project_volume_to_viewandIsocortexEntireProjector.project_volumewere affected and both are fixed. Read-only volumes — a memory-mapped reference atlas, say — now project instead of raisingValueError: assignment destination is read-only.Why not save-and-restore
The issue offers either. Masking never touches the input at all, so it also covers read-only arrays and does not briefly publish a corrupted volume to a concurrent reader.
Cost
Measured at streamline scale (250k paths × 200 padded length, gathering from a 250M-voxel volume): results identical, peak memory unchanged, the
maxpath ~3% slower. The bool mask is a temporary an order of magnitude smaller than theintpindex array the existing.flat[paths]gather already allocates. Themean/sumbranches directly below already mask this way, with a full second copy.Tests
tests/test_project_volume_mutation.py, 30 cases over both projector classes:left/right/both), both kinds, and four dtypes covering thenp.iinfoandnp.finfobranches —volume.flat[0]andvolume[0, 0, -1]are asserted by name so a regression says which cell;20 of the 30 fail against the unfixed source. The repeatability tests are not among them and say so in their docstrings: the old code wrote the same sentinel every call, and neither clobbered cell lies on a mini-CCF streamline, so a repeated projection could not see the corruption. They are labelled characterizations rather than left to look like detectors.
Full suite: 102 passed.
Note for #19
PR #19 pins this defect with three
xfail(strict=True)tests. Once this merges they become XPASS failures — that is the intended signal to delete those markers on rebase. Itstests/test_projector_2d.py/tests/test_entire_projector.pycoverage and this file overlap and can be consolidated there; I kept the fix onmainrather than in the stack so it does not wait on #18.docs/source/glossary.rst, theMiniCCF.volumedocstring and the CLAUDE.md gotcha all described the mutation as current behaviour and are updated.🤖 Generated with Claude Code