Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
788 commits
Select commit Hold shift + click to select a range
6beffa0
fix(dynacell): gate the backfill on mean PCC, not per-row PCC
alxndrkalinin Aug 3, 2026
0669bda
docs(dynacell): record why a staleness gate compares mean PCC
alxndrkalinin Aug 3, 2026
5a25142
feat(dynacell): give the __bf train sets their own grouped-eval buckets
alxndrkalinin Aug 3, 2026
686dd0c
feat(dynacell): add the 32 brightfield-ablation predict leaves
alxndrkalinin Aug 3, 2026
8428ce0
feat(dynacell): fold celldiff_2d into the 12 in-focus 2D grouped buckets
alxndrkalinin Aug 4, 2026
c2d0481
feat(viscy-models): let the PatchGAN discriminator preserve the Z axis
alxndrkalinin Aug 4, 2026
de9ad32
feat(dynacell): add the pix2pix2d in-focus track and its 12 fit leaves
alxndrkalinin Aug 4, 2026
a679895
fix(dynacell): move the joint nucleus/membrane FCMAE fits to the 7-da…
alxndrkalinin Aug 4, 2026
c534ef9
test(dynacell): license the pix2pix2d 4-GPU widening with its measure…
alxndrkalinin Aug 4, 2026
8ef96ee
feat(dynacell): add the pix2pix2d nucleus predict leaves for the two …
alxndrkalinin Aug 4, 2026
847c60b
feat(dynacell): add the pix2pix2d A549-trained membrane predict leaves
alxndrkalinin Aug 4, 2026
304736b
feat(dynacell): add the pix2pix2d iPSC-trained membrane predict leaves
alxndrkalinin Aug 4, 2026
5307307
feat(dynacell): add the pix2pix2d joint-nucleus and A549-ER predict l…
alxndrkalinin Aug 4, 2026
755fac9
feat(dynacell): add the 4 pix2pix2d TOMM20 A549-trained predict leaves
alxndrkalinin Aug 4, 2026
2cffa41
fix(dynacell): emit the brightfield grouped-eval buckets, not just pa…
alxndrkalinin Aug 4, 2026
2e1abdd
feat(dynacell): add the 4 pix2pix2d TOMM20 iPSC-trained predict leaves
alxndrkalinin Aug 4, 2026
dd54bac
feat(dynacell): add the 4 pix2pix2d SEC61B joint-trained predict leaves
alxndrkalinin Aug 4, 2026
9c9073d
feat(dynacell): add the 4 pix2pix2d TOMM20 joint-trained predict leaves
alxndrkalinin Aug 4, 2026
eabb367
refactor(dynacell): derive _TRAIN_SETS from the bucket table
alxndrkalinin Aug 4, 2026
968535b
docs(dynacell): point pix2pix2d leaves at the overlay's sliding-windo…
alxndrkalinin Aug 4, 2026
272051c
fix(viscy-utils): compute MS-SSIM outside autocast so 16-mixed cannot…
alxndrkalinin Aug 5, 2026
ff1e29a
feat(dynacell): add the pix2pix2d joint-membrane predict leaves
alxndrkalinin Aug 5, 2026
7ac29ee
feat(dynacell): fold pix2pix2d into the 12 narrowed 2D grouped-eval b…
alxndrkalinin Aug 5, 2026
be5d243
test(dynacell): give the cache-reuse fixture both pixel scalings
alxndrkalinin Aug 5, 2026
050ea6a
feat(dynacell): stamp and gate the cubic version behind eval metrics
alxndrkalinin Aug 5, 2026
fdd74a0
fix(dynacell): default the eval launcher to the declared cubic version
alxndrkalinin Aug 5, 2026
5b3df33
test(dynacell): pin instance AP to mask_metrics.csv
alxndrkalinin Aug 5, 2026
8fd2695
docs(dynacell): document benchmark validation split
alxndrkalinin Aug 6, 2026
22cec5a
docs(dynacell): align overview with camera-ready benchmark
alxndrkalinin Aug 6, 2026
3217d8e
feat(dynacell): evaluate pix2pix3d in the mito iPSC-trained bucket
alxndrkalinin Aug 6, 2026
967ab4c
feat(dynacell): add the narrowed ER joint FCMAE rescore eval bucket
alxndrkalinin Aug 7, 2026
02543fe
feat(dynacell): add the narrowed membrane joint FCMAE rescore eval bu…
alxndrkalinin Aug 7, 2026
c67256b
feat(dynacell): add the four brightfield-trained grouped eval buckets
alxndrkalinin Aug 29, 2026
2066caf
fix(dynacell): resume from the newest last*.ckpt, not the fixed last.…
alxndrkalinin Aug 29, 2026
a3539fb
Merge origin/main into dynacell-models
alxndrkalinin Sep 1, 2026
b3db8b4
feat(dynacell): train the brightfield nucleus arm on the healthy FNet…
alxndrkalinin Sep 2, 2026
0617ed7
test(viscy-models): match the current patch-extent error message
alxndrkalinin Sep 2, 2026
6c78cba
style(dynacell): format generate_celldiff_trajectory_eval.py
alxndrkalinin Sep 2, 2026
1135607
fix(dynacell): stamp metrics provenance after the files it certifies
alxndrkalinin Sep 2, 2026
c15d1bf
fix(dynacell): key subtract_nuclei into the instance identity only fo…
alxndrkalinin Sep 2, 2026
ac0fe9f
fix(dynacell): fail clearly when segment() is asked for an instance-b…
alxndrkalinin Sep 2, 2026
c10fdc2
fix(dynacell): count reversals in the rollback dry run
alxndrkalinin Sep 2, 2026
494b820
fix(dynacell): resolve --ckpt last from the newest last*.ckpt too
alxndrkalinin Sep 2, 2026
4aacb8d
fix(dynacell): map both path grammars in the release checkpoint assem…
alxndrkalinin Sep 2, 2026
44b61c3
fix(dynacell): resolve a last*.ckpt pin by its own epoch/step, not by…
alxndrkalinin Sep 2, 2026
d1ea492
test(dynaclr): join annotations on the full fov_name, not its first c…
alxndrkalinin Sep 2, 2026
59343ad
fix(viscy-utils): floor the SSIM stability constants symmetrically
alxndrkalinin Sep 2, 2026
2e04bed
fix(dynacell): stop the croissant package __init__ requiring the opti…
alxndrkalinin Sep 2, 2026
1fc7ff9
fix(dynacell): reject a fit that adopts an EMA shadow without ema_kimg
alxndrkalinin Sep 2, 2026
56aa73c
fix(dynacell): index train splits against the pooled store they name
alxndrkalinin Sep 2, 2026
5573b8a
fix(dynacell): bind-free srun steps so a fragmented allocation can la…
alxndrkalinin Sep 2, 2026
cb04b0b
docs(dynacell): state the real VRAM floor the HEK constraint enforces
alxndrkalinin Sep 2, 2026
b5d6841
fix(dynacell): validate before truncating the _seg_cleaned output store
alxndrkalinin Sep 2, 2026
74b40c8
fix(dynacell): record the real grid stride instead of a hardcoded 2.0
alxndrkalinin Sep 2, 2026
f998ef4
fix(dynacell): merge MANIFEST.json instead of truncating it on each pack
alxndrkalinin Sep 2, 2026
f12a6c0
fix(dynacell): source croissant keywords and creators per dataset
alxndrkalinin Sep 2, 2026
99f1f74
fix(dynacell): route the croissant and distribute CLIs from __main__
alxndrkalinin Sep 2, 2026
a6ddada
fix(dynacell/eval): keep the cache manifest honest under exclude_fov_…
alxndrkalinin Sep 2, 2026
78ce963
fix(dynacell): bind-free srun in the array and serial batch launchers…
alxndrkalinin Sep 2, 2026
767bfe5
test(dynacell): point the batch fixture at leaves that share a run_root
alxndrkalinin Sep 2, 2026
33d2fee
ci(dynacell): run the batch launcher and release-path suites
alxndrkalinin Sep 2, 2026
dccde26
fix(dynacell): make --print-script in the batch launcher actually wri…
alxndrkalinin Sep 2, 2026
d8d30cb
test(dynacell): re-pin the spectral parity golden to cubic 0.9.0a1
alxndrkalinin Sep 2, 2026
dce42af
fix(dynacell): give sample-mode OZX archives their own destination
alxndrkalinin Sep 2, 2026
235cba7
fix(dynacell): carry voxel scale and position attrs into sampled OZX
alxndrkalinin Sep 2, 2026
8fa7638
fix(dynacell): raise when a Croissant doc has no distribution block
alxndrkalinin Sep 2, 2026
1be4466
docs(viscy-utils): correct the trailing-plane count for z_reduction='…
alxndrkalinin Sep 2, 2026
5c8c781
fix(dynacell): don't read an sstat counter reset as a stalled job
alxndrkalinin Sep 2, 2026
922e6c8
fix(dynacell): reject a --skip entry that matches no scanned directory
alxndrkalinin Sep 2, 2026
fd2a3ac
refactor(dynacell): drop redundant sys.path edits from the new tools …
alxndrkalinin Sep 2, 2026
197a29f
fix(dynacell): refuse to narrow a Cellpose label image past its dtype
alxndrkalinin Sep 2, 2026
eb9391b
fix(dynacell): classify migration moves against the post-move tree
alxndrkalinin Sep 2, 2026
73e9afd
fix(dynacell): publish a store's real shape range, not position 0's
alxndrkalinin Sep 2, 2026
1d7dd6d
fix(dynacell): stop the release-tool dry runs from writing to their o…
alxndrkalinin Sep 2, 2026
9ad5dd9
test(dynacell): make the GAN EMA and lazy-R1 tests exercise the engine
alxndrkalinin Sep 2, 2026
622fc47
feat(dynacell): add brightfield predicts for the FNet3D patch/aug arms
alxndrkalinin Sep 8, 2026
945743b
feat(dynacell): add a grouped eval bucket for the brightfield patch/a…
alxndrkalinin Sep 8, 2026
da531db
feat(dynacell): add W4 rescore buckets for the joint FCMAE nucleus an…
alxndrkalinin Sep 8, 2026
4918d2e
docs(viscy-models): drop the dangling plan reference in the MorphEm shim
alxndrkalinin Sep 8, 2026
e64cc1a
docs(dynacell): state the repository boundary for paper artifacts
alxndrkalinin Sep 9, 2026
9640537
fix(viscy-transforms): stop BatchedRandAffined blanking singleton-axi…
alxndrkalinin Sep 10, 2026
e7c49bb
fix(dynacell): bake the retrained 2D best checkpoints into the predic…
alxndrkalinin Sep 10, 2026
44110ec
fix(dynacell): repair the 3D predict leaves pinning rotated-away chec…
alxndrkalinin Sep 10, 2026
74fa18a
fix(dynacell): correct resume, cache, inference, and monitoring behav…
alxndrkalinin Sep 11, 2026
ff31563
docs(dynacell): label preprocess pipeline_ids as store-name stems
alxndrkalinin Sep 11, 2026
e03e5b2
docs(dynacell): describe a549_pack_ozx.yaml as distribute CLI defaults
alxndrkalinin Sep 11, 2026
6d46d76
fix(dynacell): align segmentation.yaml with run_cellpose_segmentation
alxndrkalinin Sep 11, 2026
13cb336
fix(dynacell): pin the MorphEm trust_remote_code model to a Hub commit
alxndrkalinin Sep 11, 2026
8cf19b5
fix(dynacell): bake the second wave of retrained 2D checkpoints
alxndrkalinin Sep 11, 2026
f5fb551
docs(dynacell): name assemble_pool as the a549_assemble_pool.yaml con…
alxndrkalinin Sep 11, 2026
5f218d8
fix(dynacell): bake the last two retrained 2D joint checkpoints
alxndrkalinin Sep 11, 2026
e1d75cf
docs: gate re-predict completeness on chunk mtime, not chunk count
alxndrkalinin Sep 11, 2026
b764ce9
fix(dynacell): force pred-side recompute in the 12 2D eval buckets
alxndrkalinin Sep 14, 2026
22efbd9
docs(dynacell): correct four leaves that say predict runs the raw gen…
alxndrkalinin Sep 15, 2026
f09a6ec
docs(dynacell): record the measured size of the EMA predict flip
alxndrkalinin Sep 15, 2026
684f768
build(hf_demo): pin cubic 0.9.0a1, two alphas behind everything else
alxndrkalinin Sep 15, 2026
a23257b
chore(dynacell): add gitignored experiments/ directory for investigat…
alxndrkalinin Sep 15, 2026
caff36e
docs(dynacell): state the experiments rule as a principle, not a path
alxndrkalinin Sep 15, 2026
39fc128
feat(dynacell): log flow-matching validation loss for CellDiff-2D fits
alxndrkalinin Sep 16, 2026
0a6d25b
feat(dynacell/tools): subset positions and re-key focus planes in the…
alxndrkalinin Sep 24, 2026
2bdb284
feat(dynacell): register DynaCell-lite as a separate test-only dataset
alxndrkalinin Sep 24, 2026
7ba7ec5
feat(dynacell/eval): switches to skip FID, PRC and MIND
alxndrkalinin Sep 24, 2026
078f8ed
fix(dynacell/eval): refuse per-position timepoint-count mismatches
alxndrkalinin Sep 24, 2026
8f73ba7
feat(dynacell): add voxel-matched FNet-2D ablation (nucleus + ER, iPSC)
alxndrkalinin Sep 25, 2026
8989335
feat(dynacell): extend DynaCell-lite configs to membrane, ER and mito
alxndrkalinin Sep 25, 2026
a701867
docs(dynacell): document DynaCell-lite and how to put a model on it
alxndrkalinin Sep 25, 2026
53344b8
feat(dynacell/tools): promote the DynaCell-lite subset simulator and …
alxndrkalinin Sep 25, 2026
7d90573
feat(dynacell/tools): run the lite comparison for every organelle
alxndrkalinin Sep 25, 2026
a5b1547
fix(dynacell/eval): stop ER/mito segmentation clipping the volume it …
alxndrkalinin Sep 25, 2026
ad61244
feat(dynacell/tools): register fnet2d_voxelmatched in the grouped-eva…
alxndrkalinin Sep 25, 2026
340e5a0
feat(dynacell): add predict leaves for the voxel-matched FNet-2D arm
alxndrkalinin Sep 25, 2026
27cc941
feat(dynacell): add focused eval buckets for the voxel-matched FNet-2…
alxndrkalinin Sep 25, 2026
9bbf97a
fix(dynacell/configs): point the CELL-Diff A549 movie leaves at a liv…
alxndrkalinin Sep 26, 2026
2b669f7
docs(dynacell/configs): correct the best-epoch note in the VSCyto3D m…
alxndrkalinin Sep 26, 2026
9d52125
test(dynacell/tools): match the lite comparison fixture to the Dice c…
alxndrkalinin Sep 26, 2026
40de793
feat(dynacell): Spotlight v2 segmentation-aware auxiliary loss across…
alxndrkalinin Sep 26, 2026
b7848f9
fix(dynacell/eval): score GLCM+/CP metrics in one GT-fit feature spac…
alxndrkalinin Sep 26, 2026
5636e70
fix(dynacell/eval): round CP intensity_min/max to float32 on both dev…
alxndrkalinin Sep 26, 2026
baa7ae0
fix(dynacell/eval): clip the CP shared-space z at ±20 before KID/FID/…
alxndrkalinin Sep 26, 2026
1597a34
build(dynacell): pin cubic 0.9.0a2 and keep 0.9.0a1 caches as measure…
alxndrkalinin Sep 29, 2026
18b6fe4
fix(dynacell/eval): fail the job on MicroMS3IM calibration errors ins…
alxndrkalinin Sep 29, 2026
e1f5ad8
fix(dynacell/configs): point the infection-finetune leaves at the ren…
alxndrkalinin Sep 29, 2026
2d1ed32
fix(dynacell/eval): drop constant GT slices from the MicroMS3IM calib…
alxndrkalinin Sep 29, 2026
c39d59f
build(dynacell/tools): let run_eval_direct evals use 40-48 GB GPUs ag…
alxndrkalinin Sep 29, 2026
a2e9c64
feat(dynacell): add sample-matched UNeXt2-2D ablation (nucleus + ER, …
alxndrkalinin Sep 29, 2026
7862ed6
feat(dynacell/tools): register the sample-matched UNeXt2-2D arm in th…
alxndrkalinin Sep 29, 2026
212c20c
feat(dynacell): add predict leaves for the sample-matched UNeXt2-2D arm
alxndrkalinin Sep 29, 2026
efc7fe3
feat(dynacell): add focused eval buckets for the sample-matched UNeXt…
alxndrkalinin Sep 29, 2026
ee810bc
fix(dynacell): pix2pix3d A549 eval targets + drop 1-voxel regions bef…
alxndrkalinin Sep 29, 2026
8b2413f
perf(viscy-utils): resolve package-level names lazily (#509)
alxndrkalinin Sep 30, 2026
d7f0015
feat(dynacell): Spotlight v2 arm leaves, controls and eval waves (#507)
alxndrkalinin Sep 30, 2026
d087d96
perf(dynacell/eval): keep torch off the light evaluation import path …
alxndrkalinin Sep 30, 2026
203fb3f
feat(dynacell): add Spotlight v2 wave2h eval buckets
alxndrkalinin Sep 30, 2026
e7475b3
feat(dynacell): add Spotlight v2 wave2j eval buckets
alxndrkalinin Sep 30, 2026
8e41498
fix(dynacell/eval): invalidate pred caches and metrics when the predi…
alxndrkalinin Sep 30, 2026
dfcf9a5
build: parallel test runs with pytest-xdist (#512)
alxndrkalinin Sep 30, 2026
3409560
test(viscy-transforms): make the Gaussian-noise statistics test deter…
alxndrkalinin Sep 30, 2026
095ccbb
build(dynacell): pin cubic 0.9.0a3, measured equivalent to 0.9.0a2 (#…
alxndrkalinin Sep 30, 2026
7ab3547
refactor(dynacell/eval): simplify the #511 prediction-provenance code…
alxndrkalinin Sep 30, 2026
3168f74
test(dynacell/eval): stub the metrics names added since #499 and #500…
alxndrkalinin Sep 30, 2026
92693ae
test(viscy-utils): raise the CLI subprocess timeout to 120 s
alxndrkalinin Sep 30, 2026
6fb9bd3
fix(dynacell/tools): label every release checkpoint as raw-trained
alxndrkalinin Oct 1, 2026
dbf6e64
feat(dynacell): add Spotlight v2 wave2k eval buckets
alxndrkalinin Oct 1, 2026
6ea0f32
feat(dynacell): add second segaux draws for UNeXt2-3D and pix2pix3d n…
alxndrkalinin Oct 1, 2026
b8060e3
feat(dynacell): add Spotlight v2 wave2l eval buckets
alxndrkalinin Oct 1, 2026
8c5c715
feat(dynacell): add Spotlight v2 wave2m eval buckets
alxndrkalinin Oct 1, 2026
72e87fc
feat(dynacell): add Spotlight v2 wave2i eval buckets
alxndrkalinin Oct 1, 2026
5c7bd4a
docs(dynacell): replace the lite UNetViT3D A549 caveat with measured …
alxndrkalinin Oct 1, 2026
9624d89
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 1, 2026
11e3d13
feat(dynacell): add Spotlight v2 arms on the FNet-3D augmentation recipe
alxndrkalinin Oct 1, 2026
ba71f4c
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 1, 2026
359ff43
feat(dynacell): foreground-limited pixel metrics (opt-in) (#517)
alxndrkalinin Oct 2, 2026
39caff8
feat(dynacell): add Spotlight v2 wave2n eval buckets
alxndrkalinin Oct 2, 2026
4450efd
perf(dynacell): route small-cohort FID through sample-space SVD (#518)
alxndrkalinin Oct 2, 2026
2798dee
perf(dynacell): use cubic 0.9.0a4 ER and mitochondria workflows (#519)
alxndrkalinin Oct 2, 2026
b0a91aa
fix(dynacell): fall back to CPU for ER/mito masks when no GPU is visible
alxndrkalinin Oct 3, 2026
5ee6af4
feat(dynacell): add Spotlight v2 wave2o eval buckets
alxndrkalinin Oct 3, 2026
759ac93
feat(dynacell): add Spotlight v2 wave2p eval buckets
alxndrkalinin Oct 4, 2026
4c66f0b
feat(dynacell): add Spotlight v2 wave2q eval buckets
alxndrkalinin Oct 4, 2026
6c28a31
feat(dynacell): Spotlight v2 topology losses (SAUNA-map Dice, soft-cl…
alxndrkalinin Oct 4, 2026
8f5fd35
Merge remote-tracking branch 'origin/dynacell-models' into dynacell-s…
alxndrkalinin Oct 5, 2026
aae4b00
feat(dynacell): add Spotlight v2 wave2r eval buckets
alxndrkalinin Oct 5, 2026
343f5d6
feat(dynacell): generate Spotlight v2 eval buckets for ER and mito
alxndrkalinin Oct 5, 2026
dc85180
feat(dynacell): add Spotlight v2 wave2s eval buckets
alxndrkalinin Oct 5, 2026
c02c0b2
feat(dynacell): add Spotlight v2 wave2t eval buckets
alxndrkalinin Oct 5, 2026
a5c4e88
feat(dynacell): add Spotlight v2 wave2u eval buckets
alxndrkalinin Oct 5, 2026
4cc614f
docs(dynacell/eval): document the segmentation backends per target
alxndrkalinin Oct 5, 2026
191ad49
docs(dynacell/eval): rewrite the eval README as a reference
alxndrkalinin Oct 5, 2026
188bfda
feat(dynacell): add Spotlight v2 wave2v eval buckets
alxndrkalinin Oct 5, 2026
d515545
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 5, 2026
6a2f0bc
feat(dynacell): add Spotlight v2 checkpoint-stage control buckets
alxndrkalinin Oct 5, 2026
484af70
docs(dynacell/eval): correct the nuclei_channel_name / nuclei_gt_path…
alxndrkalinin Oct 5, 2026
404c019
refactor(dynacell): run evals in the uv project environment, not a si…
alxndrkalinin Oct 5, 2026
ad23c0d
docs(dynacell): re-run dataset notebook against the v1.1 S3 release
alxndrkalinin Oct 5, 2026
bfbecc6
feat(dynacell): publish Pix2Pix3D checkpoints in the release zoo by d…
alxndrkalinin Oct 6, 2026
f047c97
docs(dynacell): link the released checkpoints from the README and not…
alxndrkalinin Oct 6, 2026
e706265
docs(dynacell): fix the README install instructions
alxndrkalinin Oct 6, 2026
31ce8df
docs(dynacell): add CITATION.cff and a Citation section for the paper
alxndrkalinin Oct 6, 2026
05718ec
feat(dynacell): add Spotlight v2 wave2w eval buckets
alxndrkalinin Oct 6, 2026
91fbb5e
feat(dynacell): add Spotlight v2 mito segaux checkpoint-stage control
alxndrkalinin Oct 6, 2026
7341cb3
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 6, 2026
4918261
dynacell eval: only_conditions for grouped leaves; remove dead single…
alxndrkalinin Oct 6, 2026
f8c830c
feat(dynacell): add Spotlight v2 wave2x eval buckets
alxndrkalinin Oct 6, 2026
681bd45
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 6, 2026
7b7cb43
dynacell HF demo: serve the released checkpoints and rebuild demo dat…
alxndrkalinin Oct 6, 2026
19765e7
perf(dynacell): CELL-Diff on DynaCell-lite in 1.43 h per fit on one H…
alxndrkalinin Oct 6, 2026
84cc1ae
feat(dynacell): let generate_trajectory take a sampler and time grid
alxndrkalinin Oct 6, 2026
6dc9124
perf(hf_demo): generate the CELL-Diff trajectory with the 14-evaluati…
alxndrkalinin Oct 7, 2026
53136e9
docs(dynacell): drop copy-paste comment artifacts from the brightfiel…
alxndrkalinin Oct 7, 2026
868d1db
docs(dynacell): stop hand-counting brightfield fits in the train-set …
alxndrkalinin Oct 7, 2026
c9c6914
docs(viscy-models): reflow the MorphEm shim comment
alxndrkalinin Oct 7, 2026
74ef75e
feat(dynacell): score ER/mito masks on the focus plane for the 2D ben…
alxndrkalinin Oct 7, 2026
2c1fde7
feat(dynacell): add slab3 focus-plane ER/mito buckets for the 2D benc…
alxndrkalinin Oct 7, 2026
b3cd3fe
feat(dynacell): add Spotlight v2 wave1 eval buckets
alxndrkalinin Oct 7, 2026
326efce
docs(dynacell): state the focus-aware projection defaults as they are
alxndrkalinin Oct 7, 2026
e190761
feat(dynacell): add slab3 phase-floor buckets for the 2D benchmark
alxndrkalinin Oct 7, 2026
8cc2de0
feat(dynacell): register the Track H CELL-Diff-2D background-target arms
alxndrkalinin Oct 7, 2026
3bad1a7
feat(dynacell): add Spotlight v2 wave2y eval buckets
alxndrkalinin Oct 7, 2026
bd5c674
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 7, 2026
a074d85
feat(dynacell): add Spotlight v2 wave2z eval buckets
alxndrkalinin Oct 7, 2026
2f35040
feat(dynacell): add Spotlight v2 wave3a and ctrl4 eval buckets
alxndrkalinin Oct 7, 2026
8685808
feat(dynacell): add Spotlight v2 wave3b eval buckets
alxndrkalinin Oct 7, 2026
d9f9d6a
feat(dynacell): add second segaux draws for pix2pix3d membrane and ER…
alxndrkalinin Oct 7, 2026
143d305
feat(dynacell): add Spotlight v2 wave3c eval bucket
alxndrkalinin Oct 7, 2026
0cbeda1
feat(dynacell): Spotlight v2 Track H background-target arms for CELL-…
alxndrkalinin Oct 7, 2026
89b3b31
feat(dynacell): add Spotlight v2 wave3d eval buckets
alxndrkalinin Oct 8, 2026
297d5f7
feat(dynacell): add Spotlight v2 wave3e eval buckets
alxndrkalinin Oct 9, 2026
70e89fb
fix(hf_demo): colour the plot titles from the browser's resolved theme
alxndrkalinin Oct 9, 2026
6ab8934
feat(dynacell): add Spotlight v2 wave3f eval bucket
alxndrkalinin Oct 10, 2026
add50a1
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 10, 2026
b0900cc
feat(dynacell): add the FNet-3D vscyto3daug nucleus loss-weight sweep…
alxndrkalinin Oct 10, 2026
d1fdc87
feat(dynacell): add SAUNA-weighted Dice arms for UNeXt2-3D and pix2pi…
alxndrkalinin Oct 10, 2026
1689036
feat(dynacell): add Spotlight v2 wave3h eval buckets
alxndrkalinin Oct 10, 2026
a4388e1
feat(dynacell): add Spotlight v2 wave3i eval bucket
alxndrkalinin Oct 10, 2026
1a81e0f
feat(dynacell): add Spotlight v2 wave3g eval bucket
alxndrkalinin Oct 10, 2026
476edc0
feat(dynacell): add Spotlight v2 wave3j eval bucket
alxndrkalinin Oct 10, 2026
e99b75c
feat(dynacell): register the mito segaux draw-2 epoch-76 control
alxndrkalinin Oct 10, 2026
e6d3ec4
feat(dynacell): add Spotlight v2 wave3k and ctrl5 eval buckets
alxndrkalinin Oct 10, 2026
57265bb
feat(dynacell): add Spotlight v2 wave3l eval bucket
alxndrkalinin Oct 10, 2026
2f9ef31
feat(dynacell): opt-in channels_last/compile_model flags and LinearLR…
alxndrkalinin Oct 12, 2026
8289d7b
feat(dynacell): add Spotlight v2 wave3m eval buckets
alxndrkalinin Oct 13, 2026
1f072d2
perf(dynacell): size the predict profiles from measurement, 32/256G -…
alxndrkalinin Oct 13, 2026
ed6b27d
refactor(dynacell): fold the six rescore buckets into only_conditions
alxndrkalinin Oct 13, 2026
3ae322e
test(dynacell): guard the generated/hand-written split in grouped leaves
alxndrkalinin Oct 13, 2026
3460c98
perf(dynacell): give the h200 predict leaves their own measured profile
alxndrkalinin Oct 13, 2026
ca2ffa2
feat(dynacell): add Spotlight v2 ctrl6 budget-control eval buckets
alxndrkalinin Oct 13, 2026
5ab98ee
feat(dynacell): add Spotlight v2 ctrl7 fork-control eval buckets
alxndrkalinin Oct 13, 2026
cb981ce
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 13, 2026
25fe29b
feat(dynacell): add Spotlight v2 wave3n and ctrl6b eval buckets
alxndrkalinin Oct 13, 2026
d2c6c57
feat(dynacell): add the pix2pix3d membrane SAUNA second-draw arm
alxndrkalinin Oct 13, 2026
41274b8
perf(dynacell): default 3-D FCMAE and FNet3D fits to the measured spe…
alxndrkalinin Oct 13, 2026
92dad73
docs(dynacell): state the speed flags once per family overlay
alxndrkalinin Oct 13, 2026
ebc7d32
test(dynacell): simplify the speed-flag composition test
alxndrkalinin Oct 13, 2026
481badf
docs(dynacell): state the weak UNeXt2-3D numerics evidence for benchmark
alxndrkalinin Oct 13, 2026
996adb4
feat(dynacell): 2D benchmark on one focus slab — slab3-only predict +…
alxndrkalinin Oct 14, 2026
a23b75c
feat(dynacell): add Spotlight v2 ctrl8 halfw checkpoint-stage eval bu…
alxndrkalinin Oct 14, 2026
e29f8f8
feat(dynacell): score interior-only foreground metrics by default (#527)
alxndrkalinin Oct 14, 2026
64d3a75
test(dynacell): register the 16 focus2d grouped buckets as hand-written
alxndrkalinin Oct 14, 2026
b5b7305
test(dynacell): drop never-committed buckets from the hand-written re…
alxndrkalinin Oct 14, 2026
711894f
feat(dynacell): add Spotlight v2 ctrl9 segaux checkpoint-stage eval b…
alxndrkalinin Oct 14, 2026
098150f
feat(dynacell): add Spotlight v2 ctrl9b segaux e76 zikv eval bucket
alxndrkalinin Oct 14, 2026
7a224b8
Merge branch 'dynacell-models' of https://github.com/mehta-lab/VisCy …
alxndrkalinin Oct 14, 2026
5eada9a
feat(dynacell): add a thin-wall Dice target arm for UNeXt2-3D membrane
alxndrkalinin Oct 15, 2026
050f553
feat(dynacell): segmenter-consistency fine-tune loss (P1, nucleus) (#…
alxndrkalinin Oct 16, 2026
9a79589
feat(dynacell): add warmup-matched pix2pix2d fit leaves
alxndrkalinin Oct 22, 2026
ca4b43f
feat(dynacell): add focus2d seed-replicate buckets for the 2D benchmark
alxndrkalinin Oct 22, 2026
dc87ddd
feat(dynacell): add predict leaves for the warmup-matched pix2pix2d fits
alxndrkalinin Oct 23, 2026
adf8c6d
feat(dynacell): add focus2d eval buckets for the warmup-matched pix2p…
alxndrkalinin Oct 23, 2026
6c83fff
perf(viscy-data,viscy-utils): read and write each predict volume once…
alxndrkalinin Oct 23, 2026
8e74a7e
perf(viscy-data,viscy-utils): blend on the GPU, normalize once, flush…
alxndrkalinin Oct 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [main, modular-viscy-staging]
pull_request:
branches: [main, modular-viscy-staging]
branches: [main, modular-viscy-staging, dynacell-models]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
38 changes: 33 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches: [main, modular-viscy-staging]
pull_request:
branches: [main, modular-viscy-staging]
branches: [main, modular-viscy-staging, dynacell-models]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:
working-directory: applications/${{ matrix.application }}

test-dynacell-configs:
name: Test dynacell benchmark configs (Python 3.13, ubuntu-latest)
name: Test dynacell configs and regressions (Python 3.13, ubuntu-latest)
runs-on: ubuntu-latest

steps:
Expand All @@ -139,10 +139,11 @@ jobs:
# (falls back to numpy/scikit-image without CUDA), so the eval extra
# installs and runs on a GPU-less runner. eval_gpu (cupy/cucim) is NOT
# installed — those are the only CUDA-only deps.
run: uv sync --frozen --extra eval --group test
# Include viscy-utils' test group for the prediction-writer fixtures.
run: uv sync --frozen --package dynacell --package viscy-utils --extra eval --group test
working-directory: applications/dynacell

- name: Run benchmark-schema + submit-tool + eval-runtime tests
- name: Run benchmark configs and pipeline regressions
# test_benchmark_config_composition + test_submit_benchmark_job cover
# config composition + the launcher. test_runtime + the two
# test_evaluation_pipeline_parallel* suites cover the runtime module,
Expand All @@ -152,14 +153,41 @@ jobs:
# segmenter + feature-extractor model loads — but pipeline import still
# needs the eval stack). test_evaluation_grouped drives the
# multi-condition driver against the same cache-only fixture.
# test_evaluation_microssim_calibration pins that MicroMS3IM calibration
# propagates OOM, drops constant GT slices from the fit pool, and scores
# NaN only on a degenerate GT pool (real cubic, CPU).
#
# test_submit_benchmark_batch covers the OTHER two srun emitters (array
# + chained-serial), which drifted out of sync with the single-job
# template because only the latter was under test. test_distribution,
# test_croissant and test_main_cli_routing cover the release path: the
# OZX packer / MANIFEST ledger, the per-dataset Croissant attribution,
# and the entry-point wiring that kept both of those CLIs reachable.
# All four are CPU-only and need no extra deps.
run: |
uv run --frozen pytest \
uv run --frozen --no-sync pytest \
tests/test_benchmark_config_composition.py \
tests/test_submit_benchmark_job.py \
tests/test_submit_benchmark_batch.py \
tests/test_runtime.py \
tests/test_evaluation_pipeline_parallel.py \
tests/test_evaluation_pipeline_parallel_cpu.py \
tests/test_evaluation_grouped.py \
tests/test_evaluation_microssim_calibration.py \
src/dynacell/evaluation/segmentation_test.py \
tests/test_distribution.py \
tests/test_croissant.py \
tests/test_main_cli_routing.py \
tests/test_engine.py \
tests/test_pipeline_cache.py \
tests/test_evaluation_fid_provenance.py \
src/dynacell/evaluation/feature_metrics_test.py \
src/dynacell/evaluation/provenance_test.py \
tools/watch_stalled_jobs_test.py \
../../packages/viscy-utils/tests/test_prediction_writer.py \
../../packages/viscy-utils/tests/test_prediction_metadata.py \
../../packages/viscy-utils/src/viscy_utils/callbacks/monitor_health_test.py \
../../packages/viscy-utils/src/viscy_utils/callbacks/linear_lr_cooldown_test.py \
-v
working-directory: applications/dynacell

Expand Down
136 changes: 67 additions & 69 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,7 @@

VisCy is a **uv workspace monorepo** for virtual staining and computational microscopy. Sub-packages live under `packages/`.

## Repo Layout

```
pyproject.toml # Root config (ruff, pytest, uv workspace)
packages/
viscy-data/ # Data loading and Lightning DataModules
viscy-models/ # Neural network architectures
viscy-transforms/ # Image transforms
src/viscy/ # Umbrella package (re-exports)
applications/ # Self-contained research applications
```

### Packages vs Applications
## Packages vs Applications

- **Shared code belongs in `packages/`**, not in applications.
- **Applications must not import from each other.** If two applications need the same logic, move it to an existing package or create a new one.
Expand All @@ -30,16 +18,6 @@ applications/ # Self-contained research applications

Use `uv` package manager. Run commands with `uv run <command>`. Edit `pyproject.toml` to modify dependencies and sync to update `uv.lock`.

```sh
uv venv -p 3.13
uv sync --all-packages --all-extras
```

If `uv` is not installed:
```sh
curl -LsSf https://astral.sh/uv/install.sh | sh
```

On HPC, symlink the uv cache out of your home directory first:
```sh
mkdir -p /hpc/mydata/firstname.lastname/.cache/uv && ln -s /hpc/mydata/firstname.lastname/.cache/uv ~/.cache/uv
Expand Down Expand Up @@ -77,6 +55,68 @@ When the user says "cancel all jobs," scope it to **batch jobs only**, never the

**Subagent prompts for job status:** ask for completeness vs config, not just liveness. A prompt like "check the liveness of wandb run X" returns `state: finished` for a SIGTERM'd run and reads as success. Phrase it as "is run X complete relative to its configured `max_epochs`, and what was the exit reason (clean finish, scancel, OOM, timeout, exception)?"

### Hung-but-allocated jobs

**A job can finish its work and never exit.** Job `35083019_0` (a `pix2pix3d` predict) wrote its last chunk at 2026-07-30 19:20, then held an A6000 + 32 CPUs + 256 GB for **17.5 h** doing nothing. `squeue` shows `RUNNING` with a climbing wall clock — indistinguishable from healthy compute — and the rich progress bar only reaches `.out` at exit, so the log looks normal too. Measured rate: 1 in 509 allocations over 26 days.

**The discriminator is CPU time, and only its derivative.** Across those 509 allocations every healthy job spent CPU at >= 0.93x wall; the hung one sat at 0.561 with a *zero* incremental rate. Absolute ratio alone is useless (a 16-CPU fit runs at ~7.8x wall), so compare two samples: `sstat -j <id> -a -P --format=JobID,AveCPU` — `-P` is mandatory, the default width truncates to `10-15:41:+` and misparses.

**A full-looking output store plus a `RUNNING` job is NOT this signature.** On a
re-predict launched with `--overwrite`, the writer rewrites chunks in place and the previous
run's chunks stay on disk, so the store reads 100% complete from the first second — 36 of 36
CellDiff-2D predicts looked finished-but-hung this way on 2026-09-11 while every one was
healthy at a 1.01x incremental CPU rate. Judge on the CPU derivative above, never on "the
output looks done"; see *Re-predict completeness* in `applications/dynacell/CLAUDE.md` for
the mtime-based gate. In the same direction, a single `find -newermt '-20 minutes'` returning
nothing is not evidence of a stall either: `/hpc` attribute caching hides fresh writes from a
login node, and listing real mtimes (`find -printf '%T@ %p' | sort -rn`) showed writes
seconds old at the same moment.

**Using the stall watchdog.** `applications/dynacell/tools/watch_stalled_jobs.py` automates exactly that comparison. Start it whenever a campaign has long jobs in flight and leave it running:

```sh
# one-shot check: exit 0 = no stall detected, exit 1 = something is stalled, exit 2 = tool error
uv run --no-sync python applications/dynacell/tools/watch_stalled_jobs.py --once

# continuous, 10-min poll (launch in the background; it runs until killed)
uv run --no-sync python applications/dynacell/tools/watch_stalled_jobs.py --interval 600
```

Quiet polls print `no stall detected, tracking N: <stepid>(<samples>) ...`, or `collecting history` when a step lacks a 15-minute baseline. A stalled step prints one `STALLED <stepid> <name> on <node>: wall Xh, cpu Yh, burned Z core-s/s over the last Wh` line per poll. Reading it:

- It **only reports — it never cancels.** Cancelling a job with `afterok` dependents strands them, so follow the kill order above by hand.
- **Interactive sessions are excluded by name** (`nomachine`, `gpu-hold`, `interactive`, bare `bash`/`sh`/`srun`). A renamed interactive session would get flagged — it still would not be cancelled, but don't act on the alert without checking.
- It needs **two samples >= 15 min apart** and a step **>= 30 min old**, so expect no verdict on a fresh step for the first couple of polls.
- Both modes persist samples in `$XDG_CACHE_HOME/viscy/watch_stalled_jobs-<user>@<hostname>.json` (default cache root: `~/.cache`), so each node keeps its own history by default; `--state-file` overrides the path. The file records the host that wrote it and refuses to load on any other, whatever its schema version: `~/.cache` is on NFS, where `flock` only excludes processes on the same node, so run the daemon and its `--once` checks on one node. A `--state-file` shared between nodes is claimed by the first host to create it (`O_EXCL`); a second host that started from the absent file fails on its first save instead of overwriting the winner's history. Repeat `--once` after at least 15 minutes. Its first invocation collects history; exit 0 does not establish that every job is healthy.
- Histories are **per SLURM step** (`<jobid>.<step>` from `squeue -s`), judged against that step's own elapsed time rather than the job's. A `submit_benchmark_batch` chain advancing to its next `srun` step, or a requeue reusing a step id, starts a fresh history with that step's own clocks; the previous step's CPU cannot establish progress for the new one. A step first seen mid-allocation is judged on its cumulative CPU over its own age, so a watcher started late still catches a step that was already hung. Only `.extern` is skipped: `.batch` is tracked like any other step, because `submit_benchmark_batch --parallel` and `run_eval_direct.slurm` run their compute directly in the batch script with no `srun` step; for `srun`-based jobs the batch step never burns CPU, so it never qualifies.
- A step that has not burned CPU **over a window of at least 30 minutes** is never flagged: startup NFS staging is legitimately ~0% CPU, and a short import-time burst must not qualify a step either. Qualification uses cumulative CPU over the step's age at the first sample taken once the step is >= 30 min old (whether or not that is the first sample), or CPU since first sight (only once >= 30 min have elapsed). A step that once qualified stays qualified through a long idle.
- `--user` defaults to `alex.kalinin`; `sstat` only works on your own running jobs, so it cannot watch someone else's.

**Predict wall limits are per family, sized from measurement** (`launcher_profiles/`):

| Profile | `time` | Basis |
|---|---|---|
| `hardware_predict_any_gpu.yml` | 2 days | longest single-pass predict measured 21.9 h |
| `hardware_predict_celldiff.yml` | 7 days | CELL-Diff runs 5.8-95.6 h; 8 predicts TIMEOUTed at the old 4-day cap on 2026-07-19 |
| `hardware_h200_single.yml` | 4 days | **shared with 40 fit leaves** — do not re-tune from predict data |

A new slow family gets its **own profile**; raising a shared cap to cover it makes the cap meaningless. `test_predict_leaf_wall_limit_matches_its_family` pins all 421 predict leaves to this table, and `generate_hek_predict_configs.py` picks the profile from `_HARDWARE_PROFILE` so regeneration cannot revert it.

**Fit wall limits, same rule.** `hardware_4gpu.yml` is 4 days; `hardware_4gpu_long.yml` is 7 days and carries **only** the four joint ER/mito FCMAE leaves, which each TIMEOUTed twice at 4 days. Measured 1.58-1.82 epochs/h ⇒ 110-126 h for `max_epochs: 200`. Joint fits are the slow case because they pool iPSC + A549, roughly doubling steps per epoch. A 7-day 4-GPU H100/H200 request backfills materially worse, so `test_only_measured_slow_fits_get_the_long_wall` guards both directions — add a leaf only with a measured epochs/h rate.

**Measure epochs/h from consecutive retained checkpoint mtimes**, not wall time ÷ steps. Each `epoch=N-step=M.ckpt` is an `(epoch, wall-clock)` pair, and two intervals inside one allocation cross-check each other (the ER-pretrained fit reproduced to three digits). Note `save_top_k` means the highest *retained* epoch is a best-by-monitor epoch, not progress — read `epoch` out of `last.ckpt` for that (`torch.load(..., mmap=True)`).

**Two traps when killing one:**
- **Lightning swallows SIGTERM.** `signal_connector.py` installs a handler that logs `Received SIGTERM` / `Bypassing SIGTERM` and sets a flag — it does not exit. `scancel` alone cannot stop a Lightning process outside its training loop; it dies on the KILL escalation (`ExitCode 0:9`), which took ~6 min here.
- **Clear dependents' `Dependency=` BEFORE `scancel`**, or `afterok` chains land in `DependencyNeverSatisfied` permanently. `scontrol update JobId=<dep> Dependency=` to drop it, or `Dependency=afterok:<other>` to re-point the chain.

**Forensics — capture this BEFORE cancelling, or the cause is unknowable:**
1. `grep State /proc/<pid>/status` and `cat /proc/<pid>/task/*/wchan` (both readable without root, `ptrace_scope=0` here). `State: D` names an uninterruptible storage/driver call; `S` means a Python-level block.
2. `py-spy dump --pid <pid>` (`uv tool install py-spy`) gives the full Python stack. If it errors `Failed to find python version from target process`, that is *itself* the answer: the process already reached interpreter finalization, so `trainer.predict()` returned and the writer's `plate.close()` completed.
3. **Do not rely on SIGABRT + `PYTHONFAULTHANDLER=1`** — verified to produce no dump once the interpreter is finalizing.

**Known unbounded wait in the predict teardown path:** zarr 3.2.1 `core/sync.py:89` registers `cleanup_resources` via `atexit`, which calls `_executor.shutdown(wait=True)` with no timeout — one stuck `zarr_pool` thread blocks interpreter exit forever at 0% CPU. (The same function caps its io-thread join at `timeout=0.2` "to avoid hanging"; the executor shutdown was left unbounded.) That is the proximate frame, but not the whole story here: a pure Python join still lets the SIGTERM handler run and log, and job `35083019_0` never logged it, so the terminal block was a C-level uninterruptible call underneath.

### Joint vs single-set training batch semantics

`HCSDataModule` and `BatchedConcatDataModule` produce the same number of GPU samples per training step — but the YAML `batch_size` value that gets there is **different by a factor of `num_samples`**. Easy to misread either by skimming.
Expand All @@ -98,25 +138,12 @@ Examples (verified against the `applications/dynacell/configs/benchmarks/virtual

When in doubt, read both `train_dataloader` overrides directly — they are short. Don't infer from comments alone.

### Common Commands

```sh
uvx ruff check packages/ # lint
uvx ruff check --fix packages/ # lint + auto-fix
uvx ruff format packages/ # format
uv run pytest # all tests
```

### Testing

```sh
uv run pytest # all tests
uv run pytest packages/viscy-data/ # single package (data)
uv run pytest packages/viscy-models/ # single package (models)
```

Prefer `{file}_test.py` in the same directory as `{file}.py`, unless there are import issues, in which case use `tests/`.

Run the suite in parallel with `pytest -n <cores>` (pytest-xdist, in the `test` group). The root `conftest.py` pins each worker to one OpenMP/BLAS thread. Without that pin, torch in every worker grabs every core and the parallel run is 10x slower than it should be. `-n auto` counts the cores the shell may use, so inside a 1-CPU `srun` step it runs a single worker; give the step more CPUs (`srun --overlap -c N`) first.

---

## Project Conventions
Expand All @@ -137,10 +164,6 @@ Prefer `{file}_test.py` in the same directory as `{file}.py`, unless there are i

### Code Style

- Docstrings use **numpy style** (`convention = "numpy"`).
- Lint rules: `D, E, F, I, NPY, PD, W`.
- `D` rules are ignored in `**/tests/**` and notebooks.
- Format: double quotes, spaces, 120 char line length.
- Use a subagent to run tests and complex bash commands, especially those expected to return complex output.
- Run independent tasks (multi-file edits across separate concerns, cross-cutting verifications, distinct review angles) in parallel via concurrent subagents in a single message. Subagent startup overhead is negligible relative to sequential blocking. Only sequence subagents when a later task needs an earlier task's output.

Expand Down Expand Up @@ -187,17 +210,7 @@ Ask yourself if your test is actually covering the true function.

### Coding Philosophy

#### 1. Think Before Coding

Don't assume. Don't hide confusion. Surface tradeoffs.

Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them — don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.

#### 2. Simplicity First
#### 1. Simplicity First

Minimum code that solves the problem. Nothing speculative.

Expand All @@ -208,7 +221,7 @@ Minimum code that solves the problem. Nothing speculative.
- If you write 200 lines and it could be 50, rewrite it.
- Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.

#### 3. Surgical Changes
#### 2. Surgical Changes

Touch only what you must. Clean up only your own mess.

Expand All @@ -223,18 +236,3 @@ When your changes create orphans:
- Don't remove pre-existing dead code unless asked.

The test: every changed line should trace directly to the user's request.

#### 4. Goal-Driven Execution

Define success criteria. Loop until verified.

Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"

For multi-step tasks, state a brief plan:
1. [Step] → verify: [check]
2. [Step] → verify: [check]

Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Mode recipe: Spotlight foreground-aware loss (Kalinin et al. 2025).
# Cross-cutting: sets loss, fg_mask_key, nonzero filtering, AND
# Otsu-centered normalization. fg_threshold: 0.0 is valid ONLY
# because normalizations use subtrahend: otsu_threshold.
# Otsu-centered normalization. The Otsu-centred target is what makes 0 the
# FG/BG boundary in the model's output space -- which the Dice term needs
# regardless of fg_threshold, since that field is inert here (see below).
#
# Requires: viscy preprocess --compute_otsu --compute_fg_masks
# Requires: viscy preprocess --compute_otsu true --compute_fg_masks true
#
# Base ordering: this mode must come AFTER the data recipe in the
# base: list so that its normalizations replace the data default.
Expand All @@ -14,6 +15,10 @@ model:
init_args:
lambda_mse: 0.5
sigmoid_k: -0.95
# INERT while data.fg_mask_key is set below: forward() short-circuits
# on `if fg_mask is not None`, so the mask from the store defines
# foreground and this threshold is never consulted. It applies only if
# fg_mask_key is removed. Same for data.nonzero_threshold.
fg_threshold: 0.0
data:
init_args:
Expand Down
Loading
Loading