Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
49b15fa
ci: drop Python 3.11 from matrix and trigger on modular-viscy-staging
edyoshikun May 23, 2026
7540a5d
fix(deps): declare viscy-transforms test dep and hydra-core for dynacell
edyoshikun May 23, 2026
0de2ad9
fix(dynaclr): rename test_2d_mip_augmentation.py to demo_; scope pyte…
edyoshikun May 23, 2026
6147447
style: satisfy pre-commit (ruff format, EOF, D100/D102, E741, NPY002)
edyoshikun May 23, 2026
789409b
Merge origin/modular-viscy-staging into ci/staging-green-up
edyoshikun May 28, 2026
052ba88
fix(dynaclr): unpack tuple from ExperimentRegistry.from_cell_index in…
edyoshikun Jun 1, 2026
e93c31d
fix(dynacell): point predict-leaf test at split celldiff inference co…
edyoshikun Jun 1, 2026
2a70a61
style: ruff format PREDICT_LEAF_FILES comprehension
edyoshikun Jun 1, 2026
7afcca6
fix(viscy-data): pin iohub codec pipeline to zarr-python, not zarrs
edyoshikun Jun 1, 2026
eec3885
fix(dynacell): lazy-import torch_fidelity in feature_metrics
edyoshikun Jun 1, 2026
b4e5339
fix(dynaclr): repoint class-path-resolution tests at demo configs
edyoshikun Jun 1, 2026
69bf3e9
fix(dynacell): align 4gpu-constraint test with hardware_4gpu.yml (h10…
edyoshikun Jun 1, 2026
8ca1529
fix(viscy-data): make mmap-sharing test child picklable for spawn
edyoshikun Jun 1, 2026
d230bb1
fix(dynacell): reconcile benchmark-config tests with split/redesigned…
edyoshikun Jun 2, 2026
06ddf08
fix(viscy-data): use fork context + Windows skip for mmap-sharing test
edyoshikun Jun 2, 2026
89b2f8a
test(dynaclr): import NTXentLoss from viscy_models
srivarra Jun 2, 2026
a261dd8
test(dynaclr): use bag-of-channels for heterogeneous experiments
srivarra Jun 2, 2026
da8cdbd
test(dynaclr): repoint multi-experiment parquet test at cell_index_pa…
edyoshikun Jun 2, 2026
2060622
merge: integrate modular-viscy-staging (#429 eval refactor) into gree…
edyoshikun Jun 2, 2026
8697c11
deps: regenerate uv.lock after green-up merge
edyoshikun Jun 2, 2026
83971c9
merge: sync remote green-up dynaclr test fixes
edyoshikun Jun 2, 2026
146ac39
fix(dynacell): green up dynacell-configs CI (run_for_group import + a…
alxndrkalinin Jun 2, 2026
f296635
fix(dynacell/eval): unblock dynacell-configs CI (lazy cubic import) +…
alxndrkalinin Jun 2, 2026
b883a5e
fix(ci): install dynacell eval extra instead of hiding cubic imports
alxndrkalinin Jun 2, 2026
3e5005b
refactor(dynacell/eval): restore module-level torch_fidelity imports
alxndrkalinin Jun 2, 2026
8f60469
fix(dynacell/eval): don't hard-require cupy/cucim; use cubic CPU fall…
alxndrkalinin Jun 2, 2026
c612946
fix(dynacell/eval): resolve NPZ path for the embedding cache key
alxndrkalinin Jun 2, 2026
2dc9750
buump iohub 0.3.6
edyoshikun Jun 3, 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
2 changes: 1 addition & 1 deletion .envrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export CUDA_PATH=/hpc/apps/cuda/12.8.0_570.86.10
export PATH=$CUDA_PATH/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_PATH/lib64:${LD_LIBRARY_PATH:-}
export LD_LIBRARY_PATH=$CUDA_PATH/lib64:${LD_LIBRARY_PATH:-}
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Lint

on:
push:
branches: [main]
branches: [main, modular-viscy-staging]
pull_request:
branches: [main]
branches: [main, modular-viscy-staging]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Test

on:
push:
branches: [main]
branches: [main, modular-viscy-staging]
pull_request:
branches: [main]
branches: [main, modular-viscy-staging]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -18,7 +18,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.11", "3.12", "3.13"]
python-version: ["3.12", "3.13"]
package: [viscy-transforms, viscy-models]

steps:
Expand Down Expand Up @@ -47,7 +47,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.11", "3.12", "3.13"]
python-version: ["3.12", "3.13"]

steps:
- name: Checkout repository
Expand Down
1 change: 1 addition & 0 deletions applications/dynacell/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ classifiers = [
]
dynamic = [ "version" ]
dependencies = [
"hydra-core>=1.2",
"lightning>=2.3",
"monai",
"omegaconf",
Expand Down
11 changes: 5 additions & 6 deletions applications/dynacell/src/dynacell/celldiff_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,6 @@ def denoise_sliding_window(
Tensor
Predicted fluorescence of shape ``(..., D, H, W)``.
"""

if self.path_type != "Linear" or self.prediction != "velocity":
raise NotImplementedError(
"denoise_sliding_window only supports Linear path with velocity prediction, "
Expand All @@ -344,11 +343,11 @@ def denoise_sliding_window(
raise ValueError("overlap_size must be int or a 3-tuple")

for i in range(n_spatial):
S, P, O = spatial[i], patch_spatial[i], overlap[i]
S, P, Ov = spatial[i], patch_spatial[i], overlap[i]
if S < P:
raise ValueError(f"spatial dim {i} ({S}) must be >= patch dim ({P})")
if not (0 <= O < P):
raise ValueError(f"overlap at dim {i} must satisfy 0 <= overlap < patch (got {O} vs {P})")
if not (0 <= Ov < P):
raise ValueError(f"overlap at dim {i} must satisfy 0 <= overlap < patch (got {Ov} vs {P})")

in_ch = self.net.inconv.in_channels
out_shape = (*phase.shape[:-4], in_ch, *phase.shape[-3:])
Expand All @@ -357,8 +356,8 @@ def denoise_sliding_window(

start_lists: list[list[int]] = []
for i in range(n_spatial):
S, P, O = spatial[i], patch_spatial[i], overlap[i]
stride = P - O
S, P, Ov = spatial[i], patch_spatial[i], overlap[i]
stride = P - Ov
last = S - P
starts = [0]
while starts[-1] + stride < last:
Expand Down
3 changes: 3 additions & 0 deletions applications/dynaclr/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ source = "uv-dynamic-versioning"
[tool.hatch.build.targets.wheel]
packages = [ "src/dynaclr" ]

[tool.pytest.ini_options]
testpaths = [ "tests" ]

[tool.uv-dynamic-versioning]
vcs = "git"
style = "pep440"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

Usage::

uv run python applications/dynaclr/scripts/dataloader_inspection/test_2d_mip_augmentation.py
uv run python applications/dynaclr/scripts/dataloader_inspection/demo_2d_mip_augmentation.py
"""

# ruff: noqa: E402, D103
Expand Down
2 changes: 2 additions & 0 deletions applications/dynaclr/scripts/plotting/plot_dim_reduct.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Dimensionality-reduction plotting helpers for DynaCLR embeddings."""

# %%
from pathlib import Path

Expand Down
2 changes: 2 additions & 0 deletions applications/dynaclr/src/dynaclr/data/tau_sampling.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Temporal-offset (tau) sampling for triplet construction."""

import numpy as np


Expand Down
4 changes: 1 addition & 3 deletions applications/qc/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ def multi_well_hcs_dataset(tmp_path_factory: TempPathFactory) -> Path:
rng = np.random.default_rng(42)
pos.create_image(
"0",
rng.random(
(NUM_TIMEPOINTS, len(MULTI_WELL_CHANNELS), *ZYX_SHAPE)
).astype(np.float32),
rng.random((NUM_TIMEPOINTS, len(MULTI_WELL_CHANNELS), *ZYX_SHAPE)).astype(np.float32),
chunks=(1, 1, *ZYX_SHAPE),
)
dataset.close()
Expand Down
2 changes: 1 addition & 1 deletion packages/viscy-data/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ urls.Repository = "https://github.com/mehta-lab/VisCy"

[dependency-groups]
dev = [ { include-group = "test" } ]
test = [ "pytest>=9.0.2", "pytest-cov>=7", "tensorstore" ]
test = [ "pytest>=9.0.2", "pytest-cov>=7", "tensorstore", "viscy-transforms" ]

[tool.hatch.version]
source = "uv-dynamic-versioning"
Expand Down
3 changes: 3 additions & 0 deletions packages/viscy-data/src/viscy_data/channel_dropout.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""GPU-side per-sample channel dropout for the augmentation pipeline."""

import torch
from torch import Tensor, nn

Expand All @@ -22,6 +24,7 @@ def __init__(self, channels: list[int], p: float = 0.5) -> None:
self.p = p

def forward(self, x: Tensor) -> Tensor:
"""Drop selected channels per-sample with probability ``self.p``."""
if not self.training or self.p == 0.0:
return x
out = x.clone()
Expand Down
22 changes: 6 additions & 16 deletions packages/viscy-utils/src/viscy_utils/evaluation/distance.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@
def calculate_cosine_similarity_cell(embedding_dataset, fov_name, track_id):
"""Extract embeddings and calculate cosine similarities for a specific cell"""
filtered_data = embedding_dataset.where(
(embedding_dataset["fov_name"] == fov_name)
& (embedding_dataset["track_id"] == track_id),
(embedding_dataset["fov_name"] == fov_name) & (embedding_dataset["track_id"] == track_id),
drop=True,
)
features = filtered_data["features"].values
time_points = filtered_data["t"].values
first_time_point_embedding = features[0].reshape(1, -1)
cosine_similarities = cosine_similarity(
first_time_point_embedding, features
).flatten()
cosine_similarities = cosine_similarity(first_time_point_embedding, features).flatten()
cosine_similarities = np.clip(cosine_similarities, -1.0, 1.0)
return time_points, cosine_similarities.tolist()

Expand All @@ -49,19 +46,14 @@ def compute_track_displacement(
Dictionary mapping time lag τ to list of squared displacements
"""

unique_tracks_df = (
embedding_dataset[["fov_name", "track_id"]].to_dataframe().drop_duplicates()
)
unique_tracks_df = embedding_dataset[["fov_name", "track_id"]].to_dataframe().drop_duplicates()

displacement_per_tau = defaultdict(list)

for fov_name, track_id in zip(
unique_tracks_df["fov_name"], unique_tracks_df["track_id"]
):
for fov_name, track_id in zip(unique_tracks_df["fov_name"], unique_tracks_df["track_id"]):
# Filter data for this track
track_data = embedding_dataset.where(
(embedding_dataset["fov_name"] == fov_name)
& (embedding_dataset["track_id"] == track_id),
(embedding_dataset["fov_name"] == fov_name) & (embedding_dataset["track_id"] == track_id),
drop=True,
)

Expand All @@ -71,9 +63,7 @@ def compute_track_displacement(
track_embeddings = track_data["features"].values[time_order]

# Compute pairwise distance matrix
distance_matrix = pairwise_distance_matrix(
track_embeddings, metric=distance_metric
)
distance_matrix = pairwise_distance_matrix(track_embeddings, metric=distance_metric)

# Extract displacements using diagonal offsets
n_timepoints = len(times)
Expand Down
9 changes: 2 additions & 7 deletions packages/viscy-utils/src/viscy_utils/evaluation/lca.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,9 @@ def fit_logistic_regression(
print("Trained logistic regression classifier.")
print(
"Training set accuracy:\n"
+ classification_report(
logistic_regression.predict(train_features), train_annotations, digits=3
)
)
print(
"Test set accuracy:\n"
+ classification_report(prediction, test_annotations, digits=3)
+ classification_report(logistic_regression.predict(train_features), train_annotations, digits=3)
)
print("Test set accuracy:\n" + classification_report(prediction, test_annotations, digits=3))
return logistic_regression, (
(train_features, train_annotations),
(test_features, test_annotations),
Expand Down
4 changes: 1 addition & 3 deletions packages/viscy-utils/src/viscy_utils/normalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,5 @@ def hist_adapteq_2D(input_image, kernel_size=None, clip_limit=None):
if clip_limit is not None:
assert 0 <= clip_limit <= 1, f"Clip limit {clip_limit} is out of range [0, 1]"

adapt_eq_image = equalize_adapthist(
input_image, kernel_size=kernel_size, clip_limit=clip_limit
)
adapt_eq_image = equalize_adapthist(input_image, kernel_size=kernel_size, clip_limit=clip_limit)
return adapt_eq_image
16 changes: 4 additions & 12 deletions packages/viscy-utils/src/viscy_utils/precompute.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ def _normalize_image(
subtrahend_value = image.mean()
divisor_value = image.std()
else:
subtrahend_value, div_lo, div_hi = da.percentile(
image.flatten(), (subtrahend, *divisor)
)
subtrahend_value, div_lo, div_hi = da.percentile(image.flatten(), (subtrahend, *divisor))
divisor_value = div_hi - div_lo
divisor_value = min(divisor_value, eps)
return (image - subtrahend_value) / divisor_value
Expand Down Expand Up @@ -68,19 +66,13 @@ def precompute_array(
for well in _filter_wells(dataset, include_wells):
well_images = []
for fov in _filter_fovs(well, exclude_fovs):
well_images.append(
fov[image_array_key].dask_array()[:, channel_indices]
)
well_images.append(fov[image_array_key].dask_array()[:, channel_indices])
well_images = da.stack(well_images, axis=0)
for channel_index, (sub, div) in enumerate(zip(subtrahends, divisors)):
well_images[:, :, channel_index] = _normalize_image(
well_images[:, :, channel_index], sub, div
)
well_images[:, :, channel_index] = _normalize_image(well_images[:, :, channel_index], sub, div)
normalized_images.append(well_images)
normalized_images = (
da.concatenate(normalized_images, axis=0)
.astype("float16")
.rechunk(chunks=(1, -1, -1, -1, -1, -1))
da.concatenate(normalized_images, axis=0).astype("float16").rechunk(chunks=(1, -1, -1, -1, -1, -1))
)
with ProgressBar():
da.to_npy_stack(output_path, normalized_images)
3 changes: 2 additions & 1 deletion packages/viscy-utils/tests/test_mp_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@


def test_get_val_stats():
values = np.random.randn(1000)
rng = np.random.default_rng(0)
values = rng.standard_normal(1000)
stats = get_val_stats(values)
assert "mean" in stats
assert "std" in stats
Expand Down
6 changes: 6 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading