Skip to content

fix: High/Medium audit bugs across state, environ, random, nn, transform - #244

Merged
chaoming0625 merged 13 commits into
mainfrom
fix/audit-high-medium
Oct 20, 2026
Merged

chaoming0625 merged 13 commits into
mainfrom
fix/audit-high-medium

Conversation

@chaoming0625

@chaoming0625 chaoming0625 commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Fixes the High/Medium bugs found in a package-wide audit. Every fix has a regression test that fails before the change. The spec is in doc/specs/2026-10-audit-fixes.md.

Core state & hooks

  • Mode equality and hashing now include parameters (Batching(batch_size, batch_axis) and the components of JointMode), so jit retraces when a static mode changes. Comparing with a non-Mode returns NotImplemented.
  • update_from_ref, replace, copy, copy_from and TreefyState.to_state no longer share or lose hook managers or tag sets.
  • Raising HookCancellationError inside a hook now cancels the write. Logged hook errors report the original exception.
  • HookManager rebuilds its cache under the lock and runs callbacks outside it, so they can no longer deadlock.
  • StateTraceStack.__exit__ no longer hides an in-flight exception, and no longer leaves stale entries on the stack.

environ

  • Settings and callbacks are now process-global; only the context() stack stays per thread.
  • context(precision=...) blocks that overlap across threads no longer turn x64 off for each other.

random

  • pareto now samples Lomax, matching numpy and its docs. Behavior change.
  • multinomial counts always sum to n.
  • zipf accepts a list size.

nn

  • BatchNorm with track_running_stats=False now normalizes. Running stats keep their shape when feature_axis is not last.
  • MaxPool and MaxUnpool:
    • MaxPool indices now use a layout that does not depend on the input size, so MaxUnpool handles odd sizes without output_size.
    • Index values are unchanged for evenly divisible inputs without padding.
    • MaxPool(return_indices=True, in_size=...) no longer crashes in the constructor.
  • Delay:
    • The delay step and update_every_step are rounded correctly (e.g. 0.3/0.1 → 3).
    • init_maybe_prefetch(PrefetchDelay) works.

transform

  • DCE and constant folding keep side effects (debug_print, callbacks, jit_error_if).
  • The x - x / 0 * x rewrites now apply only to integer scalars.
  • vmap_new_states, vmap2_new_states and pmap2_new_states roll back pre-existing states that were written with mapped tracers, then raise a clear error. pmap traces are detected through the parent_trace chain. States excluded via state_to_exclude are poisoned and unwound.
  • Argument handling:
    • Negative static_argnums work.
    • jit infers static/donate argnums from argnames, and argnames from argnums, like jax.jit.
    • A state may mix batched and unbatched leaves.
  • Python code generation for scan now produces runnable code.

Tests

  • conftest.py simulates 8 CPU devices via jax.config.update('jax_num_cpu_devices', 8), so multi-device tests run without accelerators.
  • New tests cover multi-device pmap2, a stress test of the global hook registry, and odd-size MaxUnpool checked against a reference.

Behavior changes

  • random.pareto samples are 1 lower than before.
  • vmap*_new_states raises when a batched value is written into a pre-existing state. Previously the tracer leaked silently.
  • environ settings are shared across threads.
  • For odd or padded MaxPool inputs, x.ravel()[indices] no longer gathers the maxima.

Test plan

  • pytest brainstate -n 8: 5438 passed, 53 skipped (slow tests and missing optional frameworks), 0 failed.

Summary by Sourcery

Resolve high- and medium-severity correctness, concurrency, numerical, and transformation bugs across the package while adding comprehensive regression coverage.

Bug Fixes:

  • Fix correctness issues across state and hook management, environment handling, random distributions, neural-network layers, and transformations.
  • Preserve transformation side effects, prevent tracer leaks, and improve static argument handling and generated scan code.
  • Correct MaxPool/MaxUnpool behavior for odd and padded inputs, including index handling and inferred output sizes.

