Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ jobs:
name: Lint & type-check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true

Expand All @@ -41,10 +41,10 @@ jobs:
name: Build docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true

Expand All @@ -62,10 +62,10 @@ jobs:
matrix:
python-version: ["3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true

Expand All @@ -86,7 +86,7 @@ jobs:

- name: Upload coverage
if: matrix.python-version == '3.12'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: coverage-xml
path: coverage.xml
39 changes: 22 additions & 17 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
name: Deploy docs
name: Docs

on:
push:
branches: [master]
branches: [main]
workflow_dispatch:

# Deploy via the GitHub Pages "GitHub Actions" source (set it in repo
# Settings -> Pages). Zensical has no `gh-deploy`; we build and upload the
# site as a Pages artifact instead.
permissions:
contents: write

concurrency:
group: docs-${{ github.ref }}
cancel-in-progress: true
contents: read
pages: write
id-token: write

jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v5
- uses: actions/configure-pages@v6
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v8.2.0
with:
python-version: "3.13"
enable-cache: true

- name: Sync (docs group)
run: uv sync --group docs

- name: Deploy to GitHub Pages
run: uv run mkdocs gh-deploy --force
- run: uv sync --dev
- run: uv run zensical build
- uses: actions/upload-pages-artifact@v6
with:
path: site
- uses: actions/deploy-pages@v6
id: deployment
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
name: Build distributions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v8.2.0

- name: Build sdist + wheel
run: uv build
Expand All @@ -31,7 +31,7 @@ jobs:
run: uvx twine check dist/*

- name: Upload artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: dist
path: dist/*
Expand All @@ -44,7 +44,7 @@ jobs:
permissions:
id-token: write # OIDC for Trusted Publishing
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
name: dist
path: dist
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repos:

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.15.15
rev: v0.15.16
hooks:
# Run the linter.
- id: ruff
Expand Down
5 changes: 3 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ message: >-
fork and also cite the original TopOMetry work listed under references.
type: software
title: topometry-nosc
version: 0.1.0
version: 0.3.0
date-released: 2026-06-10
authors:
- family-names: Madsen
given-names: Jakob
Expand Down Expand Up @@ -34,6 +35,6 @@ references:
journal: eLife
year: 2024
volume: 13
article-number: RP100361
number: RP100361
doi: 10.7554/eLife.100361.2
url: "https://doi.org/10.7554/eLife.100361.2"
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[![PyPI](https://img.shields.io/pypi/v/topometry-nosc)](https://pypi.org/project/topometry-nosc/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/HauserGroup/topometryNoSC/blob/master/notebooks/example.ipynb)
[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
Expand Down Expand Up @@ -161,9 +162,14 @@ Example `TopOGraph` fit:

## Changelog

**0.2.0** — Core-only release
- Removed single-cell / scanpy / AnnData wrappers (now a standalone geometry toolkit)
- Core API unchanged: `TopOGraph`, spectral scaffolds, graph operators, layouts, metrics, plotting
**0.3.0** — First PyPI release
- Published to PyPI as [`topometry-nosc`](https://pypi.org/project/topometry-nosc/)
- Simplified internals delegate to scikit-learn/SciPy/umap-learn where possible;
slimmer runtime dependencies (ANN backends and extra layouts are opt-in extras)
- Standalone building blocks (`kNN`, `Kernel`, `cknn_graph`, `EigenDecomposition`,
`LE`, `Projector`) usable and tested outside the `TopOGraph` orchestrator

See [CHANGELOG.md](CHANGELOG.md) for the full history.

#### Citation

Expand Down
14 changes: 1 addition & 13 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,13 +1 @@
# Changelog

## Unreleased

- Documentation migrated to MkDocs Material.
- Added API reference through mkdocstrings.
- Added provenance and citation documentation.

## v2.0.0 — Core-only release

- Removed single-cell / scanpy / AnnData wrappers (now a standalone geometry toolkit).
- Core API unchanged: `TopOGraph`, spectral scaffolds, graph operators, layouts,
metrics, plotting.
--8<-- "CHANGELOG.md"
99 changes: 0 additions & 99 deletions mkdocs.yml

This file was deleted.

7 changes: 4 additions & 3 deletions src/topo/layouts/map.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,12 @@ def fuzzy_embedding(
euclidean_output : bool, default=True
Whether to use the faster code specialised for euclidean output metrics
save_every : int or None, optional
If provided and >0, store the embedding every `save_every` epochs into
If provided and >0, store the final embedding into
`aux_data["checkpoints"]` as a list of dicts:
[{"epoch": e, "embedding": Y_e}, ...]
WARNING: storing many snapshots can be memory intensive. Consider
passing `save_callback` to stream snapshots to disk.
Per-epoch checkpointing is not implemented since layout optimization
is delegated to `umap-learn`; at most the initial (epoch 0) and final
snapshots are captured.
save_limit : int or None, optional
Maximum number of snapshots to keep in-memory in `aux_data`.
If exceeded, the earliest snapshots are discarded (FIFO).
Expand Down
26 changes: 16 additions & 10 deletions src/topo/layouts/projector.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from topo.base.ann import kNN
from topo.layouts.isomap import Isomap
from topo.layouts.map import fuzzy_embedding
from topo.spectral.eigen import EigenDecomposition
from topo.spectral import LE
from topo.tpgraph.kernels import Kernel
from topo.utils._utils import get_landmark_indices

Expand Down Expand Up @@ -73,10 +73,11 @@ class Projector(BaseEstimator, TransformerMixin):

projection_method : str, default='Isomap'
Which projection method to use. `UMAP` delegates to `umap-learn`; `MAP`
is TopoMetry's local checkpoint-aware graph-layout optimizer. Current options are:
is TopoMetry's graph-layout optimizer (optional initial/final snapshot
capture). Current options are:
* 'Isomap' - one of the first manifold learning methods
* ['t-SNE'](https://github.com/DmitryUlyanov/Multicore-TSNE) - a classic manifold learning method
* 'MAP' - local checkpoint-aware graph-layout optimization
* 'MAP' - graph-layout optimization with initial/final snapshot capture
* ['UMAP'](https://umap-learn.readthedocs.io/en/latest/index.html) - upstream `umap-learn` estimator
* ['PaCMAP'](http://jmlr.org/papers/v22/20-1061.html) (Pairwise-controlled Manifold Approximation and Projection) - for balanced visualizations
* ['TriMAP'](https://github.com/eamid/trimap) - dimensionality reduction using triplets
Expand Down Expand Up @@ -125,11 +126,11 @@ def __init__(
keep_estimator=False,
random_state=None,
verbose=False,
# ---- NEW: checkpointing passthrough to MAP ----
save_every=None, # int or None: store Y every `save_every` epochs
# ---- checkpointing passthrough to MAP ----
save_every=None, # int or None: store final MAP embedding in aux checkpoints
save_limit=None, # cap snapshots kept in memory
save_callback=None, # callable(epoch:int, Y:np.ndarray) -> None
include_init_snapshot=True, # store epoch=0 (post-init) snapshot
include_init_snapshot=True, # store epoch=0 snapshot before SGD
):
self.n_components = n_components
self.metric = metric
Expand Down Expand Up @@ -317,17 +318,22 @@ def fit(self, X: np.ndarray | csr_matrix | Kernel, **kwargs: Any) -> "Projector"
if self.init == "spectral":
try:
self.init_Y_ = np.asarray(
EigenDecomposition(
n_components=self.n_components
).fit_transform(K)
LE(
K,
n_eigs=int(self.n_components),
laplacian_type="normalized",
drop_first=True,
return_evals=False,
),
dtype=np.float32,
)
except Exception:
warnings.warn(
"Spectral layout initialization failed, falling back to random initialization..."
)
self.init_Y_ = self.random_state.randn(
_n_rows(K, "projection graph"), self.n_components
)
).astype(np.float32)
else:
self.init_Y_ = self.random_state.randn(
_n_rows(K, "projection graph"), self.n_components
Expand Down
2 changes: 1 addition & 1 deletion src/topo/spectral/eigen.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def eigendecompose(
G_csr,
k=k,
which="LM" if largest else "SM",
tol=float(eigen_tol), # type: ignore
tol=_eigsh_tol(eigen_tol),
)
except ArpackError as exc:
raise RuntimeError(
Expand Down
Loading