Skip to content

Spotlight v2 segmentation-aware-loss - #535

Open
alxndrkalinin wants to merge 87 commits into
mainfrom
dynacell-spotlight-v2-arms
Open

alxndrkalinin wants to merge 87 commits into
mainfrom
dynacell-spotlight-v2-arms

Conversation

@alxndrkalinin

@alxndrkalinin alxndrkalinin commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Third of three stacked PRs that replace #494. This one adds the Spotlight v2 segmentation-aware-loss experiments. It is based on #534 (DynaCell v1.1), which is based on #533 (shared packages).

Spotlight is not part of the DynaCell paper, so its experiments get their own PR. The shared Spotlight losses (SegAuxDice, SAUNA/clDice maps, SegConsistencyLoss, BackgroundLowPass) are already in #533. The default-off engine hooks and mask_conditioning.py are in #534. Everything stays under applications/dynacell: every Spotlight runtime piece is dynacell.* (engine classes, eval pipeline, paths registry, launcher), and an applications/spotlight that imports dynacell would break the repo's no-sideways-imports rule. That move waits until the DynaCell LightningModules live in a package.

Contents (725 new files + 4 reverted trims)

  • Configs, all new (e9ad5c71, 714 files):
    • 479 arm leaves in 97 model dirs. Arms: segaux, segauxself, SAUNA/clDice, segcons + control, Track H bglp/bgflat, Stage 1b ccond/cjoint, the half/double weight sweep, cpout. Also the fresh v2 baselines, seed replicates and the small controls.
    • the four *_spotlight_fit* model overlays;
    • the 231 spotlight_v2_* grouped eval buckets.
  • Tools and tests, all new (d77a21d0, 13 files; DynaCell v1.1 application #534 later took over the two seg_consistency tests, so 11 remain new here):
    • the generators generate_spotlight_v2_leaves.py and generate_spotlight_v2_eval_configs.py, plus the eval roster;
    • the mask tools write_otsu_thresholds.py and write_classical_fg_masks.py;
    • cellpose_dense.py (the differentiable Cellpose behind the P1 segcons loss);
    • their tests and the seg_consistency focus/training tests.
  • The trims DynaCell v1.1 application #534 made, reverted to the dynacell-models text (46e30fb9, a0997f9d, 1d346d34, e2fc238a):
    • the Spotlight registrations and _SPOTLIGHT_ARM_MODELS skip in the grouped generator;
    • the Spotlight entries of the config guards and test_paths;
    • the generated headers of the 40 2D seed1 leaves (generate_spotlight_v2_leaves_test pins them byte for byte);
    • the generator lists in evaluation/README.md and test_evaluate_compose.py.
  • Kept from DynaCell v1.1 application #534: the shared skip rule (skip_reason) in the walk and the instance-AP audit, and its tests. This includes DynaCell v1.1 application #534's live-tree fix (FOLLOWUPS Migration from microDL #1). MEASURED on the live tree at 3b07b0d9:
    • walk_predictions() completes with 652 parsed stores. The 2D seed1 replicates are skipped through _SPOTLIGHT_ARM_MODELS, as on dynacell-models.
    • The audit returns 0 errors (dynacell-models: 102).
    • A default regeneration emits 0 wmatch blocks.
    • The canonical pool (596 stores) is identical, store by store, to dynacell-models' own tolerant parse.

Tree equivalence with dynacell-models

At this branch's head, git diff --stat origin/dynacell-models HEAD lists 24 files (+544/−119). Every other path, including engine.py, evaluation/paths.py, the root pyproject.toml and CLAUDE.md, is byte-identical to dynacell-models 8e74a7e. The full list, with a reason per file, is in the split's EQUIVALENCE record. In summary:

File(s) Why it differs
.github/workflows/lint.yml, test.yml no dynacell-models PR target; new test-utils job; the dynacell job no longer repeats the viscy-utils tests (#533)
tools/generate_grouped_eval_configs{,_test}.py, tools/generate_instance_ap_eval_configs{,_test}.py #534's shared skip rule for the walk and the instance-AP audit, the single model-dir acceptance rule, the live-tree fix (wmatch parsed; listed checkpoint re-predicts and named P1 segcons replicates skipped; registry guards), and their tests
11 packages/viscy-utils files, uv.lock #533's review fixes: torchvision declared, lazy viscy_utils.callbacks, CLI-injector cleanup, guard for fits without callbacks, step-cadence last.ckpt, save_last taken from every monitored checkpoint, zero-range CLAHE guard, check_finite removed, docstrings
6 VCP tutorial files (.py + .ipynb) install viscy-data from GitHub, since PyPI 0.1.0 lacks what the writer reads (#533)

So the three PRs together carry all 2,626 paths of #494, and nothing else changes.

Tests

Local run: pytest -n 8 on an A40 node, in this branch's own venv (uv sync --all-packages --all-extras --frozen).

Suite Result
applications/dynacell -k 'not test_combined_ddp' 5058 passed, 197 skipped, 0 failed (at 09a32857)

test_benchmark_config_composition.py collects 2,478 tests here (1,773 on #534). 705 of them are Spotlight leaves.

CI: pending retarget. This stacked PR gets no checks until it retargets to main, and it must be green then, before any merge.

Review guide

660 of the 714 config files carry a generator header: 427 leaves from generate_spotlight_v2_leaves.py, whose test pins every committed leaf to its output, and 233 from the eval generator. Review the generators and their tests rather than those files. The 54 hand-written ones are:

  • the eight v1 {fnet2d,fnet3d,pix2pix2d_unetvit,pix2pix3d_unetvit}_spotlight dirs (nucleus, membrane);
  • the ER/mito fnet3d_vscyto3daug arm templates;
  • the four *_spotlight_fit* overlays.

The only lines that differ from dynacell-models are in the table above.

A /code-review of the re-added hunks confirmed:

  • the 23-file equivalence (24 with the audit test DynaCell v1.1 application #534 added since);
  • generate_spotlight_v2_leaves.py --check: 467 leaves, 0 stale;
  • no Spotlight condition in any canonical bucket.

Its other findings concern code that is identical on dynacell-models and are left as follow-ups:

  • the _SPOTLIGHT_ARM_MODELS comment ignores the 2D seed1 replicates the 2D appendix scores;
  • the long-wall leaf list vs its comment, and the CLAUDE.md "measured only" rule;
  • arm tokens triplicated by hand in the grouped generator;
  • the seed1 headers cite a personal plan path.

🤖 Generated with Claude Code

alxndrkalinin and others added 30 commits October 26, 2026 15:47
Port the Spotlight-motivated losses from dynacell-models so they live
in the shared package before any application consumes them:
SegAuxDice (seg_aux.py) with the SAUNA-map and soft-clDice weight maps
(seg_aux_maps.py), the segmenter-consistency loss (seg_consistency.py),
the Track H background low-pass target (background_lowpass.py), and
SpotlightLoss(return_components=True). Defaults keep SpotlightLoss's
existing behavior; no package imports an application.

Source on dynacell-models: 40de793 (#498), 6c28a31 (#521),
0cbeda1 (#524), 050f553 (#530).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the Spotlight fg-mask recipe from dynacell-models.
generate_fg_masks now binarizes a CLAHE-equalized, 5^3-median-filtered
volume at an Otsu threshold picked on that volume after a sigma=5
Gaussian; compute_otsu uses the same full-resolution smoothing, so the
otsu_grid_spacing parameter is gone from generate_normalization_metadata
and VisCyTrainer.preprocess. The median and Gaussian run through cubic
on the GPU when cubic and a device are present, else SciPy; cubic stays
an optional import. torch is imported lazily so that light callers of
write_meta_field do not pay for it.

Behavior change: Spotlight masks and Otsu thresholds written by
`viscy preprocess` differ from main's.

Source on dynacell-models: 40de793 (#498), d087d96 (#510).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rite-once

Port the HCSPredictionWriter and prediction_metadata work from
dynacell-models:
- z_reduction="center" and z_planes (slab-only predicts write only the
  planes they predicted, and record them);
- per-FOV completion markers and the checkpoint/settings identity that
  predict resume and the eval caches key on (prediction_metadata.py);
- refuse writes into an existing store when a FOV outside the run has
  unmarked prediction channels, or when the array would grow;
- assemble each volume in memory and write it once on a background
  thread (bytes unchanged);
- raise NotImplementedError when trainer.world_size > 1, instead of
  letting ranks race on one store.

Source on dynacell-models: 2b848a6, 1be4466, 74fa18a (#497),
8e41498 (#511), 996adb4 (#526), 6c83fff (#531), 8e74a7e (#532).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the training guardrails from dynacell-models. VisCyCLI now
injects, on every `fit`:
- a latest-weights ModelCheckpoint that owns last.ckpt (the monitored
  checkpoint gets save_last=False), so last.ckpt always means the
  latest epoch, inheriting the leaf's write cadence;
- MonitorHealthCheck, which raises when the monitored metric is
  non-finite or bit-identical for 10 validation epochs;
- OptimizerHealthCheck, which raises after 5 consecutive non-finite
  losses or on GradScaler collapse.
Also adds the opt-in LinearLRCooldown callback and raises the CLI
subprocess test timeout to 120 s (it timed out on main).

Behavior change: this applies to cytoland, dynaclr and dynacell fits.

Source on dynacell-models: 20e54db, daa7477, bc0f444, 74fa18a
(#497), 92693ae, 2f9ef31 (#528).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port two viscy-utils changes from dynacell-models:
- ssim_25d / ms_ssim_25d: clamp the variances, bound the covariance by
  Cauchy-Schwarz through a safe sqrt, floor the stability constants
  symmetrically, and run MS-SSIM outside autocast, so 16-mixed training
  cannot NaN the gradient. SSIM/MS-SSIM loss values change slightly for
  MixedLoss users.
- viscy_utils/__init__ resolves its exports lazily (PEP 562), keeping
  torch and lightning off light import paths.

Source on dynacell-models: a556bc9 (#484), 130e034, cee4e41,
272051c, 59343ad, 8b2413f (#509).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…asks

Port the viscy-data side of the dynacell-models predict and Spotlight
work:
- HCSDataModule.predict_z_planes and SlidingWindowDataset z_planes /
  read_z_planes, so a 2D model predicts only its focus slab;
- cache_volume: with num_workers == 0 predict reads each (position, t)
  volume once and, when every transform is pointwise, normalizes it
  once;
- foreground masks are read in their stored dtype (uint8) and cast to
  float on the device in on_after_batch_transfer.

Behavior change: every app's predict at num_workers=0 takes the
read-once path.

Source on dynacell-models: 40de793 (#498), 996adb4 (#526),
6c83fff (#531), 8e74a7e (#532).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hannels

Port from dynacell-models:
- patch_size may be a per-axis (z, y, x) tuple through the patch
  embedding, transformer unpatchify and ViT bottleneck, which lets
  CELLDiffNet run Z-preserving at Z=1 for the 2D track (an int is still
  accepted);
- CELLDiffNet(cond_channels=1) adds extra conditioning input channels
  (default keeps the old single-channel net).

Source on dynacell-models: 0126fe6, 0617ed7, 40de793 (#498).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the time_schedule option of the CellDiff ODE integrators from
dynacell-models: uniform (default, unchanged), cosine, or a Beta(p, q)
grid packed toward the data end, which is what lets the DynaCell-lite
recipe sample in 14 velocity evaluations. scipy joins the celldiff
extra for betaincinv.

Source on dynacell-models: 19765e7 (#520).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port MorphEmModel from dynacell-models, the fifth deep-feature
extractor of the DynaCell evaluation. transformers is imported lazily
and the trust_remote_code model is pinned to a Hub commit.

Source on dynacell-models: d3f6422 (#479), 4918d2e, 13cb336,
c9c6914.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port preserve_z from dynacell-models, so the 3D PatchGAN runs on Z=1
inputs for pix2pix2d (default off, unchanged).

Source on dynacell-models: c2d0481.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port from dynacell-models:
- UNet3D(downsample_z=True): False keeps Z through the encoder, which
  is how FNet2D runs on single planes (default unchanged);
- UNet3DBase.forward raises on non-5D input, and a comment flags the
  ConvTranspose checkerboard artifact.

Source on dynacell-models: a556bc9 (#484), 83a1d69, 16b1d31.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port 9640537 from dynacell-models: a size-1 spatial axis (Z=1 for 2D
models) gets an identity row in the affine, instead of being resampled
off the volume and zeroed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NormalizeSampled and MinMaxSampled set pointwise = True, which lets the
read-once predict path in viscy-data normalize a whole volume once
instead of once per window.

Source on dynacell-models: 8e74a7e (#532).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ministic

Port 3409560 (#513) from dynacell-models.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port dfcf9a5 (#512) from dynacell-models: pytest-xdist joins the test
group, and a root conftest.py pins OMP/MKL/OpenBLAS to one thread under
each xdist worker. Without the pin every worker's torch sizes its pool
to all cores, which made `-n 11` 10x slower than it should be and timed
out subprocess tests. CLAUDE.md documents the usage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the marker half of c50300f (#489) from dynacell-models.
strict_markers = true turns an unregistered marker into a collection
error, so a typo'd marker can no longer silently decorate nothing.
hpc_integration moves from the cytoland and dynaclr conftests to the
root: an addinivalue_line registration only holds when that conftest
is in the invocation's ancestry. dynaclr drops its own testpaths so
the root ini applies.

requires_data is registered too: main's
applications/dynacell/tools/generate_grouped_eval_configs_test.py uses
it, and strict markers would otherwise drop that module at collection.
(dynacell-models no longer uses the marker; the dynacell PR that
follows removes the registration with it.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the root half of 7b7cb43 (#523) from dynacell-models so the HF
demo's tests import its app module. The directory already exists on
main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port d1ea492 from dynacell-models. The orchestrated linear-classifier
fixture joined on the first fov_name component, which failed 7 tests
on main once pytest collects dynaclr's colocated *_test.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the cytoland recipe comment fixes from 40de793 (#498): the
preprocess flags take explicit values (`--compute_otsu true
--compute_fg_masks true`), fg_threshold and nonzero_threshold are inert
while fg_mask_key is set, and the Otsu-centred target is what makes 0
the FG/BG boundary. Comments only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the tutorial fix from d3f6422 (#479): cytoland and viscy-utils
are not on PyPI yet, so the tutorials install them from the GitHub
monorepo; their siblings resolve from PyPI. Adds the jupytext-paired
notebooks (no outputs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port 93ac234 from dynacell-models: drop the repo-layout tree, setup
and common-command blocks, lint/docstring rules (pyproject.toml is the
source) and the generic coding-philosophy sections. The DynaCell job
runbook from the same file arrives with the dynacell PR, since it
documents tools that only exist there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
viscy-utils had no CI job on main or on dynacell-models: four of its
test files ran inside the dynacell job and the rest (losses, metrics,
meta_utils, optimizer health, CLI, lazy imports) ran nowhere. Mirror
the viscy-data job on ubuntu-latest for Python 3.12 and 3.13, and make
the gate job wait for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the test-dynacell-configs job from dynacell-models, minus the
eight test files that only arrive with the dynacell PR: sync
viscy-utils' test group with dynacell, and add test_submit_benchmark_batch,
test_engine, test_pipeline_cache, feature_metrics_test and the
viscy-utils prediction-writer, prediction-metadata, monitor-health and
LR-cooldown tests. The dynacell-models PR target is not carried over.

Source on dynacell-models: 33d2fee, 74fa18a (#497), 2d1ed32 (#506),
4450efd (#518), 2798dee (#519), 2f9ef31 (#528).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Start from dynacell-models' uv.lock and re-resolve with `uv lock`
against this tree, whose applications/dynacell/pyproject.toml is still
main's. Against dynacell-models' lock this drops the ten dynacell-only
packages (dinov3, etils, ftfy, jsonpath-rw, mlcroissant, pandas-stubs,
ply, rdflib, submitit, termcolor) and resolves cubic back to 0.7.0;
the dynacell PR restores them. Against main's lock it adds pytest-xdist
and moves boto3/botocore 1.43.0 -> 1.43.20, cellpose 4.1.1 -> 4.2.1.1,
pyarrow 19.0.1 -> 24.0.0, s3transfer and tracksdata; torch and
lightning are unchanged. `uv lock --check` exits 0 (main's own lock
does not).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port every DynaCell config path of dynacell-models verbatim: the model
leaves (baselines, A549/joint/HEK/brightfield, the 2D track with its
seed replicates and warmup-matched refit, the temporal and
patch-vs-augmentation ablations, CELL-Diff-R2 and the DynaCell-lite
leaves), the grouped and instance-AP eval buckets of the paper models,
the shared overlays, train/predict sets and launcher profiles, and the
deletion of 155 stale config files. The leaves are generated; review
the generators and guard tests in the code commits, not each leaf.

Spotlight v2 arm leaves, their four fit overlays and their 231
spotlight_v2_* eval buckets are left out; they follow in their own PR.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port every DynaCell code path of dynacell-models verbatim (226
non-merge commits since 2026-06-16; PRs #479-#530): the evaluation
pipeline (cpdino/MorphEm/focus-aware 2D, focus2d slab metrics,
foreground-limited metrics, MicroMS3IM, scale-invariant pixel metrics,
CP shared space, provenance and cache invalidation), the paths
registry, preprocessing/registry/croissant/distribution, the launcher
and stall watchdog, the lite benchmark and CELL-Diff fast inference,
the 2D track, the HF demo, docs, and the engine, celldiff_wrapper and
mask_conditioning modules exactly as on dynacell-models.

The three LightningModules keep their Spotlight training hooks
(seg_aux, seg_consistency, mask_mode, target_bg_lowpass). Every hook
defaults off, the bit-identity tests guard that, and only Spotlight
leaves turn them on; two DynaCell-paper 2D checkpoints store
seg_aux_weight in their hparams, so a hook-free engine could not
resume them. mask_conditioning.py comes along because engine.py and
celldiff_wrapper.py import it.

The files that import the Spotlight leaf generator are trimmed in the
commits that follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Take dynacell-models' uv.lock verbatim, which this tree's pyprojects now
match (`uv lock --check` exits 0). Against the shared-packages lock it
moves cubic 0.7.0 -> 0.9.0a4 and adds dinov3 (pinned commit), etils,
ftfy, jsonpath-rw, mlcroissant, pandas-stubs, ply, rdflib, submitit and
termcolor. A fresh `uv lock` would instead float dinov3 to its current
HEAD and bump mlcroissant/pandas-stubs, so the verbatim lock keeps the
versions the paper evaluations ran with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its only user was main's generate_grouped_eval_configs_test.py, which
the DynaCell port replaced: the live-data tests there are now marked
slow. With strict markers, nothing needs the registration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restore the eight dynacell test files the shared-packages PR left out
of the test-dynacell-configs job, now that they exist:
test_evaluation_microssim_calibration, segmentation_test,
test_distribution, test_croissant, test_main_cli_routing,
test_evaluation_fid_provenance, provenance_test and
watch_stalled_jobs_test. The job now matches dynacell-models'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port the DynaCell job runbook of the root CLAUDE.md from dynacell-models
(6193b9c, bcbfbe6, c4c5ed0, 74fa18a #497, e1d75cf): how to tell a
finished-but-hung job from healthy compute by the CPU-time derivative,
the stall watchdog (tools/watch_stalled_jobs.py), per-family predict
and fit wall limits, and the forensics to capture before cancelling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alxndrkalinin and others added 3 commits October 26, 2026 18:51
…is branch

evaluation/paths.py and tests/test_paths.py cited
tools/generate_spotlight_v2_leaves.py, which this branch does not
have, and test_paths claimed a pin to it that only exists with the
Spotlight v2 configs. Say where the generator lives instead. The
Spotlight model names stay: without them canonical_model_name would
resolve an arm onto its baseline. Comments only.

Found by /code-review on #534 (finding 8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #533's fix that takes last.ckpt from every monitored
checkpoint, not only the first. No conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #534's shared foreign-model predicate (walk and instance-AP
audit), the single model-dir acceptance rule, the keep-raising rule
for campaign models that do not parse, and #533's save_last fix.
Resolutions:
- generate_grouped_eval_configs.py: dynacell-models' version (Spotlight
  registrations and _SPOTLIGHT_ARM_MODELS skip) plus #534's
  _match_model_variant and is_foreign_model_dir in the walk;
- evaluation/paths.py and tests/test_paths.py: dynacell-models' text,
  which names the Spotlight leaf generator this branch carries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The changes require final human review because they are too complex or risky for automated approval.

No open issues 🎉

Review effort: Balanced

alxndrkalinin and others added 8 commits October 26, 2026 22:33
…ed walk

_CODE_TO_PAPER declares pix2pix2d_unetvit_wmatch{,_seed1} a parse-only
registration "same rule as fnet2d_voxelmatched", but unlike that model
they were never added to _DETERMINISTIC_MODELS. Their 56 stores on the
live tree therefore raised "unknown model code-name", which alone stops
walk_predictions. Register them like fnet2d_voxelmatched: they now parse,
and, as for every parse-only ablation, they are scored in their focused
sibling buckets (the committed *__2d_focus2d_pix2pix2d_wmatch leaves),
not by regenerating the canonical ones.

Fixes part of FOLLOWUPS #1 (live-tree walk crash).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…walk

The live tree holds second-checkpoint predictions of registered models
beside their canonical stores, under train-set dirs paths.py does not
know: fnet3d_paper/ipsc_step{8400,11k}, fnet2d_voxelmatched/ipsc_step200k,
fcmae_vscyto{2d_scratch_samplematched,3d_scratch}/ipsc_final and
fcmae_vscyto3d_scratch/ipsc_repredict (34 stores). key_from_prediction_store
raises on them ("unknown train_set token 'ipsc_final'"), which stopped
walk_predictions and filled the instance-AP audit. They come from the
voxel-matched FNet-2D and sample-matched UNeXt2-2D ablations, are
scored only by hand-written experiment buckets, and no committed config
references them, so they are not canonical inputs. Each
(model dir, train-set dir) pair is now listed in _CHECKPOINT_REPREDICTS
with its campaign; the walk skips and counts them and the audit skips
them. A train-set dir that is neither canonical nor listed still raises.

Fixes part of FOLLOWUPS #1 (live-tree walk crash).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nucleus/pix2pix2d_unetvit_segcons_{s1,ctrl_s1,x10} (12 stores, Spotlight
P1 follow-ups of 2026-10-19) sit in the canonical tree but are in
neither this generator's registry nor paths.PAPER_KEY, so the
PAPER_KEY-based foreign test cannot see them and they raised "unknown
model code-name". They are now listed by name in _FOREIGN_MODEL_DIRS
and skipped like the PAPER_KEY arms; the skip message no longer says
"PAPER_KEY". Any other unknown dir still raises. The new audit test
holds one store of each kind on the live tree (registered, checkpoint
re-predict, named foreign, wmatch, unknown train set) and expects only
the unknown one to be flagged.

Fixes the rest of FOLLOWUPS #1 (live-tree walk crash).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_CHECKPOINT_REPREDICTS matched (model dir, train-set dir) in every
organelle, so a future mito/fnet3d_paper/ipsc_step8400 store would have
been skipped as a known re-predict instead of raising, though only the
ER one exists. Entries are now (organelle, model, train set) triples
for exactly the stores on the live tree; a listed token in another
organelle raises "unknown train_set token" again (new test, fails
before this change). The audit test now uses the nucleus step-11k
store, since the step-8400 one is ER-only.

Found by /code-review on the FOLLOWUPS #1 fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
walk_predictions and audit_prediction_coverage each called
is_foreign_model_dir and is_checkpoint_repredict inline, so a third skip
category added to one could silently miss the other and split them
again. Both now call skip_reason(zarr_path), which names the category;
the walk counts per category. No behavior change.

Found by /code-review on the FOLLOWUPS #1 fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lass

Two guards on the hand-kept registries behind the live-tree crash:
- every _CODE_TO_PAPER model must parse. At a04b3eb this lists
  pix2pix2d_unetvit_wmatch{,_seed1}, the omission that stopped
  walk_predictions;
- the name-based skip lists stay disjoint from what the campaign
  registers: no _FOREIGN_MODEL_DIRS entry in _CODE_TO_PAPER or the
  parse tuples (it would be dropped silently, since the foreign check
  runs first), and no re-predict token among the canonical train sets.

Found by /code-review on the FOLLOWUPS #1 fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The organelle-scoping edit left a broken line wrap in the
_CHECKPOINT_REPREDICTS comment. Comment only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #534's fix for the walk/audit crash on the live prediction
tree (FOLLOWUPS #1): wmatch registered for parsing, the listed
checkpoint re-predicts and the named P1 segcons replicate dirs skipped
with counts, one skip rule for walk and audit, registry guard tests.
Resolution in generate_grouped_eval_configs.py: dynacell-models'
Spotlight registrations and _SPOTLIGHT_ARM_MODELS stay, with the
wmatch entries and _FOREIGN_MODEL_DIRS added beside them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The changes require final human review because they are too complex or risky for automated approval.

No open issues 🎉

Review effort: Balanced

alxndrkalinin and others added 2 commits October 26, 2026 23:11
… leaves

Registering pix2pix2d_unetvit_wmatch{,_seed1} for parsing (97c30a1)
fixed the live-tree walk, but a default regeneration would then also
fold their 56 stores into the canonical {organelle}_{train_set}
buckets and the instance-AP leaves, against DON'T-FOLD: the committed
*__2d_focus2d_pix2pix2d_wmatch buckets are where they are scored.
They are now listed in _SIBLING_ONLY_MODELS: walk_predictions still
returns them, so sibling buckets can be built from it, but main()
buckets canonical_predictions() (the walk minus that list) and the
instance-AP in_scope() drops them. New tests: canonical_predictions
leaves them out, in_scope rejects them.

MEASURED on the live tree, both generators run the default way (no
--out-root) emit 0 wmatch blocks, and the canonical pool (628 stores)
is identical, store by store, to what the pre-fix rules parse, so a
default regeneration writes exactly what a04b3eb would have written
had its walk not crashed.

Found by /code-review on the FOLLOWUPS #1 fix (finding 2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #534's fix that keeps pix2pix2d_unetvit_wmatch{,_seed1} out
of the canonical grouped buckets and the instance-AP leaves on a
default regeneration. No conflicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The changes require final human review because they are too complex or risky for automated approval.

No open issues 🎉

Review effort: Balanced

alxndrkalinin and others added 5 commits October 29, 2026 11:29
…r framework

The camera-ready title is now "Evaluating Dynamic 3D Virtual Staining
of Live Cells with DynaCell": DynaCell names a broader framework for
image-based profiling of cell-state dynamics, and this paper's
evaluation framework is part of it. Update the title in CITATION.cff
(software and preferred citation), the README and both HF card BibTeX
entries, and the Croissant citeAs string; rewrite the CITATION.cff
abstract and the README tagline to describe the evaluation framework
as part of DynaCell.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015kuht3npKtb2o5E6h19xbA
Mirror paper 1c85e84: "This evaluation framework is part of DynaCell, a
broader framework" repeated the noun. The paper's contribution is now
the DynaCell evaluation suite, and DynaCell stays the broader framework.
The CITATION.cff abstract also says "a set of baseline models", so
"suite" means one thing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015kuht3npKtb2o5E6h19xbA
The paper's title is now "The DynaCell Evaluation Suite for Dynamic 3D
Virtual Staining of Live Cells" (Overleaf b73f25f). Updates
CITATION.cff, the README and HF card BibTeX, and the Croissant citeAs.
test_croissant.py: 17 passed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015kuht3npKtb2o5E6h19xbA
#533 was squash-merged as 4a4ac9f while this branch carried its
commits only up to 0b1cb97, so the merge base is pre-#533 main and
four files conflicted:
- .github/workflows/test.yml, pyproject.toml, uv.lock: main's copy
  equals #533 at 0b1cb97 and this branch only adds to it, so this
  branch's side is kept (uv lock --check passes);
- applications/dynaclr/tests/conftest.py: unchanged here since
  0b1cb97, so main's side is taken (the pandas infer_string fix).
No file changed on both sides after 0b1cb97, and every merged file
equals this branch's copy where it changed the file, else main's.
Full suite (pytest -n 11): 5269 passed, 0 failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzpgExfYMKCgogfnhcSgDc
Each did a one-time job that is done, and nothing else imports them:
- build_migration_manifest, run_migration, restandardize_config_paths
  (+ tests): the canonical-path migration was applied 2026-07-13; a
  rebuilt manifest on 07-24 had nothing left to move;
- retokenize_deconv_predictions (+ test): the 24 a549__deconv /
  joint__legacy_deconvgt renames were applied 2026-07-22; paths.py
  still reads both tokens for the dirs kept on purpose;
- scan_nan_microssim_caches (+ test): one audit for the cubic 0.9.0a1
  NaN MicroMS3IM caches that #506 fixed;
- add_instance_ap_to_ablation_leaves: one codemod, already applied;
- smoke_joint_leaf: an April CPU smoke the config-composition tests
  now cover.
The provenance builders (cpdino seg masks, HEK stores, A549 dual fuse,
random-init checkpoints) stay. README entries and one bake_best_ckpt
docstring line follow. Full suite: 5251 passed, 0 failed (the 18
fewer are the two deleted tests/ files; the 14 colocated tool tests
were never collected by the root run).

Raised in review by edyoshikun.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MzpgExfYMKCgogfnhcSgDc
Base automatically changed from dynacell-v1.1-application to main November 4, 2026 16:21
alxndrkalinin and others added 2 commits November 4, 2026 08:27
#534 was squash-merged into main as 7286fb2. This branch stacked on
#534 but stopped six commits short of its final head (d7986fd): the
paper retitle, the #533 squash merge, and the deletion of the finished
one-shot tools. Merging that head first keeps the real ancestry, so the
following merge of main resolves against #533's squash commit instead
of the pre-split base. None of these six commits touches a file this
branch changes, and no file here references a deleted tool.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APoGhoKfkCho9Z1VMS5jsG
main's tree equals #534's final head (d7986fd) byte for byte, and the
previous commit merged that head here, so every one of the 47 conflicts
(the 40 generated 2D seed1 leaves, evaluation/paths.py and README, the
grouped generator and its test, test_paths, test_evaluate_compose,
test_benchmark_config_composition) resolves to this branch's side. They
are the trims #534 made that this PR reverts. The merge leaves the
tree unchanged: the PR's diff against main is byte-identical to its
previous diff against its #534 base (772 files).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APoGhoKfkCho9Z1VMS5jsG

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants