Skip to content

Diffusion maps with adaptive anisotropic affinity kernels - #144

Merged
WeilerP merged 4 commits into
mainfrom
feat/diffmap
Sep 2, 2026
Merged

WeilerP merged 4 commits into
mainfrom
feat/diffmap

Conversation

@WeilerP

@WeilerP WeilerP commented Sep 2, 2026

Copy link
Copy Markdown
Owner

Changes

  • update post gen hooks to render relative imports correctly
  • add new submodules via aliases to the built project init file

New

  • preprocessing module: wrap pp.neighbors from scanpy/rapids_singlecell to facilitate adaptive anisotropic affinity kernels
  • tools module: wrap tl.diffmap from scanpy/rapids_singlecell for palantir-style diffusion maps

Related issues

Closes #143.

Wraps scanpy/rapids_singlecell (tuned via back)
Add `neighbors`, a wrapper around scanpy's/ rapids_singlcell's (set via
the `backend` arg) `pp.neighbors` that can build Palantir's adaptive
anisotropic affinity kernel (Setty et al., 2019) instead of the
canonical fixed-bandwidth one, without taking on `palantir` as a
dependency.

With `adaptive=False` (default) the call passes through to the
backend's own `pp.neighbors`. With `adaptive=True`, `_adaptive_kernel`
derives a per-observation bandwidth from that observation's
`floor(knn / 3)`-th nearest neighbor, scales and exponentiates the kNN
distances, then symmetrizes; Coifman-Lafon density normalization is
applied on top when `alpha > 0`. The kernel is stored under its own
`key_added` prefix in `obsp`/`uns`, so it coexists with a plain
neighbors graph rather than overwriting it.

Checked against `palantir.utils.compute_kernel` on 300 synthetic
observations: identical sparsity pattern, maximum elementwise
difference 3.3e-16.
Add `diffmap`, which builds the adaptive anisotropic kernel via
`neighbors(adata, adaptive=True, ...)` and hands it to `sc.tl.diffmap`
through `neighbors_key`. The kernel is Palantir's; the
eigendecomposition, the density normalization of the diffusion
operator and the output keys remain scanpy's.

Results land in the standard `obsm["X_diffmap"]` and
`uns["diffmap_evals"]`, so downstream `sc.tl.dpt` and
`sc.pl.embedding(basis="diffmap")` keep working unchanged.
`backend="rapids"` routes both the kNN search and the
eigendecomposition through `rapids_singlecell`.

The cross-subpackage import carries `# noqa: TID252`: a sibling
subpackage is only reachable relatively, and the absolute form is not
valid Python while the package name is an unrendered Jinja token.
Alias both subpackages at the package root, the way
`scanpy/__init__.py` aliases its own `preprocessing` and `tools`, so
callers write `pp.neighbors(...)` and `tl.diffmap(...)` against a
familiar surface.
A module inside the package can reach a sibling subpackage only
through a parent-relative import, which ruff's TID252 rejects, while
the absolute form is not valid Python before the package name is
rendered. The template therefore writes the relative import with an
explicit `# noqa: TID252`, and generation now rewrites it to the
absolute form and drops the suppression, leaving the generated project
canonical and its own hooks passing unmodified. This is the treatment
`unquote_pixi_key` already gives the quoted pixi self-dependency key.

Generation now also fails if such an import cannot be rewritten,
rather than silently shipping a stale suppression.
`_check_import_calls.py` reports that at commit time for free, since
it runs the real rewrite over a disposable copy instead of
reimplementing it.
@WeilerP WeilerP changed the title Feat/diffmap Diffusion maps with adaptive anisotropic affinity kernels Sep 2, 2026
@WeilerP
WeilerP merged commit 997f018 into main Sep 2, 2026
2 checks passed
@WeilerP
WeilerP deleted the feat/diffmap branch September 2, 2026 20:34
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.

Diffusion maps with adaptive anisotropic affinity kernel

1 participant