Enhancements:

  • Make modes parameter-aware for equality, hashing, and JIT retracing.
  • Make environment settings and callbacks process-global while keeping temporary context overrides thread-local.
  • Improve hook cancellation, error reporting, cache safety, and callback concurrency.
  • Improve state-copy isolation and trace-stack cleanup.
  • Correct delay rounding, BatchNorm statistics, multinomial and distribution shape handling, and prefetch delay initialization.

CI:

  • Configure tests to simulate eight CPU devices so multi-device coverage runs without accelerators.

Documentation:

  • Document the audit fixes, behavior changes, and updated MaxUnpool and environment semantics.

Tests:

  • Add regression coverage for state and hook isolation, concurrent hooks, thread-safe environments, multi-device transformations, odd-size pooling, numerical fixes, side-effect preservation, and argument handling.

…mpare Mode parameters

- Mode.__eq__/__hash__ include Batching(batch_size, batch_axis) and JointMode
  components, and return NotImplemented for non-Mode operands, so jit static
  Mode arguments retrace when parameters change.
- update_from_ref keeps the state's own HookManager and source info;
  TreefyState.to_state and State.replace get a fresh HookManager; copy,
  replace, copy_from and to_state copy the tag set instead of aliasing it.
- Hook.execute re-raises HookCancellationError so raising it cancels a write;
  logged hook errors report the original exception.
- HookManager rebuilds its cache under the lock and runs callbacks outside it.
- StateTraceStack.__exit__ no longer masks an in-flight exception and removes
  itself (and leaked entries above it) from the stack.
…s threads

- EnvironmentState settings and callbacks are shared by all threads; only the
  context() override stack stays per thread, so worker threads see dt/precision.
- Overlapping context(precision=...) blocks across threads restore the most
  recent still-active override instead of clobbering the global JAX flag; with
  none left, the global precision setting (or the import-time precision) wins.
…s, zipf size

- pareto subtracts 1 from jax's Pareto-I samples so support starts at 0, as in
  numpy and the docstring.
- multinomial treats the last pval as 1 - sum(rest) like numpy and clips the
  sampled index, so counts always sum to n.
- zipf normalizes size via _size2shape (list sizes no longer fail as unhashable
  static args).
…ing, PrefetchDelay

- BatchNorm with track_running_stats=False normalizes with batch statistics
  instead of returning the input unchanged.
- BatchNorm reshapes batch mean/var to the running-stat shape before the EMA
  update, so feature_axis != -1 no longer corrupts running stats.
- MaxUnpool scatters indices against the original input layout when
  output_size matches it (fixes odd-sized inputs); MaxPool(return_indices=True,
  in_size=...) no longer crashes in the constructor.
- Delay rounds float delay/dt ratios that are within 1e-6 of an integer
  instead of ceil-ing them, and update_every_step uses round() (0.3/0.1 -> 3).
- init_maybe_prefetch(PrefetchDelay) resolves the registered delay instead of
  raising TypeError.
…onate argnames

- DCE keeps equations with effects (debug_print, callbacks, jit_error_if) and
  their inputs; constant folding skips effectful equations, including nested jit.
- x - x and 0 * x simplifications only apply to 0-d integer outputs (no wrong
  shape, NaN preserved).
- vmap_new_states / vmap2_new_states roll back pre-existing states written with
  mapped tracers and raise a clear error; states excluded via state_to_exclude
  are poisoned and unwound instead of keeping dead tracers.
- StatefulFunction / make_jaxpr normalize negative static_argnums per call.
- jit infers static/donate argnums from argnames (and vice versa) via the
  function signature, like jax.jit.
- Mixed batched/unbatched leaves in one state are accepted (unbatched leaves
  broadcast).
- Python code generation for scan with consts, const-only body ops and unused
  nested-jaxpr invars produces runnable code.
…add multi-device tests

jax.pmap does not invalidate its inner DynamicJaxprTrace, so the leaked-state
check of pmap2_new_states missed it. A tracer now also counts as leaked when
its trace is not on the current trace's parent_trace chain.

