Skip to main content

histotuner

Linux And O2 Install

PyPI does not allow published package metadata to hard-code direct CPU wheel URLs for PyTorch, so histotuner cannot enforce a CPU-only Linux PyTorch build at package metadata level. The clean pattern is to install the PyTorch flavor you want first, then install histotuner without re-resolving dependencies.

Recommended default CPU install on Linux:

pip install torch==2.11.0 torchvision==0.26.0 --index-url https://download.pytorch.org/whl/cpu
pip install --no-deps histotuner

Recommended HMS O2 / Linux GPU install for CUDA 12.8:

conda create -n histotuner python=3.13 -y
conda activate histotuner
conda config --env --set channel_priority strict

conda install -c rapidsai -c conda-forge -c nvidia -c nodefaults \
  "cuda-version=12.8" "cuda-bindings>=12.8,<13" "cuda-python>=12.8,<13" \
  "cuda-core>=0.3,<0.4" "cuda-nvrtc=12.*" "cuda-nvrtc-dev=12.*" \
  "cuda-cudart=12.*" "cuda-cudart-dev=12.*" \
  cupy cuml=26.06 cudf=26.06 cugraph=26.06 rmm pylibraft \
  numpy scipy=1.16.2 scikit-learn numba umap-learn \
  anndata dask geopandas leidenalg matplotlib pandas pillow psutil pyqt pyyaml \
  python-igraph shapely spatialdata tifffile tqdm zarr \
  -y

pip install histotuner --no-deps --upgrade

python -m pip install torch==2.11.0 torchvision==0.26.0 --index-url https://download.pytorch.org/whl/cu128

pip install spatialdata==0.8.0

python -c "import torch; print('GPU Available:', torch.cuda.is_available())"





python -m pip install cellpose magicgui napari opencv-python openslide-bin openslide-python timm transformers wandb




python - <<'PY'
mods = ["histotuner", "spatialdata", "anndata", "zarr", "cupy", "cuml", "cudf", "cugraph", "umap", "torch"]
for m in mods:
    try:
        mod = __import__(m)
        print(m, "OK", getattr(mod, "__version__", "unknown"))
    except Exception as e:
        print(m, "FAIL", type(e).__name__, e)

import cupy, torch, histotuner as ht
print("cupy devices:", cupy.cuda.runtime.getDeviceCount())
print("torch cuda:", torch.cuda.is_available(), torch.cuda.device_count())
print(ht.umap_backend_status())
PY

To use a different GPU PyTorch build, replace the PyTorch index, for example cu126 or cu128, before installing histotuner --no-deps.

If you do use a plain pip install histotuner, the exact PyTorch wheel flavor chosen on Linux depends on pip resolution and the available package indexes in that environment.

GPU UMAP and Clustering on Linux

histotuner installs CPU UMAP support through umap-learn. GPU UMAP is optional because it depends on the local CUDA driver/toolkit stack and should be installed separately from the package dependencies in pyproject.toml.

GPU UMAP uses RAPIDS cuML when both cuml and cupy are available in the active Python environment and a CUDA-capable NVIDIA GPU is visible.

The same optional GPU stack is also used by native clustering:

  • ht.umap(...)
  • ht.leiden(...)
  • ht.dbscan(...)
  • histotuner-leiden
  • histotuner-dbscan

Check the active environment from Python:

import histotuner as ht

ht.umap_backend_status()

Expected GPU-ready output has gpu_available: True, with both gpu_cuml and gpu_cupy set to True.

Recommended install path on Linux is to create a RAPIDS-compatible environment with the official RAPIDS install selector:

https://docs.rapids.ai/install/

To request an interactive O2 GPU session for testing:

srun --pty -p gpu_quad --gres=gpu:l40s:1 -c 8 --mem=64G --time=0-03:00 /bin/bash

Inside the allocation, activate the same environment used by batch jobs and verify both the Slurm GPU allocation and the Python RAPIDS stack:

