Skip to content

Commit ecb8d53

Browse files
Donglai Weiclaude
andcommitted
WIP: docs theme/sidebar, liconn tutorial, optuna tuner + affinity QC edits
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2f8b521 commit ecb8d53

9 files changed

Lines changed: 354 additions & 15 deletions

File tree

connectomics/decoding/qc/affinity.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -203,19 +203,22 @@ def _per_z_scan(pred, z_stride: int) -> dict:
203203
sel = [(i, z) for i, z in enumerate(z_idx) if z0 <= z < z1]
204204
if not sel:
205205
continue
206-
# Slice over the trailing Z axis regardless of leading dims.
207-
block = np.asarray(pred[..., z0:z1]).astype(np.float32)
206+
# Read native dtype (typically float16) — keep the slab compact and
207+
# only widen one z-plane at a time below. Halves peak RAM on large
208+
# (C, X, Y, block_z) reads.
209+
block = np.asarray(pred[..., z0:z1])
208210
nan_count += int(np.isnan(block).sum())
209211
inf_count += int(np.isinf(block).sum())
210212
for i, z in sel:
211-
sl = block[..., z - z0].reshape(C, -1)
213+
sl = block[..., z - z0].astype(np.float32, copy=False).reshape(C, -1)
212214
means[i] = sl.mean(axis=1)
213215
stds[i] = sl.std(axis=1)
214-
g_sum += sl.sum(axis=1)
215-
g_sq += (sl.astype(np.float64) ** 2).sum(axis=1)
216+
g_sum += sl.sum(axis=1, dtype=np.float64)
217+
g_sq += np.square(sl, dtype=np.float64).sum(axis=1)
216218
g_min = np.minimum(g_min, sl.min(axis=1))
217219
g_max = np.maximum(g_max, sl.max(axis=1))
218220
g_n += sl.shape[1]
221+
del block
219222
return {
220223
"z_idx": z_idx, "means": means, "stds": stds,
221224
"g_sum": g_sum, "g_sq": g_sq, "g_min": g_min, "g_max": g_max,
@@ -233,29 +236,30 @@ def _refine_z_cuts(pred, interior_mean: np.ndarray,
233236
low_z = head_end
234237
head_rows = []
235238
if head_end > 0:
236-
block = np.asarray(pred[..., 0:head_end]).astype(np.float32)
239+
# Read each Z-plane individually; refine_window is small (~30) so the
240+
# extra h5 calls are negligible vs holding (C, X, Y, refine_window)
241+
# widened to float32 in RAM.
237242
for z in range(head_end):
238-
m = block[..., z].reshape(C, -1).mean(axis=1)
243+
m = np.asarray(pred[..., z]).astype(np.float32, copy=False) \
244+
.reshape(C, -1).mean(axis=1)
239245
ok = bool((m >= cutoff).all())
240246
head_rows.append((z, m.copy(), ok))
241247
if ok and low_z == head_end:
242248
low_z = z
243-
del block
244249

245250
tail_start = max(0, Z - refine_window)
246251
high_z = tail_start
247252
tail_rows = []
248253
if tail_start < Z:
249-
block = np.asarray(pred[..., tail_start:Z]).astype(np.float32)
250254
last_ok = -1
251255
for z in range(tail_start, Z):
252-
m = block[..., z - tail_start].reshape(C, -1).mean(axis=1)
256+
m = np.asarray(pred[..., z]).astype(np.float32, copy=False) \
257+
.reshape(C, -1).mean(axis=1)
253258
ok = bool((m >= cutoff).all())
254259
tail_rows.append((z, m.copy(), ok))
255260
if ok:
256261
last_ok = z
257262
high_z = last_ok + 1 if last_ok >= 0 else tail_start
258-
del block
259263

260264
return low_z, high_z, head_rows, tail_rows
261265

connectomics/decoding/tuning/optuna_tuner.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,24 @@ def optimize(self) -> optuna.Study:
928928
direction=direction,
929929
)
930930

931+
# Fail orphan RUNNING trials inherited from a previous process (e.g.
932+
# SLURM TIMEOUT mid-trial). GridSampler treats RUNNING trials as
933+
# already-taken, so their grid points would be skipped forever unless
934+
# we release them here.
935+
if self.tune_cfg.load_if_exists:
936+
for trial in study.get_trials(
937+
deepcopy=False, states=(optuna.trial.TrialState.RUNNING,)
938+
):
939+
study._storage.set_trial_state_values(
940+
trial._trial_id, optuna.trial.TrialState.FAIL
941+
)
942+
logger.info(
943+
"Released orphan RUNNING trial #%d (params=%s) as FAIL so its "
944+
"grid point can be re-sampled.",
945+
trial.number,
946+
trial.params,
947+
)
948+
931949
# Seed the first trial with known-good defaults so TPE has a strong
932950
# baseline from the start instead of wasting early trials on random configs.
933951
default_params = self._build_default_trial_params()

docs/source/_templates/_static/css/pytc-theme.css

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,73 @@ so make sure not to go above that value */
241241
display: block !important;
242242
}
243243
}
244-
/* docsearch end */
244+
/* docsearch end */
245+
246+
/* Sidebar accordion: parents toggle children on click; chevron flips ▸→▾.
247+
248+
State model:
249+
- Default: L2 children hidden (display:none).
250+
- Sphinx adds `.current` to the L1 on the path to the active page →
251+
that section auto-expands on load.
252+
- JS toggles `.expanded` / `.collapsed` on click. `.collapsed` wins
253+
over `.current` so the user can fold the active section too. */
254+
255+
.pytorch-menu-vertical .toctree-l1:has(> ul) > a {
256+
font-weight: 600;
257+
position: relative;
258+
padding-right: 2rem;
259+
cursor: pointer;
260+
}
261+
.pytorch-menu-vertical .toctree-l1:has(> ul) > a::after {
262+
content: "▸";
263+
position: absolute;
264+
right: 1rem;
265+
top: 50%;
266+
transform: translateY(-50%) rotate(0deg);
267+
font-size: 0.75em;
268+
opacity: 0.6;
269+
transition: transform 0.15s ease;
270+
}
271+
.pytorch-menu-vertical .toctree-l1.current:has(> ul) > a::after,
272+
.pytorch-menu-vertical .toctree-l1.expanded:has(> ul) > a::after {
273+
transform: translateY(-50%) rotate(90deg);
274+
}
275+
.pytorch-menu-vertical .toctree-l1.collapsed:has(> ul) > a::after {
276+
transform: translateY(-50%) rotate(0deg) !important;
277+
}
278+
279+
/* Hide L2 children by default; reveal for .current or .expanded; an
280+
explicit .collapsed (user folded it) wins over everything. */
281+
.pytorch-menu-vertical .toctree-l1 > ul {
282+
display: none;
283+
border-left: 2px solid rgba(238, 76, 44, 0.18);
284+
margin-left: 1.25rem;
285+
padding-left: 0.25rem;
286+
}
287+
.pytorch-menu-vertical .toctree-l1.current > ul,
288+
.pytorch-menu-vertical .toctree-l1.expanded > ul {
289+
display: block;
290+
}
291+
.pytorch-menu-vertical .toctree-l1.collapsed > ul {
292+
display: none !important;
293+
}
294+
295+
/* Children: lighter weight, deeper indent. */
296+
.pytorch-menu-vertical .toctree-l2 > a {
297+
font-weight: 400;
298+
padding-left: 1.25rem;
299+
}
300+
301+
/* Active page highlight. */
302+
.pytorch-menu-vertical .toctree-l2.current > a,
303+
.pytorch-menu-vertical .toctree-l2 > a.current {
304+
background-color: rgba(238, 76, 44, 0.08);
305+
border-left: 3px solid #ee4c2c;
306+
font-weight: 600;
307+
}
308+
309+
.pytorch-menu-vertical .toctree-l1 > a:hover,
310+
.pytorch-menu-vertical .toctree-l2 > a:hover {
311+
background-color: rgba(0, 0, 0, 0.04);
312+
}
313+
/* sidebar accordion end */

