Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
103 commits
Select commit Hold shift + click to select a range
b6d85a0
docs(20): capture phase context
edyoshikun Feb 22, 2026
3a54c8c
docs(20): create phase plan for experiment configuration
edyoshikun Feb 22, 2026
142b1a4
test(20-01): add failing tests for ExperimentConfig and ExperimentReg…
edyoshikun Feb 22, 2026
8bda967
feat(20-01): implement ExperimentConfig and ExperimentRegistry
edyoshikun Feb 22, 2026
4f2d772
refactor(20-01): clean up imports and exclude stale dynacrl workspace…
edyoshikun Feb 22, 2026
afff429
docs(20-01): complete ExperimentConfig/ExperimentRegistry plan
edyoshikun Feb 22, 2026
3ca1ebb
feat(20-02): add explicit deps and top-level experiment API exports
edyoshikun Feb 22, 2026
3e68cc1
feat(20-02): add example multi-experiment YAML configuration
edyoshikun Feb 22, 2026
b7e7629
docs(20-02): complete package wiring and example config plan
edyoshikun Feb 22, 2026
2471288
docs(phase-20): complete phase execution
edyoshikun Feb 22, 2026
293cb44
docs(21): create phase plan for Cell Index & Lineage
edyoshikun Feb 22, 2026
03bee1a
test(21-01): add failing tests for MultiExperimentIndex
edyoshikun Feb 22, 2026
680694b
feat(21-01): implement MultiExperimentIndex with lineage and border c…
edyoshikun Feb 22, 2026
98dc7a6
refactor(21-01): fix lint issues and export MultiExperimentIndex
edyoshikun Feb 22, 2026
9c25895
docs(21-01): complete MultiExperimentIndex plan summary and state update
edyoshikun Feb 22, 2026
2dbc359
test(21-02): add failing tests for valid anchors, properties, and sum…
edyoshikun Feb 22, 2026
9c6408a
feat(21-02): implement valid_anchors, experiment_groups, condition_gr…
edyoshikun Feb 22, 2026
ad55374
docs(21-02): complete valid anchors plan
edyoshikun Feb 22, 2026
bbd273f
docs(22): research batch sampling phase domain
edyoshikun Feb 22, 2026
e85b3bb
docs(22): create phase plan for batch sampling
edyoshikun Feb 22, 2026
f12e128
test(22-01): add failing tests for FlexibleBatchSampler
edyoshikun Feb 23, 2026
fe38805
feat(22-01): implement FlexibleBatchSampler with experiment-aware, co…
edyoshikun Feb 23, 2026
4b89f53
refactor(22-01): export FlexibleBatchSampler from viscy_data package
edyoshikun Feb 23, 2026
45ec057
docs(22-01): complete FlexibleBatchSampler core plan
edyoshikun Feb 23, 2026
7a40b6f
test(22-02): add failing tests for temporal enrichment, DDP coverage,…
edyoshikun Feb 23, 2026
7de55ee
feat(22-02): implement temporal enrichment, validation guards, DDP co…
edyoshikun Feb 23, 2026
fc2b67a
docs(22-02): complete temporal enrichment + DDP plan
edyoshikun Feb 23, 2026
76c35e8
docs(phase-22): complete batch sampling phase execution
edyoshikun Feb 23, 2026
a22b238
docs(23): create phase plan for Loss & Augmentation
edyoshikun Feb 23, 2026
6c3aac6
test(23-01): add failing tests for NTXentHCL
edyoshikun Feb 23, 2026
0b497e2
test(23-02): add failing tests for ChannelDropout and variable tau sa…
edyoshikun Feb 23, 2026
048d0fa
feat(23-02): implement ChannelDropout and variable tau sampling
edyoshikun Feb 23, 2026
b36e614
feat(23-01): implement NTXentHCL with hard-negative concentration
edyoshikun Feb 23, 2026
358ee57
refactor(23-02): add ChannelDropout and sample_tau to package exports
edyoshikun Feb 23, 2026
ebed6e9
docs(23-02): complete ChannelDropout and tau sampling plan
edyoshikun Feb 23, 2026
c1ee092
docs(23-01): complete NTXentHCL loss plan
edyoshikun Feb 23, 2026
ec2f7c8
docs(phase-23): complete loss & augmentation phase execution
edyoshikun Feb 23, 2026
eae255c
docs(24): create phase plan
edyoshikun Feb 23, 2026
ec4aebb
test(24-01): add failing tests for MultiExperimentTripletDataset
edyoshikun Feb 23, 2026
835f1a8
feat(24-01): implement MultiExperimentTripletDataset with lineage-awa…
edyoshikun Feb 23, 2026
ae5a30d
refactor(24-01): add MultiExperimentTripletDataset to package exports
edyoshikun Feb 23, 2026
ee7aff7
docs(24-01): complete MultiExperimentTripletDataset plan
edyoshikun Feb 23, 2026
4f03d12
test(24-02): add failing tests for MultiExperimentDataModule
edyoshikun Feb 23, 2026
d874570
feat(24-02): implement MultiExperimentDataModule with experiment-leve…
edyoshikun Feb 23, 2026
5f0e743
refactor(24-02): add MultiExperimentDataModule to dynaclr package exp…
edyoshikun Feb 23, 2026
7d960fe
docs(24-02): complete MultiExperimentDataModule plan
edyoshikun Feb 23, 2026
e25a420
docs(phase-24): complete dataset & datamodule phase execution
edyoshikun Feb 23, 2026
8b53050
docs(25): create phase plan
edyoshikun Feb 24, 2026
c5d8cdf
docs(phase-25): complete integration phase plan
edyoshikun Feb 24, 2026
2cb0d5d
feat(25-01): add end-to-end multi-experiment integration tests
edyoshikun Feb 24, 2026
2d410b7
feat(25-01): add multi-experiment YAML config and class_path validati…
edyoshikun Feb 24, 2026
04babe8
docs(25-01): complete integration plan - milestone v2.2 complete
edyoshikun Feb 24, 2026
a233981
docs(phase-25): complete integration phase execution — v2.2 milestone…
edyoshikun Feb 24, 2026
66b910f
add the pseudotime evals
edyoshikun Feb 28, 2026
734b89a
re-structure pseudotime folder
edyoshikun Feb 28, 2026
f19ee07
add the linear classifier evals and restructure folder path
edyoshikun Feb 28, 2026
0ad7548
add evaluations to dynaclr package
edyoshikun Feb 28, 2026
976b312
cli and linear classifier init
edyoshikun Feb 28, 2026
abf0401
Merge remote-tracking branch 'origin/app-dynaclr' into dynav2
edyoshikun Mar 1, 2026
fde8a9c
Merge branch 'modular-viscy-staging' into dynav2
edyoshikun Mar 1, 2026
85fd1ac
fix: address PR review blockers (airtable security, tests, qc cleanup)
edyoshikun Mar 1, 2026
2fd68cc
fix tests
edyoshikun Mar 1, 2026
fbb5cac
add track timing backup, save the aucroc as metric for linear classif…
edyoshikun Mar 2, 2026
aa04052
consolidate appending column to anndata functionality from reduce dim…
edyoshikun Mar 2, 2026
62f5895
add DINOv3 to viscy-data
edyoshikun Mar 3, 2026
83ae7e9
refactor dynaclr app folder structure
edyoshikun Mar 3, 2026
72e69e2
move losses to viscy-models
edyoshikun Mar 3, 2026
0d04b35
data folder
edyoshikun Mar 3, 2026
9a28457
porting #360
edyoshikun Mar 3, 2026
767f90c
rname dino to foundation and support openphenom
edyoshikun Mar 4, 2026
08cb895
move shells cripts to the configs folder
edyoshikun Mar 4, 2026
bba3210
fix the import for opephenom
edyoshikun Mar 4, 2026
9799859
generalize the qc class
edyoshikun Mar 4, 2026
d5060f3
add tqdm as default instead of Rich. Rich doesnt show up on the stdou…
edyoshikun Mar 6, 2026
0c48c1e
de parallelize and default cosine distance for msd and knn for PHATE.
edyoshikun Mar 6, 2026
4708412
make a cli for anndata
edyoshikun Mar 6, 2026
a828d7b
add append to obs cli
edyoshikun Mar 6, 2026
4d8a74f
adding a cell index that standardizes and spits out parqet
edyoshikun Mar 7, 2026
154e765
add example cell index
edyoshikun Mar 7, 2026
97baeda
update multiexperiment datamodule
edyoshikun Mar 7, 2026
4f1e282
recipes
edyoshikun Mar 8, 2026
6e692f0
add parquet integration test for multi-experiment training
edyoshikun Mar 11, 2026
36fec3f
demo slurm code
edyoshikun Mar 6, 2026
e0950cb
obs should use fov_name and track_id and id as UUID
edyoshikun Mar 11, 2026
d87e34f
add marker, task and channels to compute the crossval.
edyoshikun Mar 11, 2026
f8e2b69
fix prediction remodeling analysis
edyoshikun Mar 11, 2026
7e57523
Merge branch 'app-dynaclr' of github.com:mehta-lab/VisCy into app-dyn…
edyoshikun Mar 12, 2026
7eb594f
Raw logits are now passed through sigmoid before computing binary_acc…
edyoshikun Mar 12, 2026
7abdf96
fix the dataset statistics
edyoshikun Mar 13, 2026
96ccd7a
add contributing and uv to claude.md
edyoshikun Mar 16, 2026
9153d24
pydantic >2.0
edyoshikun Mar 16, 2026
cb5b723
- Add `schemas.py` with shared Pydantic data models (FOVRecord, etc.)
edyoshikun Mar 16, 2026
649fb18
uvx precommit
edyoshikun Mar 16, 2026
081a411
refactor(viscy-data): replace condition_balanced with stratify_by i…
edyoshikun Mar 16, 2026
eb60763
add norm_meta batching helpers and triplet None guard
edyoshikun Mar 16, 2026
31dc6c1
- Apply _radians_to_degrees to shear_range (Kornia expects degrees,…
edyoshikun Mar 16, 2026
0b3ea18
extend DatasetRecord from FOVRecord in viscy-data schemas; add viscy-…
edyoshikun Mar 16, 2026
6c7a458
- ExperimentRegistry backed by Collection for per-experiment channe…
edyoshikun Mar 16, 2026
37fd754
pass batch_size to Lightning metric logging for correct step counts
edyoshikun Mar 16, 2026
0a3d2e6
update recipes and add sampling-strategies guide; add CLAUDE.md
edyoshikun Mar 16, 2026
80f59eb
update pseudotime and linear classifier scripts for renamed fields
edyoshikun Mar 16, 2026
3d6007e
add configs
edyoshikun Mar 16, 2026
fc08b62
physical nomrlaiztion and fix to mlp projetion layer for adapter.
edyoshikun Mar 17, 2026
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
129 changes: 129 additions & 0 deletions .ed_planning/physical_scale_normalization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Physical Scale Normalization + Microscope Metadata + Cross-Scope Finetuning

**Branch:** `app-dynaclr`
**Date:** 2026-03-17
**Status:** Implemented, pre-commit passing, unit tests passing

---

## Motivation

Two experiments from different microscopes contain the same biology but differ in:
1. **Pixel/voxel size** — different magnifications mean cells appear at different spatial scales
2. **Embedding space** — microscope-specific biases push same-biology cells apart in latent space

The fix is two-pronged:
- **Physical scale normalization** at read time — adjust the pixel window read from disk so that after rescaling the patch is always exactly the target spatial size. No padding, no empty borders.
- **Cross-scope contrastive finetuning** — finetune the projection MLP with cross-microscope positives (same condition + HPI window) mixed with temporal positives for regularization.

---

## Files Changed

| File | Change |
|---|---|
| `packages/viscy-data/src/viscy_data/collection.py` | Added `microscope`, `pixel_size_xy_um`, `pixel_size_z_um` to `ExperimentEntry` |
| `packages/viscy-data/src/viscy_data/_typing.py` | Added `microscope` to `CELL_INDEX_GROUPING_COLUMNS` |
| `packages/viscy-data/src/viscy_data/cell_index.py` | Added `microscope` to `CELL_INDEX_SCHEMA`; write from experiment in `build_timelapse_cell_index` |
| `packages/viscy-data/tests/test_cell_index.py` | Added `microscope: ""` to `_make_valid_df` fixture |
| `applications/dynaclr/src/dynaclr/data/experiment.py` | `reference_pixel_size_*` params; `scale_factors` computed field; fail-fast validation |
| `applications/dynaclr/src/dynaclr/data/index.py` | Pass `microscope` through `_load_experiment_fovs`; fill `microscope=""` in `_align_parquet_columns` for old parquets |
| `applications/dynaclr/src/dynaclr/data/dataset.py` | `_rescale_patch`; scale-adjusted read window in `_slice_patch`; `_find_cross_scope_positive`; `cross_scope_fraction` + `hpi_window` params; `microscope` in `_META_COLUMNS` |
| `applications/dynaclr/src/dynaclr/data/datamodule.py` | Pass `reference_pixel_size_*`, `cross_scope_fraction`, `hpi_window` through to registry and datasets |
| `applications/dynaclr/src/dynaclr/engine.py` | `freeze_backbone: bool = False`; `on_fit_start` freezes backbone params |
| `applications/dynaclr/configs/training/batch_correction_fit.yml` | New example finetuning config |
| `applications/dynaclr/tests/test_dataset.py` | `TestRescalePatch` (3 tests) + `TestCrossScopePositive` (4 tests) |

---

## Design Decisions

### `None` instead of `0.0` for pixel sizes
`pixel_size_xy_um`, `pixel_size_z_um`, `reference_pixel_size_xy_um`, `reference_pixel_size_z_um` all default to `None`.

- `None` = "not provided / no rescaling requested"
- `0.0` was considered but is physically nonsensical and ambiguous
- Fail-fast `ValueError` in `ExperimentRegistry.__post_init__` if reference size is set but any experiment is missing pixel sizes — catches misconfiguration at `setup()` time, before training starts

### Scale factor convention
```
scale = experiment_um / reference_um
```
- `scale > 1` → experiment has larger pixels → read fewer disk pixels to cover same physical area
- `scale = 1` → no-op (short-circuits interpolation entirely)
- Read window: `y_half = round((patch_size // 2) * scale_y)`
- After read: `F.interpolate(..., size=target, mode="nearest-exact")` back to exact target size

### Cross-scope positives
- `cross_scope_fraction: float = 0.0` — fraction of positives per batch that are cross-microscope
- Match criteria: different `microscope`, same `condition`, `|HPI_anchor - HPI_candidate| <= hpi_window`
- Falls back to temporal positive if no cross-scope candidate found
- Validation at dataset init: raises if `cross_scope_fraction > 0` and any experiment has `microscope = ""`

### Freeze backbone
- `freeze_backbone: bool = False` on `ContrastiveModule`
- Implemented in `on_fit_start` — freezes `self.model.backbone.parameters()`
- Only the projection MLP is updated during finetuning

---

## Backwards Compatibility

All new fields default to `None` / `""` / `0.0`:

| Scenario | Behaviour |
|---|---|
| Old collection YAML (no pixel sizes) | `scale_factors = 1.0` → read window unchanged, no rescaling |
| Old parquet (no `microscope` column) | `_align_parquet_columns` fills `microscope = ""` |
| `cross_scope_fraction = 0.0` (default) | Pure temporal positives — no change to existing behaviour |
| `freeze_backbone = False` (default) | No change to optimizer |

---

## Usage

### Collection YAML — add per experiment
```yaml
experiments:
- name: scope1_exp
microscope: "scope1"
pixel_size_xy_um: 0.2028
pixel_size_z_um: 0.5
...
- name: scope2_exp
microscope: "scope2"
pixel_size_xy_um: 0.1625
pixel_size_z_um: 0.5
...
```

### Datamodule config — enable rescaling + cross-scope finetuning
```yaml
data:
class_path: dynaclr.data.datamodule.MultiExperimentDataModule
init_args:
reference_pixel_size_xy_um: 0.2028 # one scope's pixel size as reference
reference_pixel_size_z_um: 0.5
cross_scope_fraction: 0.5
hpi_window: 1.0
```

### Engine config — freeze backbone for finetuning
```yaml
model:
class_path: dynaclr.engine.ContrastiveModule
init_args:
freeze_backbone: true
lr: 1.0e-5
ckpt_path: path/to/pretrained.ckpt
```

See full example: `applications/dynaclr/configs/training/batch_correction_fit.yml`

---

## TODO

- [ ] Decide whether to add `pixel_size_xy_um`, `pixel_size_z_um`, `microscope` fields to Airtable so `build-collection` can auto-populate them (currently must be filled manually in the YAML)
- [ ] Run `fast_dev_run` smoke test with `batch_correction_fit.yml` once a two-microscope dataset is available
- [ ] QC: verify `stratify_by=["condition", "microscope"]` produces balanced batches across scopes
76 changes: 1 addition & 75 deletions .planning/MILESTONES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## v1.0 — Transforms & Monorepo Skeleton

**Shipped:** 2026-01-29
**Phases:** 1-5 (4 executed, 1 deferred)
**Phases:** 1–5 (4 executed, 1 deferred)

**What shipped:**
- uv workspace scaffolding with `packages/` directory
Expand All @@ -18,77 +18,3 @@
- Phase 4: Documentation (Zensical + GitHub Pages)

**Last phase:** 5

---

## v1.1 — Extract viscy-data

**Shipped:** 2026-02-14
**Phases:** 6-9 (4 phases, 9 plans)

**What shipped:**
- `viscy-data` package extracted with src layout (15 modules, 4015 LOC)
- All 13 data modules migrated with clean import paths (45 public exports)
- No cross-package dependency on viscy-transforms
- Optional dependency groups: `[triplet]`, `[livecell]`, `[mmap]`, `[all]`
- Shared utilities extracted from hcs.py into _utils.py
- Lazy import pattern for optional deps with clear error messages
- All existing data tests passing (71 tests)
- Tiered CI: viscy-data 3x3 base + 1x1 extras

**Last phase:** 9

---

## v1.2 — Extract viscy-models

**Shipped:** 2026-02-13
**Phases:** 10-14 (5 phases, 9 plans)

**What shipped:**
- `viscy-models` package with 8 architectures organized by function (unet/, vae/, contrastive/)
- Shared components extracted to `_components/` (stems, heads, blocks)
- Full test coverage: migrated existing + new forward-pass tests for all models
- Pure nn.Module — no Lightning/Hydra coupling
- State dict key compatibility preserved for checkpoint loading
- CI includes viscy-models in test matrix

**Pending todo from v1.2:**
- Fix deconv decoder channel mismatch in UNeXt2UpStage (pre-existing bug, xfailed test)

**Last phase:** 14

---

## v2.0 — DynaCLR Application

**Shipped:** 2026-02-17
**Phases:** 15-17 (3 phases, manual execution)

**What shipped:**
- `viscy-utils` package — shared ML infrastructure (trainer, callbacks, evaluation, cli_utils)
- EmbeddingWriter callback for prediction writing
- Linear classifier evaluation pipeline (train, apply, config)
- Embedding visualization app (Plotly/Dash)
- cli_utils with format_markdown_table() and load_config()
- pyyaml added as dependency
- `applications/dynaclr` — DynaCLR self-contained application
- ContrastiveModule engine (LightningModule for time-aware contrastive learning)
- MultiModalContrastiveModule (cross-modal distillation)
- ClassificationModule (downstream supervised classification)
- `dynaclr` CLI with LazyCommand pattern:
- `train-linear-classifier` — train logistic regression on cell embeddings
- `apply-linear-classifier` — apply trained classifier to new embeddings
- `evaluation/linear_classifiers/` — dataset discovery, config generation, SLURM scripts
- `examples/configs/` — fit.yml, predict.yml, ONNX export, SLURM templates
- `examples/DynaCLR-DENV-VS-Ph/` — infection analysis demo (ImageNet vs DynaCLR)
- `examples/embedding-web-visualization/` — interactive Plotly/Dash visualizer
- `examples/DynaCLR-classical-sampling/` — pseudo-track generation from 2D segmentation
- `examples/vcp_tutorials/` — quickstart notebook and script
- Optional [eval] extras: anndata, natsort, wandb, scikit-learn, phate, umap-learn
- All YAML configs updated with new class_path imports (dynaclr.engine, viscy_models, viscy_data, viscy_transforms)
- All Python scripts updated with new import paths

**Last phase:** 17

---
Loading