Skip to content

Conversation

@chaoming0625
Copy link
Member

@chaoming0625 chaoming0625 commented Oct 11, 2025

Summary by Sourcery

Refactor core state Dynamics class to support flexible current and delta input management, update Neuron and Synapse to use the new Dynamics base, adjust projection alignment logic to the new API, remove outdated example notebooks, and bump package version to 2.7.1.

New Features:

  • Add API to register and sum current inputs (add_current_input, sum_current_inputs).
  • Add API to register and sum delta inputs (add_delta_input, sum_delta_inputs).
  • Introduce get_input method to retrieve registered inputs by key.
  • Add align_pre method for chaining execution order of dynamics modules.

Enhancements:

  • Refactor Dynamics to inherit from brainstate.nn.Dynamics with unified input labeling.
  • Update Neuron and Synapse classes to subclass the new Dynamics and revise import paths in examples.
  • Refactor projection alignment functions to use the new Dynamics class and public before_update hooks.

Documentation:

  • Remove obsolete docs_state example notebooks.

Chores:

  • Bump version to 2.7.1.

@sourcery-ai
Copy link

sourcery-ai bot commented Oct 11, 2025

Reviewer's Guide

This PR refactors and enriches the core Dynamics class in brainpy.state to provide a unified input-handling API, updates Neuron/Synapse and projection modules to adopt it, cleans up outdated docs, and bumps the package version to 2.7.1.

Class diagram for refactored Dynamics, Neuron, and Synapse classes

classDiagram
    class Dynamics {
        +__init__(in_size: Size, name: Optional[str] = None)
        +current_inputs: dict | None
        +delta_inputs: dict | None
        +add_current_input(key: str, inp: Callable|ArrayLike, label: Optional[str] = None)
        +add_delta_input(key: str, inp: Callable|ArrayLike, label: Optional[str] = None)
        +get_input(key: str)
        +sum_current_inputs(init: Any, *args, label: Optional[str] = None, **kwargs)
        +sum_delta_inputs(init: Any, *args, label: Optional[str] = None, **kwargs)
        +align_pre(dyn: ParamDescriber[T] | T) -> T
    }
    class Neuron {
    }
    class Synapse {
    }
    Neuron --|> Dynamics
    Synapse --|> Dynamics
Loading

Class diagram for updated projection module using Dynamics

classDiagram
    class _AlignPost {
        +__init__(syn: Dynamics, out: BindCondData)
        +syn: Dynamics
        +out: BindCondData
    }
    class Projection {
    }
    class align_pre_projection {
        +__init__(*spike_generator, syn: Dynamics, comm: Callable, out: SynOut, post: Dynamics, stp: Dynamics = None)
    }
    _AlignPost --|> brainstate.nn.Module
    align_pre_projection --|> Projection
    _AlignPost o-- Dynamics
    align_pre_projection o-- Dynamics
Loading

File-Level Changes

Change Details Files
Enhanced Dynamics class with input handling and chaining
  • Added _current_inputs and _delta_inputs fields in constructor
  • Implemented current_inputs and delta_inputs properties
  • Added add_current_input, add_delta_input, get_input methods
  • Implemented sum_current_inputs and sum_delta_inputs for aggregating inputs
  • Introduced align_pre to chain execution order
  • Added helper functions _input_label_start and _input_label_repr
brainpy/state/_base.py
Updated Neuron and Synapse subclasses and example imports
  • Switched Neuron and Synapse to subclass the new state.Dynamics
  • Revised example and docstring import paths from brainpy.* to brainpy.state.*
brainpy/state/_base.py
Refactored projection module to use state.Dynamics and public before_update API
  • Replaced brainstate.nn.Dynamics references with state.Dynamics
  • Replaced private _has_before_update, _add_before_update, _get_before_update calls with public has_before_update, add_before_update, get_before_update
  • Updated type checks and annotations accordingly
brainpy/state/_projection.py
Removed obsolete documentation notebooks
  • Deleted checkpointing and SNN simulation/training notebooks in docs_state
docs_state/checkpointing-en.ipynb
docs_state/checkpointing-zh.ipynb
docs_state/snn_simulation-en.ipynb
docs_state/snn_simulation-zh.ipynb
docs_state/snn_training-en.ipynb
docs_state/snn_training-zh.ipynb
Bumped package version to 2.7.1
  • Updated version identifier in init.py
brainpy/__init__.py

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

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location> `brainpy/state/_projection.py:131-132` </location>
<code_context>

         # synapse and output initialization
         post.add_current_input(proj_name, out_cls, label=label)
-        post._add_before_update(_post_repr, _AlignPost(syn_cls, out_cls))
-    syn = post._get_before_update(_post_repr).syn
-    out = post._get_before_update(_post_repr).out
+        post.add_before_update(_post_repr, _AlignPost(syn_cls, out_cls))
+    syn = post.get_before_update(_post_repr).syn
+    out = post.get_before_update(_post_repr).out
</code_context>

<issue_to_address>
**suggestion:** Consider documenting the expected interface for Dynamics extensions.

Documenting these required methods will help developers correctly implement extensions to the Dynamics class.

Suggested implementation:

```python
class Dynamics:
    """
    Base class for synaptic dynamics extensions.

    Extensions to Dynamics should implement the following methods:
    - update(self, ...): Update the state of the synapse.
    - reset(self, ...): Reset the synapse state.
    - __call__(self, ...): Compute the synaptic output.

    Additional methods may be required depending on the specific extension.
    See the documentation for details.
    """
    # Existing implementation...

class _AlignPost(brainstate.nn.Module):
    def __init__(
        self,
        syn: Dynamics,
        out: BindCondData
    ):

```

If the `Dynamics` class is defined in another file, you should add the docstring there instead. 
If you have custom extension points or abstract methods, list them explicitly in the docstring.
</issue_to_address>

### Comment 2
<location> `brainpy/state/_base.py:139` </location>
<code_context>
            self._current_inputs = dict()

</code_context>

<issue_to_address>
**suggestion (code-quality):** Replace `dict()` with `{}` ([`dict-literal`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/dict-literal))

```suggestion
            self._current_inputs = {}
```

<br/><details><summary>Explanation</summary>The most concise and Pythonic way to create a dictionary is to use the `{}`
notation.

This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.

```python
x = {"first": "thing"}
```

Doing things this way has the added advantage of being a nice little performance
improvement.

Here are the timings before and after the change:

```
$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
```

```
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop
```

Similar reasoning and performance results hold for replacing `list()` with `[]`.
</details>
</issue_to_address>

### Comment 3
<location> `brainpy/state/_base.py:140-142` </location>
<code_context>
        if key in self._current_inputs:
            if id(self._current_inputs[key]) != id(inp):
                raise ValueError(f'Key "{key}" has been defined and used in the current inputs of {self}.')

</code_context>

<issue_to_address>
**suggestion (code-quality):** Merge nested if conditions ([`merge-nested-ifs`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/merge-nested-ifs))

```suggestion
        if key in self._current_inputs and id(self._current_inputs[key]) != id(inp):
            raise ValueError(f'Key "{key}" has been defined and used in the current inputs of {self}.')

```

<br/><details><summary>Explanation</summary>Too much nesting can make code difficult to understand, and this is especially
true in Python, where there are no brackets to help out with the delineation of
different nesting levels.

Reading deeply nested code is confusing, since you have to keep track of which
conditions relate to which levels. We therefore strive to reduce nesting where
possible, and the situation where two `if` conditions can be combined using
`and` is an easy win.
</details>
</issue_to_address>

### Comment 4
<location> `brainpy/state/_base.py:190` </location>
<code_context>
            self._delta_inputs = dict()

</code_context>

<issue_to_address>
**suggestion (code-quality):** Replace `dict()` with `{}` ([`dict-literal`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/dict-literal))

```suggestion
            self._delta_inputs = {}
```

<br/><details><summary>Explanation</summary>The most concise and Pythonic way to create a dictionary is to use the `{}`
notation.

This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.

```python
x = {"first": "thing"}
```

Doing things this way has the added advantage of being a nice little performance
improvement.

Here are the timings before and after the change:

```
$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
```

```
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop
```

Similar reasoning and performance results hold for replacing `list()` with `[]`.
</details>
</issue_to_address>

### Comment 5
<location> `brainpy/state/_base.py:191-193` </location>
<code_context>
        if key in self._delta_inputs:
            if id(self._delta_inputs[key]) != id(inp):
                raise ValueError(f'Key "{key}" has been defined and used.')

</code_context>

<issue_to_address>
**suggestion (code-quality):** Merge nested if conditions ([`merge-nested-ifs`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/Python/Default-Rules/merge-nested-ifs))

```suggestion
        if key in self._delta_inputs and id(self._delta_inputs[key]) != id(inp):
            raise ValueError(f'Key "{key}" has been defined and used.')

```

<br/><details><summary>Explanation</summary>Too much nesting can make code difficult to understand, and this is especially
true in Python, where there are no brackets to help out with the delineation of
different nesting levels.

Reading deeply nested code is confusing, since you have to keep track of which
conditions relate to which levels. We therefore strive to reduce nesting where
possible, and the situation where two `if` conditions can be combined using
`and` is an easy win.
</details>
</issue_to_address>

### Comment 6
<location> `brainpy/state/_base.py:39` </location>
<code_context>
def _input_label_repr(name: str, label: Optional[str] = None):
    # unify the input label repr.
    return name if label is None else (_input_label_start(label) + str(name))

</code_context>

<issue_to_address>
**suggestion (code-quality):** Remove unnecessary casts to int, str, float or bool ([`remove-unnecessary-cast`](https://docs.sourcery.ai/Reference/Default-Rules/refactorings/remove-unnecessary-cast/))

```suggestion
    return name if label is None else _input_label_start(label) + name
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines -131 to -132
post._add_before_update(_post_repr, _AlignPost(syn_cls, out_cls))
syn = post._get_before_update(_post_repr).syn
Copy link

Choose a reason for hiding this comment

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

suggestion: Consider documenting the expected interface for Dynamics extensions.

Documenting these required methods will help developers correctly implement extensions to the Dynamics class.

Suggested implementation:

class Dynamics:
    """
    Base class for synaptic dynamics extensions.

    Extensions to Dynamics should implement the following methods:
    - update(self, ...): Update the state of the synapse.
    - reset(self, ...): Reset the synapse state.
    - __call__(self, ...): Compute the synaptic output.

    Additional methods may be required depending on the specific extension.
    See the documentation for details.
    """
    # Existing implementation...

class _AlignPost(brainstate.nn.Module):
    def __init__(
        self,
        syn: Dynamics,
        out: BindCondData
    ):

If the Dynamics class is defined in another file, you should add the docstring there instead.
If you have custom extension points or abstract methods, list them explicitly in the docstring.

"""
key = _input_label_repr(key, label)
if self._current_inputs is None:
self._current_inputs = dict()
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Replace dict() with {} (dict-literal)

Suggested change
self._current_inputs = dict()
self._current_inputs = {}


ExplanationThe most concise and Pythonic way to create a dictionary is to use the {}
notation.

This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.

x = {"first": "thing"}

Doing things this way has the added advantage of being a nice little performance
improvement.

Here are the timings before and after the change:

$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop

Similar reasoning and performance results hold for replacing list() with [].

Comment on lines +140 to +142
if key in self._current_inputs:
if id(self._current_inputs[key]) != id(inp):
raise ValueError(f'Key "{key}" has been defined and used in the current inputs of {self}.')
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Merge nested if conditions (merge-nested-ifs)

Suggested change
if key in self._current_inputs:
if id(self._current_inputs[key]) != id(inp):
raise ValueError(f'Key "{key}" has been defined and used in the current inputs of {self}.')
if key in self._current_inputs and id(self._current_inputs[key]) != id(inp):
raise ValueError(f'Key "{key}" has been defined and used in the current inputs of {self}.')


ExplanationToo much nesting can make code difficult to understand, and this is especially
true in Python, where there are no brackets to help out with the delineation of
different nesting levels.

Reading deeply nested code is confusing, since you have to keep track of which
conditions relate to which levels. We therefore strive to reduce nesting where
possible, and the situation where two if conditions can be combined using
and is an easy win.

"""
key = _input_label_repr(key, label)
if self._delta_inputs is None:
self._delta_inputs = dict()
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Replace dict() with {} (dict-literal)

Suggested change
self._delta_inputs = dict()
self._delta_inputs = {}


ExplanationThe most concise and Pythonic way to create a dictionary is to use the {}
notation.

This fits in with the way we create dictionaries with items, saving a bit of
mental energy that might be taken up with thinking about two different ways of
creating dicts.

x = {"first": "thing"}

Doing things this way has the added advantage of being a nice little performance
improvement.

Here are the timings before and after the change:

$ python3 -m timeit "x = dict()"
5000000 loops, best of 5: 69.8 nsec per loop
$ python3 -m timeit "x = {}"
20000000 loops, best of 5: 29.4 nsec per loop

Similar reasoning and performance results hold for replacing list() with [].

Comment on lines +191 to +193
if key in self._delta_inputs:
if id(self._delta_inputs[key]) != id(inp):
raise ValueError(f'Key "{key}" has been defined and used.')
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Merge nested if conditions (merge-nested-ifs)

Suggested change
if key in self._delta_inputs:
if id(self._delta_inputs[key]) != id(inp):
raise ValueError(f'Key "{key}" has been defined and used.')
if key in self._delta_inputs and id(self._delta_inputs[key]) != id(inp):
raise ValueError(f'Key "{key}" has been defined and used.')


ExplanationToo much nesting can make code difficult to understand, and this is especially
true in Python, where there are no brackets to help out with the delineation of
different nesting levels.

Reading deeply nested code is confusing, since you have to keep track of which
conditions relate to which levels. We therefore strive to reduce nesting where
possible, and the situation where two if conditions can be combined using
and is an easy win.


def _input_label_repr(name: str, label: Optional[str] = None):
# unify the input label repr.
return name if label is None else (_input_label_start(label) + str(name))
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Remove unnecessary casts to int, str, float or bool (remove-unnecessary-cast)

Suggested change
return name if label is None else (_input_label_start(label) + str(name))
return name if label is None else _input_label_start(label) + name

@chaoming0625 chaoming0625 merged commit cd22e00 into master Oct 13, 2025
19 checks passed
@chaoming0625 chaoming0625 deleted the update branch October 13, 2025 07:36
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