docs/source/_templates/layout.html

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@
165165
toctree is empty. Skip building this for now.
166166
#}
167167
{% if 'singlehtml' not in builder %}
168-
{% set global_toc = toctree(maxdepth=1,
168+
{% set global_toc = toctree(maxdepth=2,
169169
collapse=theme_collapse_navigation|tobool,
170170
includehidden=theme_includehidden|tobool,
171171
titles_only=theme_titles_only|tobool) %}
@@ -425,6 +425,25 @@ <h2>PyTorch</h2>
425425
$("article.pytorch-article a span.pre").each(function (e) {
426426
$(this).closest("a").addClass("has-code");
427427
});
428+
429+
// Sidebar accordion: click an L1 parent (that has children) to toggle
430+
// .expanded / .collapsed instead of navigating. .current is set by
431+
// Sphinx on the active section so it starts open by default; the
432+
// explicit .collapsed class lets the user fold even the active one.
433+
$('.pytorch-menu-vertical .toctree-l1 > a').each(function () {
434+
var $a = $(this);
435+
var $li = $a.parent();
436+
var $ul = $li.children('ul');
437+
if ($ul.length === 0) return;
438+
$a.on('click', function (e) {
439+
e.preventDefault();
440+
if ($ul.is(':visible')) {
441+
$li.addClass('collapsed').removeClass('expanded');
442+
} else {
443+
$li.addClass('expanded').removeClass('collapsed');
444+
}
445+
});
446+
});
428447
})
429448
</script>
430449

