Skip to content

Commit d684af5

Browse files
authored
Refresh docs notebooks; fix clear_buffer/pmap runtime bugs; drive package-wide mypy to zero (#863)
- Re-executed every docs/ notebook and refreshed outputs; repaired stale API usage. - math/environment.py: clear_buffer_memory() resets JAX's effect-token registry so a later ordered io_callback no longer hits a deleted/donated buffer. - running/jax_multiprocessing.py: jax_parallelize_map uses brainstate.transform.pmap2 and gathers chunks to host before concat (fixes TraceContextError on stateful models and device-placement failure on trailing partial chunks). Added regression tests. - Drove `mypy brainpy/` from 1568 errors to 0 across 123 files (annotation-only).
1 parent 09ebc94 commit d684af5

180 files changed

Lines changed: 63739 additions & 14282 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

brainpy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
)
8787

8888
NeuGroup = NeuGroupNS = dyn.NeuDyn
89-
dyn.DynamicalSystem = DynamicalSystem
89+
setattr(dyn, 'DynamicalSystem', DynamicalSystem)
9090

9191
# common tools
9292
from brainpy.context import (share as share)

brainpy/algorithms/offline.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
# limitations under the License.
1515
# ==============================================================================
1616
import warnings
17+
from typing import Any, Dict, Optional
1718

1819
import jax.numpy as jnp
1920
import numpy as np
@@ -48,7 +49,7 @@
4849
'register_offline_method',
4950
]
5051

51-
name2func = dict()
52+
name2func: Dict[str, Any] = dict()
5253

5354

5455
class OfflineAlgorithm(BrainPyObject):
@@ -78,7 +79,7 @@ def __call__(self, targets, inputs, outputs=None):
7879
"""
7980
return self.call(targets, inputs, outputs)
8081

81-
def call(self, targets, inputs, outputs=None) -> ArrayType:
82+
def call(self, targets, inputs, outputs=None):
8283
"""The training procedure.
8384
8485
Parameters
@@ -132,10 +133,10 @@ class RegressionAlgorithm(OfflineAlgorithm):
132133

133134
def __init__(
134135
self,
135-
max_iter: int = None,
136-
learning_rate: float = None,
137-
regularizer: Regularization = None,
138-
name: str = None
136+
max_iter: Optional[int] = None,
137+
learning_rate: Optional[float] = None,
138+
regularizer: Optional[Regularization] = None,
139+
name: Optional[str] = None
139140
):
140141
super(RegressionAlgorithm, self).__init__(name=name)
141142
self.max_iter = max_iter
@@ -192,7 +193,7 @@ class LinearRegression(RegressionAlgorithm):
192193

193194
def __init__(
194195
self,
195-
name: str = None,
196+
name: Optional[str] = None,
196197

197198
# parameters for using gradient descent
198199
max_iter: int = 1000,
@@ -248,8 +249,8 @@ class RidgeRegression(RegressionAlgorithm):
248249
def __init__(
249250
self,
250251
alpha: float = 1e-7,
251-
beta: float = None,
252-
name: str = None,
252+
beta: Optional[float] = None,
253+
name: Optional[str] = None,
253254

254255
# parameters for using gradient descent
255256
max_iter: int = 1000,
@@ -321,7 +322,7 @@ def __init__(
321322
alpha: float = 1.0,
322323
degree: int = 2,
323324
add_bias: bool = False,
324-
name: str = None,
325+
name: Optional[str] = None,
325326

326327
# parameters for using gradient descent
327328
max_iter: int = 1000,
@@ -378,15 +379,15 @@ def __init__(
378379
learning_rate: float = .1,
379380
gradient_descent: bool = True,
380381
max_iter: int = 4000,
381-
name: str = None,
382+
name: Optional[str] = None,
382383
):
383384
super(LogisticRegression, self).__init__(name=name,
384385
max_iter=max_iter,
385386
learning_rate=learning_rate)
386387
self.gradient_descent = gradient_descent
387388
self.sigmoid = Sigmoid()
388389

389-
def call(self, targets, inputs, outputs=None) -> ArrayType:
390+
def call(self, targets, inputs, outputs=None):
390391
# prepare data
391392
inputs = _check_data_2d_atls(bm.as_jax(inputs))
392393
targets = _check_data_2d_atls(bm.as_jax(targets))
@@ -437,7 +438,7 @@ class PolynomialRegression(LinearRegression):
437438
def __init__(
438439
self,
439440
degree: int = 2,
440-
name: str = None,
441+
name: Optional[str] = None,
441442
add_bias: bool = False,
442443

443444
# parameters for using gradient descent
@@ -472,7 +473,7 @@ def __init__(
472473
self,
473474
alpha: float = 1.0,
474475
degree: int = 2,
475-
name: str = None,
476+
name: Optional[str] = None,
476477
add_bias: bool = False,
477478

478479
# parameters for using gradient descent
@@ -528,7 +529,7 @@ def __init__(
528529
alpha: float = 1.0,
529530
degree: int = 2,
530531
l1_ratio: float = 0.5,
531-
name: str = None,
532+
name: Optional[str] = None,
532533
add_bias: bool = False,
533534

534535
# parameters for using gradient descent

brainpy/algorithms/online.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
'register_online_method',
3434
]
3535

36-
name2func = dict()
36+
name2func: "dict[str, type[OnlineAlgorithm] | OnlineAlgorithm]" = dict()
3737

3838

3939
class OnlineAlgorithm(BrainPyObject):

brainpy/analysis/highdim/slow_points.py

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import math
1818
import time
1919
import warnings
20-
from typing import Callable, Union, Dict, Sequence, Tuple
20+
from typing import Callable, Union, Dict, Sequence, Tuple, Optional, Any, cast
2121

2222
import jax
2323
import jax.numpy as jnp
@@ -124,21 +124,21 @@ class SlowPointFinder(base.DSAnalyzer):
124124
def __init__(
125125
self,
126126
f_cell: Union[Callable, DynamicalSystem],
127-
f_type: str = None,
128-
f_loss: Callable = None,
127+
f_type: Optional[str] = None,
128+
f_loss: Optional[Callable] = None,
129129
verbose: bool = True,
130130
args: Tuple = (),
131131

132132
# parameters for `f_cell` is DynamicalSystem instance
133-
inputs: Sequence = None,
134-
t: float = None,
135-
dt: float = None,
136-
target_vars: Dict[str, bm.Variable] = None,
137-
excluded_vars: Union[Sequence[bm.Variable], Dict[str, bm.Variable]] = None,
133+
inputs: Optional[Sequence] = None,
134+
t: Optional[float] = None,
135+
dt: Optional[float] = None,
136+
target_vars: Optional[Dict[str, bm.Variable]] = None,
137+
excluded_vars: Optional[Union[Sequence[bm.Variable], Dict[str, bm.Variable]]] = None,
138138

139139
# deprecated
140-
f_loss_batch: Callable = None,
141-
fun_inputs: Callable = None,
140+
f_loss_batch: Optional[Callable] = None,
141+
fun_inputs: Optional[Callable] = None,
142142
):
143143
super().__init__()
144144

@@ -149,7 +149,7 @@ def __init__(
149149

150150
# update function
151151
if target_vars is None:
152-
self.target_vars = bm.ArrayCollector()
152+
self.target_vars: bm.ArrayCollector = bm.ArrayCollector()
153153
else:
154154
if not isinstance(target_vars, dict):
155155
raise TypeError(f'"target_vars" must be a dict but we got {type(target_vars)}')
@@ -181,14 +181,14 @@ def __init__(
181181
else:
182182
self.target_vars = all_vars
183183
if len(excluded_vars):
184-
excluded_vars = [id(v) for v in excluded_vars]
184+
excluded_ids = [id(v) for v in excluded_vars]
185185
for key, val in tuple(self.target_vars.items()):
186-
if id(val) in excluded_vars:
186+
if id(val) in excluded_ids:
187187
self.target_vars.pop(key)
188188

189189
# input function
190190
if callable(inputs):
191-
self._inputs = inputs
191+
self._inputs: Any = inputs
192192
else:
193193
if inputs is None:
194194
self._inputs = None
@@ -258,13 +258,13 @@ def __init__(
258258
self.f_loss = f_loss
259259

260260
# essential variables
261-
self._losses = None
262-
self._fixed_points = None
263-
self._selected_ids = None
264-
self._opt_losses = None
261+
self._losses: Any = None
262+
self._fixed_points: Any = None
263+
self._selected_ids: Any = None
264+
self._opt_losses: Any = None
265265

266266
# functions
267-
self._opt_functions = dict()
267+
self._opt_functions: Dict[str, Any] = dict()
268268

269269
@property
270270
def opt_losses(self) -> np.ndarray:
@@ -315,7 +315,7 @@ def find_fps_with_gd_method(
315315
tolerance: Union[float, Dict[str, float]] = 1e-5,
316316
num_batch: int = 100,
317317
num_opt: int = 10000,
318-
optimizer: optim.Optimizer = None,
318+
optimizer: Optional[optim.Optimizer] = None,
319319
):
320320
"""Optimize fixed points with gradient descent methods.
321321
@@ -456,9 +456,9 @@ def find_fps_with_opt_solver(
456456
indices = [0]
457457
for v in candidates.values():
458458
indices.append(v.shape[1])
459-
indices = np.cumsum(indices)
459+
offsets = np.cumsum(indices)
460460
keys = tuple(candidates.keys())
461-
self._fixed_points = {key: fixed_points[:, indices[i]: indices[i + 1]]
461+
self._fixed_points = {key: fixed_points[:, offsets[i]: offsets[i + 1]]
462462
for i, key in enumerate(keys)}
463463
else:
464464
self._fixed_points = fixed_points
@@ -538,7 +538,9 @@ def exclude_outliers(self, tolerance: float = 1e0):
538538
return
539539

540540
# Compute pairwise distances between all fixed points.
541-
distances = np.asarray(utils.euclidean_distance_jax(self.fixed_points, num_fps))
541+
# ``fixed_points`` property returns ndarray-or-dict; the outlier path
542+
# operates on the array branch, so narrow it for the distance helper.
543+
distances = np.asarray(utils.euclidean_distance_jax(cast(jnp.ndarray, self.fixed_points), num_fps))
542544

543545
# Find the second smallest element in each column of the pairwise distance matrix.
544546
# This corresponds to the closest neighbor for each fixed point.
@@ -597,7 +599,9 @@ def compute_jacobians(
597599
else:
598600
raise ValueError('Only support points of 1D: (num_feature,) or 2D: (num_point, num_feature)')
599601
if isinstance(points, dict) and stack_dict_var:
600-
points = jnp.hstack(tuple(points.values()))
602+
# ``points`` is a constrained-TypeVar param; stacking a dict yields a
603+
# concrete jax.Array that mypy cannot reconcile with every constraint.
604+
points = jnp.hstack(tuple(points.values())) # type: ignore[assignment, arg-type]
601605

602606
# get Jacobian matrix
603607
jacobian = self._get_f_jocabian(stack_dict_var)(points)
@@ -761,8 +765,8 @@ def call_opt(x):
761765

762766
def _generate_ds_cell_function(
763767
self, target,
764-
t: float = None,
765-
dt: float = None,
768+
t: Optional[float] = None,
769+
dt: Optional[float] = None,
766770
):
767771
if dt is None: dt = bm.get_dt()
768772
if t is None: t = 0.

brainpy/analysis/lowdim/lowdim_bifurcation.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
# ==============================================================================
1616
from copy import deepcopy
1717
from functools import partial
18+
from typing import Any, Optional
1819

1920
import jax
2021
import jax.numpy as jnp
@@ -26,7 +27,7 @@
2627
from brainpy.analysis import stability, plotstyle, utils, constants as C
2728
from brainpy.analysis.lowdim.lowdim_analyzer import *
2829

29-
pyplot = None
30+
pyplot: Any = None
3031

3132
__all__ = [
3233
'Bifurcation1D',
@@ -380,10 +381,10 @@ def plot_limit_cycle_by_sim(
380381
duration=100,
381382
with_plot: bool = True,
382383
with_return: bool = False,
383-
plot_style: dict = None,
384+
plot_style: Optional[dict] = None,
384385
tol: float = 0.001,
385386
show: bool = False,
386-
dt: float = None,
387+
dt: Optional[float] = None,
387388
offset: float = 1.
388389
):
389390
global pyplot
@@ -404,8 +405,8 @@ def plot_limit_cycle_by_sim(
404405
mon_res = traject_model.run(duration=duration)
405406

406407
# find limit cycles
407-
vs_limit_cycle = tuple({'min': [], 'max': []} for _ in self.target_var_names)
408-
ps_limit_cycle = tuple([] for _ in self.target_par_names)
408+
vs_limit_cycle: tuple = tuple({'min': [], 'max': []} for _ in self.target_var_names)
409+
ps_limit_cycle: tuple = tuple([] for _ in self.target_par_names)
409410
for i in range(mon_res[self.x_var].shape[1]):
410411
data = mon_res[self.x_var][:, i]
411412
max_index = utils.find_indexes_of_limit_cycle_max(data, tol=tol)
@@ -468,10 +469,10 @@ def __init__(
468469
model,
469470
fast_vars: dict,
470471
slow_vars: dict,
471-
fixed_vars: dict = None,
472-
pars_update: dict = None,
472+
fixed_vars: Optional[dict] = None,
473+
pars_update: Optional[dict] = None,
473474
resolutions=None,
474-
options: dict = None
475+
options: Optional[dict] = None
475476
):
476477
super().__init__(model=model,
477478
target_pars=slow_vars,
@@ -559,10 +560,10 @@ def __init__(
559560
model,
560561
fast_vars: dict,
561562
slow_vars: dict,
562-
fixed_vars: dict = None,
563-
pars_update: dict = None,
563+
fixed_vars: Optional[dict] = None,
564+
pars_update: Optional[dict] = None,
564565
resolutions=0.1,
565-
options: dict = None
566+
options: Optional[dict] = None
566567
):
567568
super().__init__(model=model,
568569
target_pars=slow_vars,

brainpy/analysis/utils/others.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def keep_unique(candidates: Union[np.ndarray, Dict[str, np.ndarray]],
134134

135135
# If point A and point B are within identical_tol of each other, and the
136136
# A is first in the list, we keep A.
137-
distances = np.asarray(euclidean_distance_jax(candidates, num_fps))
137+
distances = np.asarray(euclidean_distance_jax(candidates, num_fps)) # type: ignore[arg-type] # candidates is an array here; euclidean_distance_jax annotation omits np.ndarray
138138
example_idxs = np.arange(num_fps)
139139
all_drop_idxs = []
140140
for fidx in range(num_fps - 1):

0 commit comments

Comments
 (0)