module purge
module load gcc/14.2.0
source "$HOME/miniconda3/etc/profile.d/conda.sh"
conda activate histotuner
nvidia-smi
python - <<'PY'
import histotuner as ht
print(ht.umap_backend_status())
PY

If nvidia-smi sees a GPU but ht.umap_backend_status() reports gpu_cupy=False, gpu_cuml=False, or gpu_device_count=0, the conda environment is not GPU-ready. Inspect the CUDA/RAPIDS stack:

conda list | egrep 'cuda-bindings|cuda-python|cuda-core|cuda-cudart|cuda-version|cuml|cudf|cupy'
python -m pip show cuda-bindings cuda-python cuda-core

A common failure is a pip-installed CUDA 13 cuda-bindings package mixed with conda CUDA 12/RAPIDS packages, for example cuda-bindings 13.x with cuda-python 12.9 and RAPIDS cuda12. Fix it by removing the pip binding and reinstalling CUDA bindings from conda:

python -m pip uninstall -y cuda-bindings
conda install -c conda-forge -c nvidia \
  "cuda-bindings>=12.9,<13" "cuda-python=12.9.*" \
  --force-reinstall

Then re-test:

python - <<'PY'
import cupy
print("cupy devices", cupy.cuda.runtime.getDeviceCount())
import cuml
print("cuml", cuml.__version__)
import histotuner as ht
print(ht.umap_backend_status())
PY

Keep CUDA/RAPIDS packages conda-managed in this environment; avoid installing cuda-* packages with pip after RAPIDS is installed.

For pip-based RAPIDS installs, choose wheels matching the installed CUDA major version and use the RAPIDS selector for the exact command:

https://docs.rapids.ai/install/

Then run UMAP with:

ht.umap(
    sdata=zarr_path,
    tableKeys=["mstar_tokens", "virchow2_tokens"],
    sample_n=25000,
    prefer_gpu="auto",  # uses GPU if RAPIDS is available, otherwise CPU
)

To require GPU and fail loudly if RAPIDS is not available:

ht.umap(
    sdata=zarr_path,
    tableKeys=["mstar_tokens", "virchow2_tokens"],
    sample_n=25000,
    prefer_gpu="gpu",
)

Native clustering uses the same prefer_gpu switch:

ht.leiden(
    sdata=zarr_path,
    tableKeys="tokens",
    obsm_key="X_umap",
    prefer_gpu="auto",
    target_col="leiden",
)
ht.dbscan(
    sdata=zarr_path,
    tableKeys="tokens",
    obsm_key="X_umap",
    prefer_gpu="gpu",
    target_col="dbscan",
)

CLI examples:

histotuner-leiden /path/to/sample.zarr \
  --tables tokens \
  --obsm-key X_umap \
  --prefer-gpu auto \
  --target-col leiden
histotuner-dbscan /path/to/sample.zarr \
  --tables tokens \
  --obsm-key X_umap \
  --prefer-gpu gpu \
  --target-col dbscan

Notes:

  • RAPIDS requires Linux or WSL2; native Windows Python environments generally cannot install/use cuML, cuGraph, or cuDF directly.
  • GPU UMAP uses cuml plus cupy.
  • GPU Leiden uses cudf, cugraph, cuml, and cupy.
  • GPU DBSCAN uses cuml plus cupy.
  • CUDA package suffixes must match the CUDA toolkit/driver stack in the environment. If installation fails, generate a fresh command from the RAPIDS selector for the specific Linux, Python, CUDA, and RAPIDS versions.

Supported token-extraction backends

histotuner can append multiple model-specific token tables into the same SpatialData Zarr while keeping shared geometry layers model-agnostic.

Currently supported token extractors:

  • hf-hub:bioptimus/H-optimus-1
  • hf-hub:MahmoodLab/UNI2-h
  • hf-hub:paige-ai/Virchow2
  • hf-hub:Wangyh/mSTAR
  • hf-hub:prov-gigapath/prov-gigapath
  • owkin/phikon-v2
  • MahmoodLab/conchv1_5
  • WenchuanZhang/Patho-CLIP-L
  • majiabo/GPFM
  • kaiko-ai/vitl14
  • xiangjx/musk

Token-grid semantics

All currently supported models export a unified 14x14 token grid so token tables can be compared directly across models.

  • phikon-v2 exports a native 14x14 patch-token grid.
  • hf-hub:bioptimus/H-optimus-1, hf-hub:Wangyh/mSTAR, and hf-hub:prov-gigapath/prov-gigapath export native 14x14 grids.
  • hf-hub:MahmoodLab/UNI2-h and hf-hub:paige-ai/Virchow2 have native 16x16 patch-token grids after special tokens are stripped, and histotuner adaptively average-pools them to 14x14.
  • conchv1_5 is special:
    • the native vision encoder runs at 448x448 with patch16
    • that produces a native 28x28 patch-token grid
    • histotuner average-pools each non-overlapping 2x2 token neighborhood to export a compatibility 14x14 token grid
  • Patho-CLIP-L is also special:
    • the native CLIP-L/14 vision encoder produces a 24x24 patch-token grid at 336x336 input resolution
    • histotuner adaptively average-pools that native 24x24 grid to export a compatibility 14x14 token grid
  • GPFM is also special:
    • the native DINOv2 ViT-L/14 encoder produces a 16x16 patch-token grid at 224x224 input resolution
    • histotuner adaptively average-pools that native 16x16 grid to export a compatibility 14x14 token grid
  • kaiko-ai/vitl14 is also special:
    • the native Kaiko ViT-L/14 encoder produces a 16x16 patch-token grid at 224x224 input resolution
    • histotuner uses the Kaiko preprocessing defaults (mean=std=0.5) and adaptively average-pools that native 16x16 grid to export a compatibility 14x14 token grid
  • xiangjx/musk is also special:
    • the native MUSK patch16 vision encoder produces a 24x24 patch-token grid at 384x384 input resolution
    • histotuner uses the MUSK preprocessing defaults (mean=std=0.5) and adaptively average-pools that native 24x24 grid to export a compatibility 14x14 token grid
    • MUSK is gated on Hugging Face and requires the optional official musk package

That pooling choice is deliberate so downstream single-cell workflows can consume every supported model through the same 14x14 token layout. For the pooled models, this is a compatibility semantic rather than the model's native tokenization:

  • UNI2-h and Virchow2: pooled from native 16x16
  • conchv1_5: pooled from native 28x28
  • Patho-CLIP-L: pooled from native 24x24
  • GPFM: pooled from native 16x16
  • kaiko-ai/vitl14: pooled from native 16x16
  • xiangjx/musk: pooled from native 24x24

Not yet supported for token extraction

  • none from the current requested set

O2 batch job generation

To generate one embedder.yaml and one embed_cluster.sh per sample folder on O2:

python generate_o2_jobs.py \
  --root-dir /n/scratch/users/a/ajn16/histotuner/full \
  --template-yaml embedder.yaml \
  --template-shell embed_cluster.sh \
  --output-dir /n/scratch/users/a/ajn16/histotuner/generated_jobs

  python generate_o2_jobs.py \
  --root-dir /n/scratch/users/a/ajn16/histotuner/heonly \
  --template-yaml embedder_HEonly.yaml \
  --template-shell embed_cluster_HEonly.sh \
  --output-dir /n/scratch/users/a/ajn16/histotuner/generated_jobs

To preview the sbatch submissions for the generated job scripts:

python submit_generated_jobs.py \
  --generated-dir /n/scratch/users/a/ajn16/histotuner/generated_jobs \
  --dry-run

Melanocyte UMAP/DBSCAN token pipeline

The scripts in o2/melanocyte_dbscan/ find SpatialData .zarr stores under a folder, map tokens to cells, compute a global UMAP for native token tables, map broad phenotype labels onto token tables, compute a second melanocyte-only UMAP, compute cell-type separability from phenotype_broad, and run DBSCAN on X_umap_melanocytes for tokens where phenotype_broad == "Melanocytes". They then generate thumbnail PDFs for dbscan_melanocytes_umap using an HE image auto-detected beside each .zarr, and save UMAP plots for each token table/model:

  • X_umap/phenotype_broad.png, excluding -1 and 0
  • X_umap_melanocytes/phenotype_broad.png, subset to Melanocytes
  • X_umap_melanocytes/dbscan_melanocytes_umap.png, excluding -1 and nan

Run a local dry-run first:

python .\o2\melanocyte_dbscan\run_token_umap_melanocyte_dbscan.py `
  "C:\Users\aj\Downloads\test" `
  --recursive `
  --dry-run

Run the local pipeline and write a summary:

python -u .\o2\melanocyte_dbscan\run_token_umap_melanocyte_dbscan.py `
  "C:\Users\aj\Downloads\test" `
  --recursive `
  --continue-on-error `
  --summary-json "C:\Users\aj\Downloads\test\melanocyte_dbscan_summary.json"

The script is intentionally a thin wrapper around histotuner package APIs. It uses native histotuner readers and writers directly and does not monkey-patch SpatialData, AnnData, UMAP, DBSCAN, or plotting internals. O2/runtime errors should be fixed in the package rather than patched inside this wrapper.

The script uses native histotuner token-table selection. By default, tokenCellMapper and phenotypeCellMapper auto-detect token tables, while UMAP and DBSCAN use the native tokens selector. Melanocyte DBSCAN uses --dbscan-min-samples 100 by default. Pass --no-thumbnail-pdfs to skip PDF generation, or --thumbnail-image-path /path/to/image.ome.tiff to provide an explicit image for a single-sample run. Pass --no-umap-plots to skip the saved UMAP plots. The pipeline runs ht.repairSpatialDataTableRegistry(...) before any per-zarr pipeline step, after global UMAP writes, after melanocyte UMAP writes, and after DBSCAN writes so on-disk tables are re-registered before downstream plotting/PDF steps. The repair function now detects Zarr v2 stores, writes Zarr v3 metadata with zarr.metadata.migrate_v3.migrate_v2_to_v3(...), and then applies the Zarr v3 table registry repair. Existing Zarr v3 stores skip migration and go straight to the v3 repair path. Cell-type separability is enabled by default with the batch-notebook defaults: phenotype_broad, max_tokens_per_cell_type=10000, max_comparison_tokens_per_cell_type=10000, initial_sample_size=200, bootstrap_repeats=100, target_relative_ci_width=0.05, and distance_metric="cosine". Per-sample output is written to <sample>/<sample>_cell_type_separability.csv. Pass --no-cell-type-separability to skip this step.

O2 parallel melanocyte DBSCAN jobs

If you are not pulling the full repo on O2, upload both files from o2/melanocyte_dbscan/ together:

  • run_token_umap_melanocyte_dbscan.py
  • submit_melanocyte_dbscan_jobs.py

The submitter copies the uploaded pipeline script into <output-dir>/scripts/run_token_umap_melanocyte_dbscan.py and points every generated Slurm job at that staged copy. This avoids accidentally running an older script from a previous upload or from a different folder.

On O2, generate one Slurm script per sample/zarr without submitting:

python o2/melanocyte_dbscan/submit_melanocyte_dbscan_jobs.py \
  --root-dir /n/scratch/users/a/ajn16/he_embed \
  --output-dir /n/scratch/users/a/ajn16/melanocyte_dbscan_jobs

Submit a single test job:

python o2/melanocyte_dbscan/submit_melanocyte_dbscan_jobs.py \
  --root-dir /n/scratch/users/a/ajn16/he_embed \
  --output-dir /n/scratch/users/a/ajn16/melanocyte_dbscan_jobs_test \
  --limit 1 \
  --submit

Submit all jobs:

python o2/melanocyte_dbscan/submit_melanocyte_dbscan_jobs.py \
  --root-dir /n/scratch/users/a/ajn16/he_embed \
  --output-dir /n/scratch/users/a/ajn16/melanocyte_dbscan_jobs \
  --submit

Submit all jobs with a smaller resource request:

python o2/melanocyte_dbscan/submit_melanocyte_dbscan_jobs.py \
  --root-dir /n/scratch/users/a/ajn16/he_embed \
  --output-dir /n/scratch/users/a/ajn16/melanocyte_dbscan_jobs \
  --cpus 8 \
  --mem 64G \
  --time 0-03:00 \
  --submit

Replace /n/scratch/users/a/ajn16/he_embed with the O2 path containing the sample folders or .zarr stores. Each submitted job runs the melanocyte DBSCAN pipeline on one sample folder, so samples run in parallel through Slurm. The --output-dir folder stores generated Slurm scripts, logs, per-job summary JSON files, and submission_manifest.csv. The main analysis outputs are written beside each sample .zarr under --root-dir, and each .zarr is updated in-place. After submitting, the job log should include:

[pipeline] Version: melanocyte_dbscan_native_package_v1_2026_07_24

If those lines are absent, the job is still using an old pipeline script. Regenerate the jobs by rerunning submit_melanocyte_dbscan_jobs.py; already submitted Slurm jobs will not change retroactively. Thumbnail PDFs are generated by default in each sample job; pass --no-thumbnail-pdfs to o2/melanocyte_dbscan/submit_melanocyte_dbscan_jobs.py to disable them. UMAP plots are also generated by default; pass --no-umap-plots to disable them. Cell-type separability is generated by default; pass --no-cell-type-separability to disable it. The submission manifest is written to /n/scratch/users/a/ajn16/melanocyte_dbscan_jobs/submission_manifest.csv.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

histotuner-0.4.6.tar.gz (229.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

histotuner-0.4.6-py3-none-any.whl (246.4 kB view details)

Uploaded Python 3

File details

Details for the file histotuner-0.4.6.tar.gz.

File metadata

  • Download URL: histotuner-0.4.6.tar.gz
  • Upload date:
  • Size: 229.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.13

File hashes

Hashes for histotuner-0.4.6.tar.gz
Algorithm Hash digest
SHA256 7c2694da950770f5dc1625962b25e4acfc7a094d4941af9cf7a01e37201469e1
MD5 a911a05e816e2daf9eb6676e2b86dfc8
BLAKE2b-256 00e29f052ae0a1d389ad967626547b45147efb19d146a49fc473370800f42946

See more details on using hashes here.

File details

Details for the file histotuner-0.4.6-py3-none-any.whl.

File metadata

  • Download URL: histotuner-0.4.6-py3-none-any.whl
  • Upload date:
  • Size: 246.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.13

File hashes

Hashes for histotuner-0.4.6-py3-none-any.whl
Algorithm Hash digest
SHA256 7f0e23f542b19e177b3bdc85ea6012ffbcf0c8fd7c60c3c992a590ab911a79a6
MD5 c8f0dd9de0a2cbc93bf88874748e8f47
BLAKE2b-256 9f241fc812236c3e75e99c4af60847efe9a8de906c76768bae30b1486d05c7b1

See more details on using hashes here.

Release history Release notifications | RSS feed

0.4.9

2 files

0.4.8

2 files

0.4.7

2 files

This release

0.4.6 This release

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.4.1

2 files

0.4.0

2 files

0.3.23

2 files

0.3.22

2 files

0.3.21

2 files

0.3.20

2 files

0.3.19

2 files

0.3.18

2 files

0.3.17

2 files

0.3.16

2 files

0.3.15

2 files

0.3.14

2 files

0.3.13

2 files

0.3.12

2 files

0.3.11

2 files

0.3.10

2 files

0.3.9

2 files

0.3.8

2 files

0.3.7

2 files

0.3.6

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.9

2 files

0.2.8

2 files

0.2.7

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page