docs/source/conf.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,11 +152,19 @@
152152
# html_theme_path = [pytorch_sphinx_theme.get_html_theme_path()]
153153

154154
html_theme_options = {
155-
'collapse_navigation': True,
155+
# False => sections render with expand/collapse arrows the user can
156+
# click to toggle children (accordion). True hides them entirely
157+
# unless the current page lives in that section.
158+
'collapse_navigation': False,
156159
'display_version': True,
157160
'logo_only': True,
158161
'navigation_with_keys': True,
159162
'navigation_depth': 3,
163+
'includehidden': True,
164+
# True => sidebar only shows document titles, not in-page subsections;
165+
# keeps the sidebar from blowing up with H2/H3 anchors from long
166+
# "Get Started" pages once we render maxdepth=2 in layout.html.
167+
'titles_only': True,
160168
}
161169

162170
# Add any paths that contain custom themes here, relative to this directory.

docs/source/tutorials/neuron/_intro.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ canonical pipeline first predicts an affinity map (the connectivity of each
33
voxel to its neighbors) with an encoder-decoder, then converts the affinity
44
map into a segmentation via watershed or a similar algorithm.
55

6-
This section covers two benchmarks:
6+
This section covers three benchmarks:
77

88
- :doc:`SNEMI3D <snemi3d>` — the classic small isotropic-anisotropic
99
benchmark, used for end-to-end affinity training and waterz
@@ -14,3 +14,6 @@ This section covers two benchmarks:
1414
benchmark evaluated with the **NERL** skeleton metric. Reproduction
1515
targets in ``tutorials/neuron_nisb/`` mirror the upstream BANIS
1616
pipeline.
17+
- :doc:`LICONN <liconn>` — the LICONN volume variant of the NISB
18+
benchmark; reuses the BANIS-style affinity pipeline and adds an
19+
affinity-mask QC step for the LICONN-specific border artifacts.

docs/source/tutorials/neuron/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ Neuron Segmentation
88

99
snemi3d
1010
nisb
11+
liconn

0 commit comments

Comments
 (0)