-
Notifications
You must be signed in to change notification settings - Fork 116
feat: stalign with a fit apply pattern. #1195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
selmanozleyen
wants to merge
150
commits into
scverse:main
Choose a base branch
from
selmanozleyen:feat/experimental-fit-core
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,271
−1
Open
Changes from all commits
Commits
Show all changes
150 commits
Select commit
Hold shift + click to select a range
5c08081
init
selmanozleyen 5847d84
doc clarifications
selmanozleyen 179ba06
don't expose the builders
selmanozleyen e187aba
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] cdb5699
deduplicate
selmanozleyen 847a46b
Merge branch 'feat/spatial_neighbours' of https://github.com/selmanoz…
selmanozleyen 69cda1a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] fddc027
arrange builders
selmanozleyen a30988a
Merge branch 'feat/spatial_neighbours' of https://github.com/selmanoz…
selmanozleyen f194b10
better behaviour
selmanozleyen ae1eba1
move classes
selmanozleyen a86572b
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen 039b852
cleanup
selmanozleyen 4699993
Merge branch 'feat/spatial_neighbours' of https://github.com/selmanoz…
selmanozleyen b5dcf9f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 4c11adf
add docs
selmanozleyen 2b937e9
Merge branch 'feat/spatial_neighbours' of https://github.com/selmanoz…
selmanozleyen ea1ce93
remove leftover
selmanozleyen ebb7fd5
resolution in reolve func
selmanozleyen 69a9394
reduce dup code
selmanozleyen 77755ed
deprecate invalid n_neighs
selmanozleyen d8a8145
expose new functions and deprecate the flat one
selmanozleyen 2e75b3f
move branches to classes
selmanozleyen 8c1c2ef
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 268d30c
remove radius abstraction
selmanozleyen 3f41243
Merge branch 'feat/spatial_neighbours' of https://github.com/selmanoz…
selmanozleyen aa9734a
add extensibility page
selmanozleyen 67bad43
unprivate methods and add extensibility page
selmanozleyen 65fa245
give a better example
selmanozleyen 85bfdeb
apply signature suggestion
selmanozleyen 79f584f
reorganize abstractions
selmanozleyen 43cdbb6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] e632f74
remove unnecessary abstraction
selmanozleyen 1f9da0e
Merge branch 'feat/spatial_neighbours' of https://github.com/selmanoz…
selmanozleyen c026860
put the warning in resolve graph builder code
selmanozleyen 10cc2f0
remove n_neighs from RadiusBuilder
selmanozleyen 6477f18
definition of Adj, Dst and rename to adj, dst
selmanozleyen 516b3e0
Generalize the base class for extensibility
selmanozleyen 141fdb6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 1fe316b
update docs
selmanozleyen bc6746a
Merge branch 'feat/spatial_neighbours' of https://github.com/selmanoz…
selmanozleyen bb1ec50
clarify delauney vs grid
selmanozleyen 84cbdc4
dtype rec
selmanozleyen 6d942ec
be a bit more verbose on funcitons
selmanozleyen ae9a4e6
spatial_neighbours_from_builder
selmanozleyen cd3f6fd
update docs to refer to each other
selmanozleyen 95012dc
refer to the classes properly
selmanozleyen 5a02b59
mark as TODO's
selmanozleyen 3af98cc
rename sugg
selmanozleyen e8a19d7
add combine into API
selmanozleyen ac682a3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 0f3329c
update docs
selmanozleyen 00ec2b0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 042f749
[pre-commit.ci] pre-commit autoupdate (#1149)
pre-commit-ci[bot] 1a87841
Functions to QC histopathology images (#1036)
timtreis 1cb6fde
remove coord_type abstraction
selmanozleyen 8afcd03
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 3c4dffb
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen 21f02c3
make builder positional consistently
selmanozleyen 744c757
make more abstractions
selmanozleyen dbc7f0d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] d7e5e5f
graphmatrixT exposure
selmanozleyen 13768e2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] ea3f3ca
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen 21f248e
add radius: float | int | tuple t
selmanozleyen 3bbc535
4 or 6
selmanozleyen a0426e6
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen f3eab97
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen 8436656
Merge branch 'main' into feat/spatial_neighbours
timtreis 07d59bf
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen 3e9fc41
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen 77627dc
mark snn as ilustrative
selmanozleyen 0afbe3c
add uns params
selmanozleyen 1711a62
deprecation warnings alignment
selmanozleyen 4632b74
set diag isnt defaulted to none
selmanozleyen f4805c1
fix
selmanozleyen f2b0fa0
radius is also positional
selmanozleyen 0ed1604
n_neighs on grid mode
selmanozleyen c85ba43
make radius kw only
selmanozleyen d9264a8
put a working example
selmanozleyen 9c89dc2
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen 52b4e79
fix api.md
selmanozleyen 60e888f
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen bfbb3e8
be more compatible with main
selmanozleyen 7e4ddec
Merge branch 'main' into feat/spatial_neighbours
selmanozleyen 9dd330e
align more to main
selmanozleyen cf38fe3
Merge branch 'feat/spatial_neighbours'
selmanozleyen 6c61ddc
save state
selmanozleyen e2266de
save state
selmanozleyen b2d25cb
refactor
selmanozleyen 14d36b0
undo lock file
selmanozleyen 86c9d76
Merge remote-tracking branch 'origin/main' into feat/experimental-fit…
selmanozleyen 8e4bb93
undo changes with main
selmanozleyen 9a489f2
resuse resolve_labels_array
selmanozleyen 1955bed
Merge branch 'main' into feat/experimental-fit-core
selmanozleyen 068f3e7
Merge branch 'main' into feat/experimental-fit-core
selmanozleyen 759e09e
Update api.md
selmanozleyen ad200ea
Update api.md
selmanozleyen 0e9b8c1
Merge branch 'main' into feat/experimental-fit-core
selmanozleyen c9eb7e4
rename file and refactor
selmanozleyen 77f3e7d
move files
selmanozleyen 57f9d21
fix imports
selmanozleyen 5d69264
fixes
selmanozleyen 6587efe
remove bloat code
selmanozleyen e45e247
typr hints
selmanozleyen beb1476
add tests
selmanozleyen 5eeb95d
reduce bloat
selmanozleyen 3c2ed02
reduce bloat
selmanozleyen 1ea9996
remove useless file
selmanozleyen ee741ab
fix
selmanozleyen ba91e4f
undo rasterize changes
selmanozleyen 1d65f4a
doc landmark
selmanozleyen a3da05a
remove bloat
selmanozleyen 373ca02
docs
selmanozleyen aaf48c7
doc debloatr
selmanozleyen 4387a61
expose result classes
selmanozleyen 85df8fc
refactoring, testing, typing and bug fix on copy mode
selmanozleyen 599186f
landmark is positional only now
selmanozleyen 8cfc770
expose api
selmanozleyen 96bc445
Merge branch 'main' into feat/experimental-fit-core
selmanozleyen 30e14cf
Merge branch 'main' into feat/experimental-fit-core
selmanozleyen d617b45
Merge branch 'main' into feat/experimental-fit-core
selmanozleyen f0868c1
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] e6b4075
lower bound jax
selmanozleyen bff9092
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 81a16f2
align expose
selmanozleyen f5ccd46
api refactor
selmanozleyen 461f5ee
expose methods in a nice place
selmanozleyen f6b907e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 265ad0b
reduce bloat
selmanozleyen 9740f5d
Merge branch 'main' into feat/experimental-fit-core
selmanozleyen 035e6e9
format
selmanozleyen 6e12e46
cleanup
selmanozleyen cbe72b5
file orga
selmanozleyen 4e5909c
file orga
selmanozleyen 3f9ab34
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 5646282
fix tests and imports
selmanozleyen f0415af
Merge branch 'feat/experimental-fit-core' of https://github.com/selma…
selmanozleyen 4a6e6ff
fix merge error
selmanozleyen bb20bc0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 44a5a22
fix merge error
selmanozleyen 62ec52f
Merge branch 'feat/experimental-fit-core' of https://github.com/selma…
selmanozleyen 6ed6344
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 6a0b106
autodoc mock jax
selmanozleyen 21cd780
remove casting
selmanozleyen 9236ac3
strict=True
selmanozleyen 5c663a2
add jax docs
selmanozleyen c07a707
speed up rasterize by avoiding loop
selmanozleyen f41aeb1
apply suggestion
selmanozleyen e405382
Merge branch 'main' into feat/experimental-fit-core
timtreis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| """In-memory model-fitting core for experimental methods. | ||
|
|
||
| The :mod:`.registry` subpackage holds the registry machinery and the family | ||
| registries; each family subpackage (e.g. :mod:`.align_samples`, | ||
| :mod:`.align_landmarks`) holds the estimator implementations. Importing this | ||
| package imports those subpackages so the estimators register themselves into the | ||
| (public) family registries. Each subpackage stays cheap to import -- heavy or | ||
| optional dependencies (e.g. JAX) are pulled in lazily, only when an estimator | ||
| actually runs. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| # Import for side effects: populates ALIGN_SAMPLES / ALIGN_LANDMARKS. | ||
| from squidpy.experimental.methods import align_landmarks, align_samples # noqa: F401 | ||
| from squidpy.experimental.methods.registry import ( | ||
| ALIGN_LANDMARKS, | ||
| ALIGN_SAMPLES, | ||
| AlignLandmarksFn, | ||
| AlignResult, | ||
| AlignSamplesFn, | ||
| Registry, | ||
| ) | ||
|
|
||
| __all__ = [ | ||
| "Registry", | ||
| "AlignResult", | ||
| "AlignSamplesFn", | ||
| "AlignLandmarksFn", | ||
| "ALIGN_SAMPLES", | ||
| "ALIGN_LANDMARKS", | ||
| ] |
21 changes: 21 additions & 0 deletions
21
src/squidpy/experimental/methods/align_landmarks/__init__.py
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| """``align_landmarks`` family: closed-form alignment from paired landmarks. | ||
|
|
||
| Importing this package registers the family's estimators into | ||
| :data:`~squidpy.experimental.methods.registry.ALIGN_LANDMARKS`. Only the | ||
| implementations are re-exported here; the registry itself lives in (and is public | ||
| from) :mod:`squidpy.experimental.methods`. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from squidpy.experimental.methods.align_landmarks._landmark import ( | ||
| AffineFitResult, | ||
| fit_affine, | ||
| fit_similarity, | ||
| ) | ||
|
|
||
| __all__ = [ | ||
| "AffineFitResult", | ||
| "fit_affine", | ||
| "fit_similarity", | ||
| ] |
155 changes: 155 additions & 0 deletions
155
src/squidpy/experimental/methods/align_landmarks/_landmark.py
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| """Closed-form landmark alignment estimators.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from collections.abc import Callable | ||
| from dataclasses import dataclass, field | ||
| from typing import Any | ||
|
|
||
| import numpy as np | ||
| import numpy.typing as npt | ||
|
|
||
| from squidpy._utils import NDArrayA | ||
| from squidpy.experimental.methods.registry import ALIGN_LANDMARKS | ||
|
|
||
|
|
||
| @dataclass | ||
| class AffineFitResult: | ||
| """A fitted ``(3, 3)`` homogeneous affine mapping query onto ref, in ``(x, y)``.""" | ||
|
|
||
| matrix: np.ndarray | ||
| source_cs: str | None = None | ||
| target_cs: str | None = None | ||
| metadata: dict[str, Any] = field(default_factory=dict) | ||
|
|
||
| def __post_init__(self) -> None: | ||
| if self.matrix.shape != (3, 3): | ||
| raise ValueError(f"Expected a (3, 3) homogeneous matrix, found shape {self.matrix.shape}.") | ||
|
|
||
| def transform(self, x: npt.ArrayLike) -> NDArrayA: | ||
| """Apply the affine to an ``(N, 2)`` ``(x, y)`` coordinate array.""" | ||
| coords = np.asarray(x, dtype=float) | ||
| if coords.ndim != 2 or coords.shape[1] != 2: | ||
| raise ValueError(f"Expected an (N, 2) coordinate array, found shape {coords.shape}.") | ||
| return coords @ self.matrix[:2, :2].T + self.matrix[:2, 2] | ||
|
|
||
|
|
||
| def _fit_landmark_relation( | ||
| ref: np.ndarray, | ||
| query: np.ndarray, | ||
| *, | ||
| method: str, | ||
| solve_fn: Callable[[np.ndarray, np.ndarray], np.ndarray], | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't it be |
||
| source_cs: str | None = None, | ||
| target_cs: str | None = None, | ||
| ) -> AffineFitResult: | ||
| ref = _validate_landmarks(ref, name="ref") | ||
| query = _validate_landmarks(query, name="query") | ||
| if ref.shape != query.shape: | ||
| raise ValueError(f"`ref` and `query` must have the same shape; got {ref.shape} and {query.shape}.") | ||
| if ref.shape[0] < 3: | ||
| raise ValueError(f"`{method}` needs at least 3 landmark pairs, got {ref.shape[0]}.") | ||
|
|
||
| matrix = solve_fn(ref, query) | ||
| return AffineFitResult( | ||
| matrix=matrix, | ||
| source_cs=source_cs, | ||
| target_cs=target_cs, | ||
| metadata={"method": method}, | ||
| ) | ||
|
|
||
|
|
||
| @ALIGN_LANDMARKS.register("similarity") | ||
| def fit_similarity( | ||
| ref: np.ndarray, | ||
| query: np.ndarray, | ||
| *, | ||
| source_cs: str | None = None, | ||
| target_cs: str | None = None, | ||
| ) -> AffineFitResult: | ||
| """4-DOF similarity fit (rotation + uniform scale + translation), via spatialdata. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| ref, query | ||
| Pre-paired ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``). | ||
| source_cs, target_cs | ||
| Optional coordinate-system labels stamped onto the result for | ||
| traceability; they do not affect the fit. | ||
| """ | ||
| return _fit_landmark_relation( | ||
| ref, | ||
| query, | ||
| method="similarity", | ||
| solve_fn=_fit_similarity, | ||
| source_cs=source_cs, | ||
| target_cs=target_cs, | ||
| ) | ||
|
|
||
|
|
||
| @ALIGN_LANDMARKS.register("affine") | ||
| def fit_affine( | ||
| ref: np.ndarray, | ||
| query: np.ndarray, | ||
| *, | ||
| source_cs: str | None = None, | ||
| target_cs: str | None = None, | ||
| ) -> AffineFitResult: | ||
| """6-DOF affine fit (rotation + non-uniform scale + shear + translation), via skimage. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| ref, query | ||
| Pre-paired ``(N, 2)`` ``(x, y)`` landmark arrays (``N >= 3``). | ||
| source_cs, target_cs | ||
| Optional coordinate-system labels stamped onto the result for | ||
| traceability; they do not affect the fit. | ||
| """ | ||
| return _fit_landmark_relation( | ||
| ref, | ||
| query, | ||
| method="affine", | ||
| solve_fn=_fit_affine, | ||
| source_cs=source_cs, | ||
| target_cs=target_cs, | ||
| ) | ||
|
|
||
|
|
||
| def _validate_landmarks(points: np.ndarray, *, name: str) -> np.ndarray: | ||
| arr = np.asarray(points, dtype=float) | ||
| if arr.ndim != 2 or arr.shape[1] != 2: | ||
| raise ValueError(f"`{name}` must be a sequence of (x, y) pairs, got shape {arr.shape}.") | ||
| if not np.all(np.isfinite(arr)): | ||
| raise ValueError(f"`{name}` must contain only finite values.") | ||
| return arr | ||
|
|
||
|
|
||
| def _fit_similarity(ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: | ||
| """4-DOF similarity fit, delegated to spatialdata.""" | ||
| from spatialdata.models import PointsModel | ||
| from spatialdata.transformations import get_transformation_between_landmarks | ||
|
|
||
| refs_pts = PointsModel.parse(ref_xy) | ||
| moving_pts = PointsModel.parse(query_xy) | ||
| sd_transform = get_transformation_between_landmarks(refs_pts, moving_pts) | ||
| return _extract_affine_matrix(sd_transform) | ||
|
|
||
|
|
||
| def _fit_affine(ref_xy: np.ndarray, query_xy: np.ndarray) -> np.ndarray: | ||
| """Full 6-DOF affine fit, delegated to skimage's least-squares estimator.""" | ||
| from skimage.transform import estimate_transform | ||
|
|
||
| model_obj = estimate_transform("affine", src=query_xy, dst=ref_xy) | ||
| return np.asarray(model_obj.params) | ||
|
|
||
|
|
||
| def _extract_affine_matrix(sd_transform: object) -> np.ndarray: | ||
| """Pull a ``(3, 3)`` homogeneous matrix out of a spatialdata transformation.""" | ||
| from spatialdata.transformations import Affine as SDAffine | ||
| from spatialdata.transformations import Sequence as SDSequence | ||
|
|
||
| if isinstance(sd_transform, SDAffine): | ||
| return np.asarray(sd_transform.matrix) | ||
| if isinstance(sd_transform, SDSequence): | ||
| return np.asarray(sd_transform.to_affine_matrix(input_axes=("x", "y"), output_axes=("x", "y"))) | ||
| raise TypeError(f"Unexpected transformation type from spatialdata: {type(sd_transform).__name__}.") | ||
14 changes: 14 additions & 0 deletions
14
src/squidpy/experimental/methods/align_samples/__init__.py
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| """``align_samples`` family: align two samples' point clouds (STalign). | ||
|
|
||
| Importing this package registers the family's estimators into | ||
| :data:`~squidpy.experimental.methods.registry.ALIGN_SAMPLES`. It stays | ||
| cheap -- JAX is pulled in lazily, only when an estimator's ``fit`` runs. Only the | ||
| implementations are re-exported here; the registry itself lives in (and is public | ||
| from) :mod:`squidpy.experimental.methods`. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from squidpy.experimental.methods.align_samples._stalign import StalignResult, fit_stalign | ||
|
|
||
| __all__ = ["fit_stalign", "StalignResult"] |
Oops, something went wrong.
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using a registry here? It's unlikely this will get extended upon much and it introduces complexity