Repository navigation
feat: stalign api and landmark alignment - #1195
Open
selmanozleyen wants to merge 42 commits into
Open
selmanozleyen wants to merge 42 commits into
selmanozleyen wants to merge 42 commits into
Conversation
timtreis
reviewed
Jun 16, 2026
timtreis
reviewed
Jun 16, 2026
timtreis
reviewed
Jun 16, 2026
| radius = int(np.ceil(float(np.max(blur_values)) * 4.0)) | ||
| denom = 2.0 * (dx * blur_values * 2.0) ** 2 | ||
|
|
||
| for x_i, y_i in zip(x, y, strict=False): |
Member
There was a problem hiding this comment.
How many points do we have? Should probably rasterize
Member
Author
There was a problem hiding this comment.
wdym? This is the function for rasterization?
timtreis
reviewed
Jun 16, 2026
timtreis
reviewed
Jun 16, 2026
timtreis
reviewed
Jun 16, 2026
timtreis
reviewed
Jun 23, 2026
timtreis
reviewed
Jun 23, 2026
This was referenced Aug 11, 2026
selmanozleyen
commented
Aug 27, 2026
selmanozleyen
force-pushed
the
feat/api-docs-restructure
branch
3 times, most recently
from
August 30, 2026 23:20
3c54e85 to
7b33737
Compare
selmanozleyen
force-pushed
the
feat/api-docs-restructure
branch
3 times, most recently
from
September 25, 2026 16:05
d594650 to
11d8760
Compare
selmanozleyen
force-pushed
the
feat/experimental-fit-core
branch
from
September 25, 2026 16:05
69dae69 to
76d69a7
Compare
selmanozleyen
force-pushed
the
feat/api-docs-restructure
branch
from
September 25, 2026 19:42
11d8760 to
e5f0d6b
Compare
The volume fit never passes landmarks, so the point term is zero and the value was dead; it existed only because `lddmm` required the argument. `sigmaP` is now optional there and checked only when points are given.
test_stalign_reference.py, moved from squidpy-ports, compares the kernel, the fits and the public entry points against values the original STalign computed. They come from one zarr store squidpy-ports publishes, fetched with pooch by sha256. The jax CI env sets JAX_ENABLE_X64, which the comparison needs.
…guide They moved from extras to [dependency-groups], so `.[dev,test,doc]` installed none of them and `uv sync --all-extras` only the default dev group.
- One session fixture downloads the bundle to scanpy's datasetdir, reads it into memory and closes the store; the fixture parameters are plain constants, checked against the bundle's root attributes in one test. - The x64 requirement is a module-level skip, and the download is marked internet. - Drop the unused slice_target_axes fixture, _run_image's unused argument, the clouds namespace, a duplicate velocity-grid test, and the sigmaP the landmark-free runs passed for nothing (_lddmm_loss now accepts None). - Recover an axis' placement from its first step instead of four guesses. - test_params.py goes back to main: since Array became HasArrayNamespace, every union arm is isinstance-checkable and _safe_matches never caught.
tests/experimental/conftest.py goes back to main: make_sdata_tables had no caller, ALIGN_PTS and TINY_SOLVER one file each, and each file needs its own variant of make_adata.
Rationale for a choice goes in the PR, not beside the line. What stays is one line each where the code cannot show it: an upstream line reference, a JAX indexing gotcha, why a division is split for bit-identity. Two comments were also wrong: niter=0 evaluates nothing, and the initial weights were listed out of order.
_check_direction only renamed a normalize_choice call, and reverse_axes and _grid_points had one caller each.
selmanozleyen
marked this pull request as draft
October 29, 2026 14:42
transform_points maps query into reference on every fit; rank-2 fits add inverse_transform_points and, for images, inverse_warp_image. The volume fit no longer takes a `direction` it could only refuse, and its deformation_grid no longer defaults to the solver's "backward": both fits' grids now mean the query grid in the reference frame. The solver's own forward/backward stays private.
- A 2D image passed where a volume is needed (or the reverse) is refused by its `z` dim, instead of its channels being read as z. - Axes must be evenly spaced, and sample_volume's must match the volume. - apply_affine takes only a (3, 3) matrix and finite points. - A fit that ends with non-finite parameters raises FloatingPointError. - The rotation error no longer points at an axes argument that doesn't exist. - A test asserted a dataclass, which is always true; it now just runs the call.
It mapped the volume onto the section, the solver's moving-to-fixed order and the reverse of the 2D paths. It now means section -> volume, the same map transform_points gives, and is inverted for the solver.
stalign_align_image and stalign_align_volume take scale (one level, or a (ref, query) pair; default scale0). The physical axes come from that level's own transformation, so a coarser level fits faster in the same units.
It is public and JAX-free but had no test of its own.
squidpy-ports now publishes it as an AnnData in a zipped zarr store. Provenance is checked for every section, not a hardcoded list that missed one.
…read aux - Non-public functions keep a one- or two-line docstring; the rationale and parameter prose they carried is gone (users read tol/patience on the Stalign*Params types). - interp works on the arrays it is given instead of re-casting them, and the single-sample axis check moves to resolve_axes, which every fit's axes pass. - The `_axis = _rasterize_points.axis` alias becomes a direct import. - _lddmm_loss's aux keeps what is read (the contrast-mapped source, and the regularisation energy the reference suite pins); landmarks are transformed only when there are some.
…tests - The container helpers the fit classes use (table lookup, obsm read and write-back, the frame check) move to _io, which imports neither _api nor _stalign; fit serialisation moves next to the to_uns/from_uns methods it backs. Nothing is imported inside a method any more. - _api's own channels-first coercion goes (the estimators run as_chw), and the obsm and fit-point shape checks reuse validate_xy, gaining its finite check. - Three landmark tests repeated others line for line; the "returns None" check of the one dropped is kept in the test that inspects the registered values.
- With `tol` set, early stopping no longer fires before `diffeo_start`, where it could return a fit whose velocity was never updated. - `rasterize_points` refuses `expand < 1`, and its mass claim says when it holds. - `writeback_affine_sdata` checks every element before writing any, so a refusal leaves nothing half-registered. - A `scale` level that doesn't exist raises, listing the levels, instead of falling back to scale0. - `warp_image` checks the image against the axes it is sampled on. - `align_landmarks` refuses a same-object table reference whose elements would move, and non-point landmark geometries. - In the jax CI env a missing jax fails the run instead of skipping it.
… classes - stalign_align_obs takes ref/query_coordinate_system and, for SpatialData, refuses obsm coordinates whose element is not placed there by the identity, the rule the image path already applies. Every fit now records the query frame, so transform checks it for all of them. - initial_velocity / velocity_grid leave the public params and lddmm: nothing passed them, and a continued fit silently restarted its weights and schedule. - The rank-2 fits share one inverse_transform_points; deformation_grid takes only the query axes it uses; _warp resolves its axes once; aligned_points is transform_points of the query. - centred_axes's always-1 scale goes (inlined); _rasterize_points exports only its public functions; the tol/patience, axes and registration-target docs say what the code does.
- The mixed-unit velocity grid is compared by value, with no solver run first. - The obs smoke test checks that what transform writes is the fit's aligned_points, not only its shape. - The public-call reference tests say what they assert instead of naming the retired notebook harness. - A landmark test that only checked a key existed goes; the two-pair minimum is asserted once, not once per `side`; the sample_volume test moves next to the rest of its module's tests. - hatch.toml: the jax matrix row is aligned and each env var has its own comment.
The test kept its own copies of the image, slice, LDDMM and raster settings the bundle was generated with. stalign-reference-v3 records all of them in uns["fixtures"], so a `params` fixture reads them from there and the copies, and the test that held them to the bundle, go.
- test_image_landmarks_reach_the_solver starts both fits from the same affine, so only the point term can tell them apart; before, landmarks also set the start and the test passed with the term dropped. - A velocity grid with a single sample along an axis (`a` too wide for the image) raises naming `a` and `expand`, instead of fitting on a NaN kernel and blaming the step sizes when it diverges. - Landmark arrays must pair up row by row even with `initial_affine`, which skipped the only check that compared them. - StalignImageParams says its lengths and steps are in the coordinate system's units, with defaults sized for roughly pixel-sized ones.
selmanozleyen
marked this pull request as ready for review
October 29, 2026 16:45
In the default float32, micron-scale axes (np.arange(200) * 2.7 + 1000) carry spacing errors above allclose's tolerance once cast, so evenly spaced axes were refused. The float64 CI env could not see it; the new test runs in whatever precision the env has.
This branch has not been deployed
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.
Continuation of #1162