Tests: pmap2 with mixed batched/unbatched state leaves, jit static_argnames
passed positionally under pmap2, pmap2_new_states rollback, per-device
callbacks, and outer-jit tracer writes that must not be flagged.
…t_size

MaxPool(return_indices=True) returned flat positions in the original input,
whose size MaxUnpool cannot recover (4 and 5 both pool to 2), so odd-sized
inputs were silently misplaced without output_size, even across batch
elements. Indices are now flat positions in a layout of
(out - 1) * stride + kernel - pad_lo per pooled axis, which depends only on the
pooled shape; MaxUnpool unravels against it and re-ravels into the natural size
or the requested output_size. Values are unchanged for evenly divisible,
unpadded inputs.
@sourcery-ai

sourcery-ai Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Sorry @chaoming0625, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 6 days and 1 hour by commenting @sourcery-ai review. Upgrade to get a review now.

@chaoming0625
chaoming0625 merged commit c8029ef into main Oct 20, 2026
7 checks passed
@sourcery-ai

sourcery-ai Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR implements a package-wide High/Medium audit remediation: it isolates state and hook metadata, removes hook and environment concurrency hazards, corrects random and neural-network edge-case semantics, and makes transform optimizations and stateful mappings preserve effects and tracer safety, with broad regression coverage and simulated multi-device testing.

Sequence diagram for deadlock-safe state hook execution

sequenceDiagram
    participant State
    participant HookManager
    participant Lock
    participant Hook

    State->>HookManager: execute_write_before_hooks()
    HookManager->>Lock: _get_cached_hooks()
    Lock-->>HookManager: fresh hook snapshot
    HookManager->>Lock: release
    HookManager->>Hook: execute(context)
    Hook-->>HookManager: transformed value or HookCancellationError
    alt hook cancels write
        HookManager-->>State: raise HookCancellationError
        State-->>State: cancel write
    else hook succeeds
        HookManager-->>State: transformed value
    end
Loading

Sequence diagram for tracer-safe mapped state updates

sequenceDiagram
    participant Transform
    participant MappedFunction
    participant TraceStack
    participant State
    participant Cleanup

    Transform->>TraceStack: begin mapped pass
    Transform->>MappedFunction: vmap_new_states() or pmap2_new_states()
    MappedFunction->>State: write mapped tracer
    MappedFunction-->>Transform: mapped result
    Transform->>Cleanup: rollback_leaked_preexisting_states()
    Cleanup->>State: restore original value
    alt pre-existing state received mapped tracer
        Cleanup-->>Transform: leaked states
        Transform->>Transform: raise_leaked_preexisting_states()
    else newly created state
        Cleanup->>TraceStack: unwind_new_state_levels()
        Transform-->>Transform: continue with mapped states
    end
Loading

Flow diagram for size-independent MaxPool and MaxUnpool indices

flowchart LR
    Input[Odd or padded input] --> Pool[MaxPool return_indices]
    Pool --> Layout[Size-independent index layout]
    Pool --> Values[Pooled values]
    Layout --> Unpool[MaxUnpool]
    Values --> Unpool
    OutputSize[Optional output_size] --> Unpool
    Unpool --> Output[Correctly scattered output]
Loading

Flow diagram for side-effect-preserving transform optimization

flowchart LR
    Jaxpr[Jaxpr equations] --> Inspect[Inspect outputs and effects]
    Inspect -->|side effects present| Keep[Keep equation at runtime]
    Inspect -->|no effects and constant inputs| Fold[Constant fold]
    Inspect -->|no effects and unused outputs| Remove[Remove equation]
    Keep --> Optimized[Optimized Jaxpr]
    Fold --> Optimized
    Remove --> Optimized
Loading

File-Level Changes

Change Details Files
Hardened state identity, hook behavior, and hook-manager concurrency semantics.
  • Mode equality and hashing now include semantic parameters and support non-Mode comparisons.
  • State copies, replacements, reference updates, and tree reconstruction isolate hook managers, tracer state, and tags.
  • Hook cancellation preserves write vetoes, error reporting exposes original exceptions, and callbacks execute outside cache locks.
  • Exceptional trace-stack exits clean up leaked entries without masking the original exception.
