Releases: johnmarktaylor91/torchlens
Release list
v2.31.0
v2.31.0 (2026-07-10)
This release is published under the Apache-2.0 License.
Bug Fixes
-
api: Correct paper-era trace compatibility (
2c96a50) -
audit: Guard visual pack demo contrasts (
a559c2d) -
backends: Align preview capability handling (
175054d) -
capture: Emit compiled-unwrap note inside restoration guard (
f505766) -
capture: Exempt boolean dispatch pooling composites (
f909554) -
capture: Factory wrappers honor device context for explicit device=None (
19f097c)
The Python factory-function wrappers bypass torch's C-level DeviceContext dispatch, so they re-inject the active device kwarg themselves. Two gaps let a torch.device('meta') context silently place tensors on CPU:
-
_maybe_inject_device_kwarg skipped injection whenever 'device' was a key in kwargs -- but nn.Linear (and other modules) forward device=None explicitly via factory_kwargs. An explicit device=None means 'defer to the active context', exactly like an absent kwarg, so meta-context params came out on CPU once torch was wrapped. Only bail when a real (non-None) device is pinned.
-
wrap_torch clears + repopulates torch's _device_constructors lru_cache with the wrapped callables, but unwrap_torch never cleared it. After an unwrap the cache stayed stale, so torch's device-context dispatch stopped recognising the restored original factories -- breaking meta context even for unwrapped torch. Clear the cache on unwrap so torch re-evaluates against the restored originals.
Regression tests cover the in-session normal-then-meta trace sequence (the meta guard must still fire), explicit device=None on the empty family, and unwrap restoring the meta context.
-
capture: Prune dead scoped candidate identities (
4e21a14) -
capture: Restore ignored routes and pre-hook effects (
9487983) -
capture: Support nonweakrefable module identities (
163db6f) -
capture: Tighten provenance typing (
6d52add) -
capture: Unwrap compiled models at public entry points (
abb5d12) -
compile: Unwrap compiled training capture entry points (
5c077f5) -
debug: Find_nan reports the nearest user frame (
e44d046) -
debug: Identify user frame by torchlens package dir, not path component (
21cd1d7)
find_nan's live source-line walk classified library frames by testing for a 'torchlens' path component, which false-matched any checkout whose repo directory is named torchlens (e.g. .../torchlens/tests/...), hiding the real user frame and reporting launcher frames like under some pytest invocations. Match the actual torchlens/torch package directories instead, skip <frozen ...> launcher frames, and keep / as user code.
-
debug: Preserve capture option explicitness (
40aa4a0) -
docs: Make witness examples executable (
764cdc3) -
export: Qualify recurrent node ids (
c56c719) -
import: Preserve bundle callable after submodule import (
9d0a888) -
import: Preserve lazy facade exports over child modules (
bd705fe) -
import: Preserve lazy facade shim and wrapper fidelity (
c194b5e) -
intervention: Over-approximate in-place snapshot gating (
d2283d7) -
intervention: Parent module-splice replacement at consuming scope (
d0d33e8) -
intervention: Reconcile live and replay replacements (
346425d) -
intervention: Snapshot in-place-op inputs for post-hook interventions (
0f4c305) -
io: Accept pre_hook_input in tlspec validation (
7c6c45a) -
io: Drop live escape diagnostics from bundles (
45f44f4) -
io: Enforce tlspec version ceiling in validation (
e4c5018) -
io: Preserve audit-only body index compatibility (
bc0a2a8) -
io: Preserve conditional body cache across round trips (
f20ccaa) -
io: Preserve current null backend addresses (
13bfaf1) -
io: Preserve intervention trace portability (
9def3c5) -
io: Preserve tensor requires grad across round trips (
f72dcfb) -
io: Reject desynchronized tlspec body indexes (
1d2f2c4) -
io: Reject missing and corrupt tlspec manifests (
444c8a8) -
io: Type witness diagnostic defaults (
936825f) -
io: Validate annotation blobs emitted by tlspec writer (
06494b3) -
report: Preserve subsecond profile timings (
6e9a265) -
selectors: Reject non-string function patterns (
32808fa) -
tinygrad: Adapt preview capture to 0.13 semantics (
66b82cc) -
validation: Align witness accounting across capture paths (
078d8dc) -
validation: Correct completeness witness claims (
067a46b) -
validation: Count distinct replay nodes (
13db70b) -
validation: Initialize witness counters when witness is off (
db9ca6a) -
validation: Isolate replay state for stateful models (
62ac1b4) -
validation: Perturb copy source values (
acb0293) -
validation: Preserve reproducibility capture identity (
3d95a43) -
validation: Reject exempted-only replay passes (
bd41543) -
validation: Require per-op exclusion proof for selective save (
13069b7) -
validation: Restore snapshotable attrs independently (
de250ae) -
validation: Support retained orphan islands ([
25e9e60](https://github.com/johnmarktaylor91/t...
v2.30.1
v2.30.1 (2026-07-09)
This release is published under the Apache-2.0 License.
Bug Fixes
- viz: Support callable node_overlay instead of an opaque TypeError (
178752d)
A callable passed to draw(node_overlay=...) fell through to the mapping path (candidate in scores) and died with "argument of type 'function' is not iterable". Route callables through external_overlay_value: invoke overlay(node) to compute a per-node value, then format it like any external score. Widen the public type to str | Mapping | Callable[[Any], Any] | None across options + both draw signatures. Regression test asserts a callable overlay renders per node.
Documentation
- intervention: Fix splice_module signature drift (input/output defaults) (
babc926)
docs/intervention_api.md showed input="activation"/output="activation"; the real signature (intervention/helpers.py:606) is input="in"/output="out".
Detailed Changes: v2.30.0...v2.30.1
v2.30.0
v2.30.0 (2026-07-09)
This release is published under the Apache-2.0 License.
Bug Fixes
- capture: Commutative reflected dunders normalize to forward op (fixes rand parent drop) (
e925e9d)
Tensor.rand (reflected int & tensor) and torch.rand both flattened to key "rand"; the torch.rand factory spec (no tensor parents) clobbered rand's binary spec via dict last-writer-wins, so reflected bitwise-AND captured ZERO parents and was misclassified as an orphan (silent dataflow corruption) and mislabeled "rand".
Fix: map COMMUTATIVE reflected operator dunders (radd/rmul/rand/ror/ rxor) to their forward op's normalized name (add/mul/and/or/xor) in _normalize_func_name; route the ops.py layer_type sites + selector-candidate helper through it. For a commutative op the swapped operand order is irrelevant, so the reflected form IS the forward op: correct label ("and_N_M"), forward binary (0,1) arg-spec (real parents), shared equivalence with the forward op. Drop the dead binary "rand" key so torch.rand keeps it uniquely.
NON-commutative reflected dunders (rsub/rtruediv/rmatmul/...) are left UNMAPPED on purpose: they compute other <op> self (operands swapped), so the r-prefixed name honestly signals the reversed order and matches real functions like torch.rsub; their parents/spec were already correct.
Add an import-time guard that binary-op and factory-func name sets never share a key (loud failure for the whole collision class). Regression tests: commutative reflected -> forward; non-commutative reflected keep their r-name; no binary/factory collision; reflected int & tensor captures its parent + labels "and".
- capture: Keep orphan retention opt-in (default keep_orphans=False) (
d3771f9)
The a6572b2 default flip to keep_orphans=True regressed the trace self-consistency validation invariant: retained island ops inflate num_ops past the computational-layer count (e.g. num_ops=8 != expected 3), and the mid-tier sweep caught ~11 failures across toy models, functionless-replacement tripwires, and buffer-datamodel validation.
Revert the default to False so islands are pruned unless explicitly requested. The orphan DATA capability (trace.orphans) and the show_orphans render option remain, now opt-in via keep_orphans=True. Defaulting orphan retention needs the validation invariants to first account for retained islands (num_ops == computational_layers + orphan_count, and a check that a retained orphan is a genuine dead-end, not a functionless-replacement capture gap) -- deferred to the validation-hardening work. Do NOT weaken the invariant to force the default.
- fastlog: Don't miscategorize warning-as-error as a predicate failure (
2ecbc18)
When a caller runs with warnings-as-errors, the informational reference-save-mode UserWarning (emitted during payload storage) was escalated to an exception, caught by the broad except Exception in the op-recording loop, and swallowed into the predicate-failure pipeline -- resurfacing as a misleading PredicateError: fastlog predicate failed with no hint of the real cause. A predicate failure is about the user's predicate raising, not a TorchLens-internal warning.
Re-raise Warning-typed escalations directly (mirroring the existing TorchLensPostfuncError/ TrainingModeConfigError passthrough) so they surface with their true type and message.
Also classify the reference save-mode caveat as visible-not-fatal in the test filterwarnings group (alongside the capability-degradation warning), since it fires whenever save_mode= 'reference' is used and is informational, not a bug signal -- fixes an order-dependent test failure (the once-per-process warning made test_true_predicate_inherits_default_save_mode pass only when a sibling reference-mode test ran first and tripped the flag).
- import: Restore torchlens.facets module importability via lazy alias stub (
487ef2e)
7edabcb dropped the eager sys.modules["torchlens.facets"] alias to keep facets lazy, but that alias was also what made import torchlens.facets / import_module("torchlens.facets") resolve to (and be identity-equal to) torchlens.semantic.facets -- test_phase_b_exports asserts that identity. The _LAZY_ATTRS entry only covers the ATTRIBUTE torchlens.facets, not the module import path.
Add a self-replacing stub module torchlens/facets.py that, when (and only when) someone imports torchlens.facets, resolves torchlens.semantic.facets and swaps itself out in sys.modules for it. Preserves laziness (facets is NOT imported at import torchlens) while restoring import torchlens.facets is torchlens.semantic.facets.
- loop-detection: Require >=2-op body for unanchored param-free loops (
fe7acea)
The recurrence grouper flagged a param-free loop from just two adjacent ops sharing a structural fingerprint, with no minimum body size -- so y = tanh(x); z = tanh(y) false-positived as a two-pass loop despite being a straight-line chain.
Guard param-free adjacency-only merges: they now require either a recurrence-anchored seed op (a reused submodule call or a stateful buffer node -- genuine repetition of a persistent identity, real even with a single-op body) or a body of at least _MIN_PARAM_FREE_LOOP_BODY_OPS (2) operations. Parameter-based merges (RNNs, weight tying) are decided by shared parameters and never reach the guard, so they are untouched; reused single-op modules (one nn.ReLU called 4x) and buffer-rewrite loops (state:1-6) stay grouped via the anchor exemption.
Thread a recurrence_anchored flag from the torch finisher (op.modules non-empty or op.is_buffer) through RecurrenceNode into the grouper. Regression tests: a bare functional tanh chain stays two single-pass layers; a reused single-op module stays one 4-pass layer.
- viz: Label elif condition-entry edges ELIF instead of a blanket IF (
8a4f4ea)
conditional_branch_edges records the edges entering each branch TEST but carries no kind, so the renderer hardcoded "IF" on every one -- an if/elif/elif ladder drew "IF" three times. Derive the real kind per edge: conditional_records[*].bool_layers is the authoritative ordered test list ([if_bool, elif_1_bool, ...]; else has no test), so map each edge's child (the condition-subgraph entry op) to its nearest downstream branch bool via a bounded forward walk and read the position -- index 0 -> IF, later -> ELIF. Arm-entry labels (THEN/ELIF N/ ELSE) are unaffected; this is the separate condition-entry family. Bounded to _BRANCH_KIND_SEARCH_LIMIT so a malformed conditional can't turn labelling into a hot loop. Regression test asserts IF + ELIF + ELIF on the ladder's condition-entry edges.
- viz: Order conditional branches if-left / then-right instead of arbitrarily (
df76300)
The sibling-ordering pass explicitly skipped conditional-fanout sources, leaving the left-to-right placement of a branch's children arbitrary. Drop the skip so conditional fanouts get the same by-execution-step ordering as any other fanout: the earlier-evaluated branch test lands on the left and the later taken arm on the right, and an if/elif/elif ladder reads IF, ELIF, ELIF, arm left-to-right at a single rank. Remove the now-unused _has_conditional_fanout helper (only the skip referenced it). Validated across the conditional-rendering, step5, integration, sibling-ordering, output-aesthetics, and container-visualization suites (44 passed).
Documentation
- examples: Migrate vis_opt= -> vis_mode= (15 files) + ledger the alias (
7263e88)
All 15 shipped example notebooks used the legacy vis_opt= kwarg; 0 used the canonical vis_mode=. Migrate them to vis_mode= (same values) and add the vis_opt -> vis_mode row to docs/reference/deprecations.md, which was missing it.
- examples: Rename 5min peek notebook to pluck + retire the peek alias in prose (
8ce9d97)
peek is the deprecated alias for pluck; the 5-minute quickstart still led with it. git mv examples/5min/peek.ipynb -> pluck.ipynb, switch its cells to tl.pluck, and update the README index link + the "labels used by pluck/extract" prose in visualize.ipynb. No remaining peek references under examples/.
Features
- capture: Retain orphan islands by default (keep_orphans=True) (
a6572b2)
Island ops -- computations unreachable from both the model inputs and outputs -- were silently pruned by default; surfacing them is more honest for introspection. Flip the CaptureOptions.keep_orphans default to True so islands are retained in raw metadata and exposed via the trace.orphans accessor.
This does NOT change layer_list, summaries, module logs, or the rendered graph: retained orphans live only on trace.orphans, so well-behaved models (no islands) are byte-identical and the field-order golden + io round-trips are unaffected. Set keep_orphans=False to restore pruning. The visualization side (rendering islands in draw) stays opt-in pending a separate orphan-in-draw pass.
- viz: Draw(show_orphans=true) renders orphan islands a...
v2.29.1
v2.29.1 (2026-07-09)
This release is published under the Apache-2.0 License.
Bug Fixes
- menagerie,tests: Egnn F507 format bugs + guard torchvision imports for lean CI (
6d676df)
Post-merge CI parity: (1) fix 4 identical F507 %-format bugs in the EGNN classics (rs_L642_2_enqa, rs_L643_2_fabind, rs_L643_3_fabflex, rs_L648_1_mean) --
"Wrong coords_agg parameter" % self.coords_agg had no placeholder and would TypeError if hit; (2) guard the module-level torchvision imports in test_auto_collapse_metrics, test_collapse_optimizer, and test_s5_render_identity_harness with pytest.importorskip (matching the repo's lean-CI + importorskip convention for mlx/paddle/pandas/transformers), so smoke collection no longer aborts where torchvision (an optional vision-shims extra) is absent.
Code Style
- Auto-format with ruff (
754cc14)
Detailed Changes: v2.29.0...v2.29.1
v2.28.0
v2.28.0 (2026-06-24)
This release is published under the Apache-2.0 License.
Bug Fixes
-
fastlog: Harden partial recording failure handling (
d2ee04d) -
viz: Collapse at stage level for nested-container models (
5ef95cd)
Descend through named backbone containers (features/encoder/backbone) and fold the repeated/similar stage children as peer boxes instead of swallowing the whole container; relaxed structural-similarity peer grouping (class/stem) so unique-but-parallel modules (Inception Mixed_*) fold; atomic conv-bn-relu fold; guard mixed-depth collapsed-edge placement.
-
viz: Fold standalone auto-collapse leaf blocks (
f7b4aee) -
viz: Keep sibling ordering with collapse predicates (
531b793) -
viz: Name the repeated module class in the run-fold ellipsis label (
06a6e9a) -
viz: Remove model-specific collapse override (
435d952) -
viz: Render run folds with ellipsis (
6bc5e84) -
viz: Tighten auto run-fold structural key (
2c47137) -
viz: Tune auto collapse landmarks (
362499f)
Documentation
-
readme: Feature 10,000+ run scope, keep 700+ validated distinct (
4ff02f6) -
readme: Feature the Model Menagerie up front in the intro (
992a7bb) -
readme: Point Gallery to the live Model Menagerie (10k+ architectures, early preview) (
fee6951) -
readme: Update menagerie stats to current campaign state (
03fd83a)
The validation campaign took the menagerie from ~700 validated to over 99% (of 11,000+ architectures) rigorously validated for capture correctness.
- viz: Document smart collapse controls (
06456f1)
Features
-
fastlog: Return partial recordings on forward errors (
40e12eb) -
viz: Add smart module collapse policy (
4dc56f6) -
viz: Fold runs of repeated blocks to representative + xN (
ce05e3b)
Fold a run of >=3 consecutive structurally-identical sibling blocks (fold key = class + internal topology, dim-insensitive) into one representative box with an xN multiplicity badge; fold the run's parallel inter-block edges to one representative edge. Fixes under-collapse on deep repeated-block nets (swin_s 96->16, vit 24->13, vgg19_bn flat conv-bn fold 58->6) while keeping distinct stages separate.
Performance Improvements
- viz: Keep collapse signal tally linear (
6f735f6)
Testing
-
viz: Accept new collapse kwarg in _DummyLog.draw mock (
fc3ac69) -
viz: Cover smart auto collapse metrics (
d3da42e)
Detailed Changes: v2.27.0...v2.28.0
v2.27.0
v2.27.0 (2026-06-22)
This release is published under the Apache-2.0 License.
Bug Fixes
- menagerie: Bump smp PSPNet recipes to encoder_depth=5 (
4178ef5)
PSPNet's default encoder_depth=3 only executes the first 3 encoder stages, where resnet50/resnet101/resnext101 backbones are topologically identical -- so backbone variants collapsed to one graph_shape_hash. Running the full encoder (depth=5) makes them trace as the genuinely distinct architectures they are. Verify-each: only rows that still instantiate and trace at depth=5 were changed.
- menagerie: Cross-env render --retry-failed + merge round-2 repairs (
48bd02e)
run_across_envs render command now passes --retry-failed so freshly-installed envs re-attempt rows the base env marked skipped:dependency_unavailable (they were being treated as completed and skipped, rendering 0). Also merges 2 verified round-2 recipe repairs and records env_specs install status/resolved_packages provenance.
- menagerie: Merge 252 round-3 recipe repairs (assign-model/syntax/dilated/attr) (
6205061)
Recovers 252 recipes a prior wave had wrongly classified as ceilings; each rewritten and verified to instantiate + trace (add missing model binding, fix syntax, drop unsupported smp dilated-mode kwargs, correct module import paths).
-
menagerie: Repair 64 catalog recipe bindings (
c0020f3) -
torch: Also guard module name access in patch_detached_references (
39a3135)
Completes the lazy-module-safety fix (after the file guard). The sys.modules crawl read mod.name via hasattr/getattr, which - like file - can propagate a non-AttributeError from a lazy shim's getattr (hasattr only swallows AttributeError). Read it defensively with a fallback to the sys.modules key. Audited the rest of the crawl: remaining attribute access is via dict/vars(), which does not trigger getattr, so the bug class is fully closed. Regression test now exercises BOTH the file and name vectors.
- torch: Guard module file access in patch_detached_references (
f07db5b)
SpeechBrain's LazyModule (and similar lazy-import shims) raise ImportError from getattr when an optional dependency (k2/flair) is missing. getattr(mod, 'file', None) only suppresses AttributeError, so the sys.modules crawl in patch_detached_references propagated the ImportError and aborted tracing of every model imported alongside SpeechBrain (11 models in the menagerie acid test). Add _safe_module_file() to guard the access broadly and treat any failure as 'no file'; add a regression test with a module whose file access raises.
- torch: Make wrapped torch.nn.functional Python ops jit.script-able (
80e1137)
torch.jit.script failed compiling any function that calls a wrapped pure-Python functional op (e.g. F.softsign): such ops are absent from torch's jit _builtin_table, so the wrapper is not registered as a builtin; jit then pulls the original op's source but resolves names against the wrapper module's globals, which lacked the torch.overrides boilerplate -> 'undefined value has_torch_function_unary'. Surfaced by spikingjelly @torch.jit.script surrogates in the model menagerie. Import has_torch_function_unary and handle_torch_function into the wrappers module globals so jit resolves the shared functional-op preamble (jit treats has_torch_function_unary as always-False, eliding the override branch). Verified: F.softsign + spikingjelly surrogate.SoftSign now script; added a jit_compat regression test; smoke green.
Chores
- menagerie: Record cross-env install provenance (status + resolved_packages) (
ad5943c)
The cross-env orchestrator writes back per-env install status and the exact resolved package set after a run, so future reruns reuse captured recipes without re-debugging.
- types: Cast coerced bundle member items for mypy 2.x (
a7a7ffd)
mypy 2.1.0 (CI) flagged _coerce_member_name_list passing the wide str|Trace|Sequence union to _coerce_member_name; the _is_list_like runtime narrowing is invisible to mypy. Cast each item to the type the narrowing guarantees. Runtime no-op; mypy 1.19 + 2.1 both clean.
Continuous Integration
- release: Skip publish steps when no dist is built (
a3449c1)
Non-releasing commits (ci/docs/chore/test/refactor pushed on their own) make semantic-release produce no new version and no dist/, so the unconditional 'Publish to PyPI' step failed with FileNotFoundError: dist and turned the Release job red despite nothing being meant to ship. Gate both publish steps on hashFiles('dist/**') != '' so they no-op when there is nothing to publish and run normally on real releases.
Features
- menagerie: A 10k+ neural-net architecture atlas as a live TorchLens acid test [skip ci] (
1896ed7)
Brings the model menagerie to main: a browsable atlas of 10,540+ neural-network architecture families plus 300+ hand-built historical "classics" -- each with no prior public PyTorch implementation, every one trace-verified -- all captured and graphed with TorchLens.
It doubles as an exhaustive correctness/coverage test of TorchLens itself: every architecture that instantiates is traced end-to-end and rendered, turning the full breadth of public deep learning into a single regression surface for the capture engine.
Highlights: - Queryable catalog (python -m menagerie.catalog stats|query|recipe) over an SQLite + TSV source. - Disk-safe parallel renderer: per-model subprocess isolation, --jobs/--gpu-jobs, GPU support, BLAS/OMP thread pinning, aesthetic passthrough, idempotent --retry/--force reruns. - Architectural graph-shape hashing: parameter/resolution/width-invariant fingerprints that dedup variants while staying architecture-discriminative -- verified resnet18/50/101 hash distinctly, while aimv2 width+resolution variants correctly collapse to a single architecture. - Rerun-proof cross-environment orchestrator with captured per-framework install recipes. - A durable adversarial "find every architecture we missed" discovery prompt + arXiv harvest crawler. - Prior-art subsumed (Younger / NAR / ONNX Zoo) as a superset, with related work credited.
[skip ci]: this is a visibility push. The menagerie is not part of the published torchlens package, so no release is cut. The render/validation campaign to 100% coverage is still in flight.
- menagerie: Add 58 adversarial-sweep families + durable discovery prompt (
3fedb00)
Fold in the cross-lab adversarial red-team sweep (hostile Opus + Codex auditors tasked with proving the catalog incomplete): - classics/: +58 historical / exotic / non-English families with no prior PyTorch implementation (Associatron, Gelenbe Random NN, Tsetlin Machine, Lenia/Flow-Lenia, Coherent Ising Machine, P-bit nets, morphological/fuzzy/oscillator nets, neuroevolution phenotypes, differentiable machines, ...). 312 classics total; all trace-verified (312/312). - data/master_catalog.tsv: +103 red-team catalog-adds (particle physics, EEG foundation models, seismology, Chinese-lab CTR/LLM, satellite). Catalog: 10,742 canonical / 6,127 verified. - DISCOVER_MODELS.md: rewritten as the durable, reusable adversarial "find missed families" sweep prompt (every-axis + non-English + newly-published coverage, family-not-variant discipline, exact catalog/classics add instructions, orchestration recipe). - discover_crawler.py: starter recent-arXiv candidate harvester to seed sweeps. -
CLAUDE.md / AGENTS.md: point top-level agents at the discovery prompt.
- menagerie: Add model menagerie catalog + 254 trace-verified historical classics (
895fbd9)
Add the menagerie/ package: a browsable atlas of neural-net architectures captured with TorchLens.
- catalog.py: queryable SQLite+TSV catalog of 10,581 canonical models across ~3,600 normalized families (CLI: stats / query / recipe). Auto-folds the historical-classics registry into the catalog at build time. - classics/: 254 historical architectures that previously lacked a PyTorch implementation, spanning every era (McCulloch-Pitts 1943 onward), domain (vision, RL, sequence, comp-neuro, genomics), paradigm (logic, PDP, neuro-symbolic, vector-symbolic, spiking), and orphan corner (stat-mech machines, hyperbolic nets, photonic/memristor, DDSP, EEGNet). Each exposes a builder plus example input and is trace-verified with TorchLens (254/254). - generate_menagerie.py: disk-safe, dependency-aware, resumable graph renderer. - README / METHODOLOGY / UPDATE_RECIPE / DISCOVER_MODELS docs.
The generated SQLite DB and canonical TSV are rebuilt from the tracked master catalog and code; bulk render outputs are gitignored.
- menagerie: Add validate-all script, cross-env orchestrator, crawl-date metadata (...
v2.26.0
v2.26.0 (2026-06-17)
This release is published under the Apache-2.0 License.
Bug Fixes
-
api: Correct legacy graph shim guidance (
cda34b4) -
capture: Raise typed reentrant trace error (
b4f993d) -
torch: Estimate quantized module flops (
f915995) -
torch: Record buffer data reassignment (
f3c7836) -
torch: Warn on collapsed transform boundaries (
0527b38)
Chores
- types: Satisfy api shim typing (
e3375c8)
Documentation
- Uniform-height menagerie gallery thumbnails (
ec37cca)
Thumbnails were sized by width, but graph aspect ratios span ~18x (deep nets ~1:12), so rendered heights varied up to 5x within a row. Uniform height=200 makes each gallery row a clean, same-height filmstrip and shrinks the overall section. Bigger gallery overhaul still to come.
- torch: Align wrapping and DataParallel guidance (
3bdfdc0)
Features
- api: Add legacy paper api shims (
1ec4f6c)
Performance Improvements
- torch: Prefilter detached reference crawl (
5c3b67b)
Testing
- api: Update public surface count (
eff5e0b)
Detailed Changes: v2.25.0...v2.26.0
v2.25.0
v2.25.0 (2026-06-17)
This release is published under the Apache-2.0 License.
Bug Fixes
-
fastlog: Derive recording record count (
f3ffa37) -
intervention: Correct projection and repr output (
caf080d) -
validation: Repair multi-output and fallback snapshots (
0dac4d0)
Features
- api: Expose export and ambiguous lookup error (
a4952c8)
Detailed Changes: v2.24.1...v2.25.0
v2.24.1
v2.24.1 (2026-06-17)
This release is published under the Apache-2.0 License.
Continuous Integration
- Drop obsolete total_audit CI (job + nightly workflow + coverage scripts) (
82205d8)
The total_audit notebook series was removed in favor of notebooks/audit/; its CI job referenced deleted notebooks (incl. ones that never existed), failing every run. Keep the real smoke job. Scrub the dead-script references in scripts/AGENTS.md + state_of_torchlens.md.
Documentation
- state: Conform capture/intervention vocab to the shipped glossary renames (
71ddae9)
peek->pluck, batched_extract->extract_dataset, replay->push, replay_from->push_from, rerun->run, and add the record/Recording entry -- matching the v2.24 public API (old names remain as deprecated aliases).
Performance Improvements
- capture: Skip non-container leaf types in tensor discovery (~29% faster) (
95dc123)
get_vars_of_type_from_obj was the #1 capture hot spot (~45% of resnet18 capture): it attribute-crawled (dir() + getattr over every attribute) every non-tensor item that reached it, including values that cannot hold a tensor. Profiling showed None alone was ~66% of those crawls, with torch device/storage another ~33%.
- Add _NON_CONTAINER_LEAF_TYPES (primitives plus None, torch device/dtype/Size/ UntypedStorage) and use it in the three leaf-skip checks, so these are skipped instead of crawled. - Skip building the discarded per-item address strings/paths when return_addresses is False (the default, ~94% of callers) via a track_addresses flag.
resnet18 capture 1030ms -> 736ms median; get_vars cumtime 5.0s -> 0.65s. The found-tensor set is unchanged (the skipped types cannot contain tensors). Full not-slow gate green apart from pre-existing failures.
Refactoring
- capture: Store module/param metadata in identity-keyed registries (
c6542fb)
Move ModuleMeta/ParamMeta storage off user-owned module/param objects (where it was a ._tl attribute that polluted state_dict / serialization / introspection and wrote an attribute onto nn.Parameter tensors) into process-wide WeakIdKeyDictionary registries in backends/torch/_tl.py. Tensors and decorated callables (TorchLens-owned) keep the attribute path. The get/set/ensure API is unchanged.
WeakIdKeyDictionary (not stdlib WeakKeyDictionary) is required: nn.Parameter is a tensor whose elementwise eq breaks ref-based dict keying; the id-keyed variant is tensor-safe and auto-removes dead entries via weakrefs (no manual finalizers, no id-reuse hazard).
Also fix _module_address_from_meta (buffer_writes.py), which read the metadata via getattr(module, "_tl", None) -- the string form that bypassed the API. After the refactor modules have no _tl attribute, so it returned "" and buffers were addressed without their module prefix, making BatchNorm running-mean updates reconcile as "unjournaled". It now uses get_module_meta().
Tests: narrow test_wrong_kind_meta (the foreign-_tl collision is vacuous for registry-backed params/modules; assert registry independence instead) and add a registry-invariants test (no _tl attr on user objects, registry-absence after clear, deepcopy does not carry meta, GC drops dead keys). Full not-slow gate green apart from pre-existing failures.
Detailed Changes: v2.24.0...v2.24.1
v2.24.0
v2.24.0 (2026-06-17)
This release is published under the Apache-2.0 License.
Chores
- audit: Revert nbstripout exemption (keep repo convention) (
cefcc82)
nbstripout strips committed notebook outputs by design; the -filter override didn't take cleanly. Review the audit via the executed HTML exports (notebooks/audit/_exports/, regenerate per CLAUDE.md) instead.
Documentation
-
Conform notebooks to glossary-v11 API + curated model-visualization gallery (
b631f48) -
Full modern README overhaul (conformed API) (
f118ab9)
Rewrite on the current tl.trace/Trace surface (legacy log_forward_pass/model_history/ vis_opt/.activation removed). New hero + 700-models and 180+/550+ metadata-field stats; six-pillar scope tour with run-verified examples; cleaned stale links (CoLab->in-repo tutorial, Drive share link, cerbrec https); dropped stale Planned Features.
-
audit: Add HuggingFace notebook (14) + commit executed outputs (nbstripout exempt) (
ddd2dd2) -
audit: Correct review-surface note (HTML exports, not the -filter trick) (
0b9fc4f) -
audit: Coverage notebooks 01-13 + visual-pack generator (
5443718)
Scoped ruff per-file-ignore (E402) for notebooks/audit/*.ipynb (shim-before-import is mandated by the audit CLAUDE.md recipe). Notebook 11 image previews stripped (graphs live in the visual pack); notebook 05 blob-index sha256 truncated in-code so regeneration stays detect-secrets-clean.
-
audit: Fix NB00 trace[int] claim, close coverage holes, README matrix (
8509646) -
audit: Foundation -- model zoo, template notebook 00, README/CLAUDE recipe (
599ce40)
Clear old audit notebooks (14 + _artifacts) and total_audit/. Replace with:
- notebooks/audit/_models.py: 18-model verified zoo (all tl.trace OK)
- notebooks/audit/00_setup_and_first_capture.ipynb: executed-green template
covering tl.trace, Trace repr/str, summary() levels, label accessors,
trace[label]/[int], Op.out, trace.layers/.ops/.modules/.params, tl.peek - notebooks/audit/README.md: human coverage matrix (00 filled, 01-14 pending)
- notebooks/audit/CLAUDE.md: maintenance recipe for future agents
- notebooks/audit/.gitignore: ignores _exports/, visual/_pages/, visual_audit.pdf
Features
- api: Glossary-conform-v11 — DO-NOW renames with deprecation aliases (
da1328d)
Implements all DO-NOW renames from the v11 glossary freeze review:
Top-level / method renames (old names kept as deprecated aliases): - rerun -> run (Trace.run, Bundle.run, intervention.run) - replay -> push (Trace.push, Bundle.push, intervention.push) - replay_from -> push_from (Trace.push_from, intervention.push_from) - peek -> pluck (tl.pluck) - batched_extract -> extract_dataset (tl.extract_dataset) - record_span -> span (tl.span, observers.span)
Selector renames (old names kept as deprecated aliases): - intervening -> without_op (intervention.without_op) - new: regex selector (no prior "matches" existed to alias)
sweep param rename: - param -> at (positional; param= accepted as deprecated kwarg)
Expose in all: - record, Recording now in torchlens.all
Cut from public export: - sites(), SiteCollection, SiteSpec removed from torchlens.all and torchlens.intervention.all; test_sites.py updated to use internal path
Also: RegexSelector class + re.search matching; resolver/hooks updated for without_op, regex, push/push_from, run
Testing
-
api: Update golden counts + trace.do() for glossary-conform-v11 (
4e5f26d) -
test_api_surface.py: TARGET_ALL updated to 76 entries (removed
sites;
added record, Recording, push, push_from, run, pluck, extract_dataset,
without_op, regex, span); test renamed to test_all_size_exactly_76. -
test_report_explain.py: all count 66 -> 76 with inline changelog.
-
test_bundle_methods.py: budget bumped <= 31 -> <= 34 for push/run additions.
-
trace.do(): dispatch calls updated to push()/run() to avoid spurious
DeprecationWarning on internal code paths.
Detailed Changes: v2.23.0...v2.24.0