brainstate/_state.py
brainstate/_state_hook_core.py
brainstate/_state_hook_manager.py
brainstate/_state_test.py
brainstate/_state_hook_manager_test.py
brainstate/_state_thread_safety_test.py
brainstate/mixin.py
brainstate/mixin_test.py
Changed environment configuration to be process-global while preserving per-thread temporary context stacks and coordinating precision overrides.
  • Shared settings and callbacks are visible across worker threads.
  • Overlapping precision contexts restore the active process-wide override correctly.
  • Added regression coverage for cross-thread settings, context isolation, and precision coordination.
brainstate/environ.py
brainstate/environ_test.py
Corrected random distribution semantics and shape handling.
  • Changed pareto to NumPy-compatible Lomax samples.
  • Made multinomial counts exhaustive by deriving the final category probability and clipping categorical indices.
  • Normalized list-shaped sizes for zipf and related random APIs.
brainstate/random/_impl.py
brainstate/random/_state.py
brainstate/random/_state_test.py
changelog.md
Fixed neural-network normalization, pooling, delay, and prefetch edge cases.
  • BatchNorm now computes batch statistics without running-stat tracking and preserves running-stat shapes across feature-axis layouts.
  • MaxPool/MaxUnpool use a pooled-shape-derived index layout that supports odd and padded inputs, while retaining compatible divisible-input indices.
  • Corrected floating-point delay-step rounding, update frequency rounding, and PrefetchDelay lookup.
  • Added constructor and reference-based regression tests, including simulated multi-device coverage.
brainstate/nn/_normalizations.py
brainstate/nn/_normalizations_test.py
brainstate/nn/_poolings.py
brainstate/nn/_poolings_test.py
brainstate/nn/_delay.py
brainstate/nn/_delay_test.py
brainstate/nn/_dynamics.py
brainstate/nn/_dynamics_test.py
brainstate/conftest.py
Made transform and IR processing preserve effects, generate valid nested code, and safely handle mapping and JIT argument edge cases.
  • DCE and constant folding retain effectful equations and avoid unsafe floating-point algebraic rewrites.
  • State mapping transforms roll back tracer writes to pre-existing states, poison and unwind excluded leaked states, detect parent-chain traces, and allow mixed batched/unbatched leaves.
  • Normalized negative static argnums and inferred static/donate argnames and argnums using callable signatures.
  • Preserved nested Jaxpr signatures and corrected generated scan calls so emitted Python executes.
brainstate/transform/_ir_optim.py
brainstate/transform/_ir_utils.py
brainstate/transform/_ir_tocode.py
brainstate/transform/_jit.py
brainstate/transform/_make_jaxpr.py
brainstate/transform/_mapping1.py
brainstate/transform/_mapping2.py
brainstate/transform/_mapping_core.py
brainstate/transform/_ir_optim_test.py
brainstate/transform/_ir_tocode_test.py
brainstate/transform/_jit_test.py
brainstate/transform/_make_jaxpr_test.py
brainstate/transform/_mapping1_test.py
brainstate/transform/_mapping2_test.py
brainstate/transform/_mapping_core_test.py
Documented the audit-fix specification, updated contributor guidance, and recorded user-visible behavior changes.
  • Added the audit specification with implementation decisions, verification guidance, and follow-up items.
  • Updated docstring example guidance.
  • Documented changed pareto, environment, and MaxUnpool behavior.
doc/specs/2026-10-audit-fixes.md
AGENTS.md
changelog.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@chaoming0625
chaoming0625 deleted the fix/audit-high-medium branch October 20, 2026 07:24
@codecov

codecov Bot commented Oct 20, 2026

Copy link
Copy Markdown

@chaoming0625 chaoming0625 mentioned this pull request Oct 20, 2026
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.

1 participant