brainstate 0.1.0.post20250211__py2.py3-none-any.whl → 0.1.0.post20250216__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. brainstate/_state.py +875 -93
  2. brainstate/_state_test.py +1 -3
  3. brainstate/augment/__init__.py +2 -2
  4. brainstate/augment/_autograd.py +257 -115
  5. brainstate/augment/_autograd_test.py +2 -3
  6. brainstate/augment/_eval_shape.py +3 -4
  7. brainstate/augment/_mapping.py +582 -62
  8. brainstate/augment/_mapping_test.py +114 -30
  9. brainstate/augment/_random.py +61 -7
  10. brainstate/compile/_ad_checkpoint.py +2 -3
  11. brainstate/compile/_conditions.py +4 -5
  12. brainstate/compile/_conditions_test.py +1 -2
  13. brainstate/compile/_error_if.py +1 -2
  14. brainstate/compile/_error_if_test.py +1 -2
  15. brainstate/compile/_jit.py +23 -16
  16. brainstate/compile/_jit_test.py +1 -2
  17. brainstate/compile/_loop_collect_return.py +18 -10
  18. brainstate/compile/_loop_collect_return_test.py +1 -1
  19. brainstate/compile/_loop_no_collection.py +5 -5
  20. brainstate/compile/_make_jaxpr.py +23 -21
  21. brainstate/compile/_make_jaxpr_test.py +1 -2
  22. brainstate/compile/_progress_bar.py +1 -2
  23. brainstate/compile/_unvmap.py +1 -0
  24. brainstate/compile/_util.py +4 -2
  25. brainstate/environ.py +4 -4
  26. brainstate/environ_test.py +1 -2
  27. brainstate/functional/_activations.py +1 -2
  28. brainstate/functional/_activations_test.py +1 -1
  29. brainstate/functional/_normalization.py +1 -2
  30. brainstate/functional/_others.py +1 -2
  31. brainstate/functional/_spikes.py +136 -20
  32. brainstate/graph/_graph_node.py +2 -43
  33. brainstate/graph/_graph_operation.py +4 -20
  34. brainstate/graph/_graph_operation_test.py +3 -4
  35. brainstate/init/_base.py +1 -2
  36. brainstate/init/_generic.py +1 -2
  37. brainstate/nn/__init__.py +4 -0
  38. brainstate/nn/_collective_ops.py +351 -48
  39. brainstate/nn/_collective_ops_test.py +36 -0
  40. brainstate/nn/_common.py +194 -0
  41. brainstate/nn/_dyn_impl/_dynamics_neuron.py +1 -2
  42. brainstate/nn/_dyn_impl/_dynamics_neuron_test.py +1 -2
  43. brainstate/nn/_dyn_impl/_dynamics_synapse.py +1 -2
  44. brainstate/nn/_dyn_impl/_dynamics_synapse_test.py +1 -2
  45. brainstate/nn/_dyn_impl/_inputs.py +1 -2
  46. brainstate/nn/_dyn_impl/_rate_rnns.py +1 -2
  47. brainstate/nn/_dyn_impl/_rate_rnns_test.py +1 -2
  48. brainstate/nn/_dyn_impl/_readout.py +2 -3
  49. brainstate/nn/_dyn_impl/_readout_test.py +1 -2
  50. brainstate/nn/_dynamics/_dynamics_base.py +2 -3
  51. brainstate/nn/_dynamics/_dynamics_base_test.py +1 -2
  52. brainstate/nn/_dynamics/_state_delay.py +3 -3
  53. brainstate/nn/_dynamics/_synouts_test.py +1 -2
  54. brainstate/nn/_elementwise/_dropout.py +6 -7
  55. brainstate/nn/_elementwise/_dropout_test.py +1 -2
  56. brainstate/nn/_elementwise/_elementwise.py +1 -2
  57. brainstate/nn/_exp_euler.py +1 -2
  58. brainstate/nn/_exp_euler_test.py +1 -2
  59. brainstate/nn/_interaction/_conv.py +1 -2
  60. brainstate/nn/_interaction/_conv_test.py +1 -0
  61. brainstate/nn/_interaction/_linear.py +1 -2
  62. brainstate/nn/_interaction/_linear_test.py +1 -2
  63. brainstate/nn/_interaction/_normalizations.py +1 -2
  64. brainstate/nn/_interaction/_poolings.py +3 -4
  65. brainstate/nn/_module.py +63 -19
  66. brainstate/nn/_module_test.py +1 -2
  67. brainstate/nn/metrics.py +3 -4
  68. brainstate/optim/_lr_scheduler.py +1 -2
  69. brainstate/optim/_lr_scheduler_test.py +2 -3
  70. brainstate/optim/_optax_optimizer_test.py +1 -2
  71. brainstate/optim/_sgd_optimizer.py +2 -3
  72. brainstate/random/_rand_funs.py +1 -2
  73. brainstate/random/_rand_funs_test.py +2 -3
  74. brainstate/random/_rand_seed.py +2 -3
  75. brainstate/random/_rand_seed_test.py +1 -2
  76. brainstate/random/_rand_state.py +3 -4
  77. brainstate/surrogate.py +183 -35
  78. brainstate/transform.py +0 -3
  79. brainstate/typing.py +28 -25
  80. brainstate/util/__init__.py +9 -7
  81. brainstate/util/_caller.py +1 -2
  82. brainstate/util/_error.py +27 -0
  83. brainstate/util/_others.py +60 -15
  84. brainstate/util/{_dict.py → _pretty_pytree.py} +108 -29
  85. brainstate/util/{_dict_test.py → _pretty_pytree_test.py} +1 -2
  86. brainstate/util/_pretty_repr.py +128 -10
  87. brainstate/util/_pretty_table.py +2900 -0
  88. brainstate/util/_struct.py +11 -11
  89. brainstate/util/filter.py +472 -0
  90. {brainstate-0.1.0.post20250211.dist-info → brainstate-0.1.0.post20250216.dist-info}/METADATA +2 -2
  91. brainstate-0.1.0.post20250216.dist-info/RECORD +127 -0
  92. brainstate/util/_filter.py +0 -178
  93. brainstate-0.1.0.post20250211.dist-info/RECORD +0 -124
  94. {brainstate-0.1.0.post20250211.dist-info → brainstate-0.1.0.post20250216.dist-info}/LICENSE +0 -0
  95. {brainstate-0.1.0.post20250211.dist-info → brainstate-0.1.0.post20250216.dist-info}/WHEEL +0 -0
  96. {brainstate-0.1.0.post20250211.dist-info → brainstate-0.1.0.post20250216.dist-info}/top_level.txt +0 -0
@@ -21,11 +21,10 @@ from __future__ import annotations
21
21
 
22
22
  import collections
23
23
  import dataclasses
24
+ import jax
24
25
  from collections.abc import Hashable, Mapping
25
26
  from types import MappingProxyType
26
27
  from typing import Any, TypeVar
27
-
28
- import jax
29
28
  from typing_extensions import dataclass_transform # pytype: disable=not-supported-yet
30
29
 
31
30
  __all__ = [
@@ -46,7 +45,8 @@ def field(pytree_node=True, *, metadata=None, **kwargs):
46
45
 
47
46
  @dataclass_transform(field_specifiers=(field,)) # type: ignore[literal-required]
48
47
  def dataclass(clz: T, **kwargs) -> T:
49
- """Create a class which can be passed to functional transformations.
48
+ """
49
+ Create a class which can be passed to functional transformations.
50
50
 
51
51
  .. note::
52
52
  Inherit from ``PyTreeNode`` instead to avoid type checking issues when
@@ -115,7 +115,7 @@ def dataclass(clz: T, **kwargs) -> T:
115
115
  The new class.
116
116
  """
117
117
  # check if already a flax dataclass
118
- if '_flax_dataclass' in clz.__dict__:
118
+ if '_brainstate_dataclass' in clz.__dict__:
119
119
  return clz
120
120
 
121
121
  if 'frozen' not in kwargs.keys():
@@ -172,8 +172,8 @@ def dataclass(clz: T, **kwargs) -> T:
172
172
  iterate_clz,
173
173
  )
174
174
 
175
- # add a _flax_dataclass flag to distinguish from regular dataclasses
176
- data_clz._flax_dataclass = True # type: ignore[attr-defined]
175
+ # add a _brainstate_dataclass flag to distinguish from regular dataclasses
176
+ data_clz._brainstate_dataclass = True # type: ignore[attr-defined]
177
177
 
178
178
  return data_clz # type: ignore
179
179
 
@@ -240,7 +240,9 @@ def _indent(x, num_spaces):
240
240
 
241
241
  @jax.tree_util.register_pytree_with_keys_class
242
242
  class FrozenDict(Mapping[K, V]):
243
- """An immutable variant of the Python dict."""
243
+ """
244
+ An immutable variant of the Python dict.
245
+ """
244
246
 
245
247
  __slots__ = ('_dict', '_hash')
246
248
 
@@ -303,7 +305,8 @@ class FrozenDict(Mapping[K, V]):
303
305
  return self._hash
304
306
 
305
307
  def copy(
306
- self, add_or_replace: Mapping[K, V] = MappingProxyType({})
308
+ self,
309
+ add_or_replace: Mapping[K, V] = MappingProxyType({})
307
310
  ) -> 'FrozenDict[K, V]':
308
311
  """Create a new FrozenDict with additional or replaced entries."""
309
312
  return type(self)({**self, **unfreeze(add_or_replace)}) # type: ignore[arg-type]
@@ -323,7 +326,6 @@ class FrozenDict(Mapping[K, V]):
323
326
 
324
327
  Example::
325
328
 
326
- >>> from flax.core import FrozenDict
327
329
  >>> variables = FrozenDict({'params': {...}, 'batch_stats': {...}})
328
330
  >>> new_variables, params = variables.pop('params')
329
331
 
@@ -426,7 +428,6 @@ def copy(
426
428
 
427
429
  Example::
428
430
 
429
- >>> from flax.core import FrozenDict, copy
430
431
  >>> variables = FrozenDict({'params': {...}, 'batch_stats': {...}})
431
432
  >>> new_variables = copy(variables, {'additional_entries': 1})
432
433
 
@@ -457,7 +458,6 @@ def pop(
457
458
 
458
459
  Example::
459
460
 
460
- >>> from flax.core import FrozenDict, pop
461
461
  >>> variables = FrozenDict({'params': {...}, 'batch_stats': {...}})
462
462
  >>> new_variables, params = pop(variables, 'params')
463
463
 
@@ -0,0 +1,472 @@
1
+ # The file is adapted from the Flax library (https://github.com/google/flax).
2
+ # The credit should go to the Flax authors.
3
+ #
4
+ # Copyright 2024 The Flax Authors.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ from __future__ import annotations
19
+
20
+ import builtins
21
+
22
+ import dataclasses
23
+ import typing
24
+ from typing import TYPE_CHECKING
25
+
26
+ from brainstate.typing import Filter, PathParts, Predicate, Key
27
+
28
+ if TYPE_CHECKING:
29
+ ellipsis = builtins.ellipsis
30
+ else:
31
+ ellipsis = typing.Any
32
+
33
+ __all__ = [
34
+ 'to_predicate',
35
+ 'WithTag',
36
+ 'PathContains',
37
+ 'OfType',
38
+ 'Any',
39
+ 'All',
40
+ 'Nothing',
41
+ 'Not',
42
+ 'Everything',
43
+ ]
44
+
45
+
46
+ def to_predicate(the_filter: Filter) -> Predicate:
47
+ """
48
+ Converts a Filter to a predicate function.
49
+
50
+ This function takes various types of filters and converts them into
51
+ corresponding predicate functions that can be used for filtering.
52
+
53
+ Args:
54
+ the_filter (Filter): The filter to be converted. Can be of various types:
55
+ - str: Converted to a WithTag filter.
56
+ - type: Converted to an OfType filter.
57
+ - bool: True becomes Everything(), False becomes Nothing().
58
+ - Ellipsis: Converted to Everything().
59
+ - None: Converted to Nothing().
60
+ - callable: Returned as-is.
61
+ - list or tuple: Converted to Any filter with elements as arguments.
62
+
63
+ Returns:
64
+ Predicate: A callable predicate function that can be used for filtering.
65
+
66
+ Raises:
67
+ TypeError: If the input filter is of an invalid type.
68
+ """
69
+
70
+ if isinstance(the_filter, str):
71
+ return WithTag(the_filter)
72
+ elif isinstance(the_filter, type):
73
+ return OfType(the_filter)
74
+ elif isinstance(the_filter, bool):
75
+ if the_filter:
76
+ return Everything()
77
+ else:
78
+ return Nothing()
79
+ elif the_filter is Ellipsis:
80
+ return Everything()
81
+ elif the_filter is None:
82
+ return Nothing()
83
+ elif callable(the_filter):
84
+ return the_filter
85
+ elif isinstance(the_filter, (list, tuple)):
86
+ return Any(*the_filter)
87
+ else:
88
+ raise TypeError(f'Invalid collection filter: {the_filter!r}. ')
89
+
90
+
91
+ @dataclasses.dataclass(frozen=True)
92
+ class WithTag:
93
+ """
94
+ A filter class that checks if an object has a specific tag.
95
+
96
+ This class is a callable that can be used as a predicate function
97
+ to filter objects based on their 'tag' attribute.
98
+
99
+ Attributes:
100
+ tag (str): The tag to match against.
101
+ """
102
+
103
+ tag: str
104
+
105
+ def __call__(self, path: PathParts, x: typing.Any) -> bool:
106
+ """
107
+ Check if the object has a 'tag' attribute matching the specified tag.
108
+
109
+ Args:
110
+ path (PathParts): The path to the current object (not used in this filter).
111
+ x (typing.Any): The object to check for the tag.
112
+
113
+ Returns:
114
+ bool: True if the object has a 'tag' attribute matching the specified tag, False otherwise.
115
+ """
116
+ return hasattr(x, 'tag') and x.tag == self.tag
117
+
118
+ def __repr__(self) -> str:
119
+ return f'WithTag({self.tag!r})'
120
+
121
+
122
+ @dataclasses.dataclass(frozen=True)
123
+ class PathContains:
124
+ """
125
+ A filter class that checks if a given key is present in the path.
126
+
127
+ This class is a callable that can be used as a predicate function
128
+ to filter objects based on whether a specific key is present in their path.
129
+
130
+ Attributes:
131
+ key (Key): The key to search for in the path.
132
+ """
133
+
134
+ key: Key
135
+
136
+ def __call__(self, path: PathParts, x: typing.Any) -> bool:
137
+ """
138
+ Check if the key is present in the given path.
139
+
140
+ Args:
141
+ path (PathParts): The path to check for the presence of the key.
142
+ x (typing.Any): The object associated with the path (not used in this filter).
143
+
144
+ Returns:
145
+ bool: True if the key is present in the path, False otherwise.
146
+ """
147
+ return self.key in path
148
+
149
+ def __repr__(self) -> str:
150
+ return f'PathContains({self.key!r})'
151
+
152
+
153
+ @dataclasses.dataclass(frozen=True)
154
+ class OfType:
155
+ """
156
+ A filter class that checks if an object is of a specific type.
157
+
158
+ This class is a callable that can be used as a predicate function
159
+ to filter objects based on their type.
160
+
161
+ Attributes:
162
+ type (type): The type to match against.
163
+ """
164
+ type: type
165
+
166
+ def __call__(self, path: PathParts, x: typing.Any):
167
+ return isinstance(x, self.type) or (
168
+ hasattr(x, 'type') and issubclass(x.type, self.type)
169
+ )
170
+
171
+ def __repr__(self):
172
+ return f'OfType({self.type!r})'
173
+
174
+
175
+ class Any:
176
+ """
177
+ A filter class that combines multiple filters using a logical OR operation.
178
+
179
+ This class creates a composite filter that returns True if any of its
180
+ constituent filters return True.
181
+
182
+ Attributes:
183
+ predicates (tuple): A tuple of predicate functions converted from the input filters.
184
+ """
185
+
186
+ def __init__(self, *filters: Filter):
187
+ """
188
+ Initialize the Any filter with a variable number of filters.
189
+
190
+ Args:
191
+ *filters (Filter): Variable number of filters to be combined.
192
+ """
193
+ self.predicates = tuple(
194
+ to_predicate(collection_filter) for collection_filter in filters
195
+ )
196
+
197
+ def __call__(self, path: PathParts, x: typing.Any) -> bool:
198
+ """
199
+ Apply the composite filter to the given path and object.
200
+
201
+ Args:
202
+ path (PathParts): The path to the current object.
203
+ x (typing.Any): The object to be filtered.
204
+
205
+ Returns:
206
+ bool: True if any of the constituent predicates return True, False otherwise.
207
+ """
208
+ return any(predicate(path, x) for predicate in self.predicates)
209
+
210
+ def __repr__(self) -> str:
211
+ """
212
+ Return a string representation of the Any filter.
213
+
214
+ Returns:
215
+ str: A string representation of the Any filter, including its predicates.
216
+ """
217
+ return f'Any({", ".join(map(repr, self.predicates))})'
218
+
219
+ def __eq__(self, other) -> bool:
220
+ """
221
+ Check if this Any filter is equal to another object.
222
+
223
+ Args:
224
+ other: The object to compare with.
225
+
226
+ Returns:
227
+ bool: True if the other object is an Any filter with the same predicates, False otherwise.
228
+ """
229
+ return isinstance(other, Any) and self.predicates == other.predicates
230
+
231
+ def __hash__(self) -> int:
232
+ """
233
+ Compute the hash value for this Any filter.
234
+
235
+ Returns:
236
+ int: The hash value of the predicates tuple.
237
+ """
238
+ return hash(self.predicates)
239
+
240
+
241
+ class All:
242
+ """
243
+ A filter class that combines multiple filters using a logical AND operation.
244
+
245
+ This class creates a composite filter that returns True only if all of its
246
+ constituent filters return True.
247
+
248
+ Attributes:
249
+ predicates (tuple): A tuple of predicate functions converted from the input filters.
250
+ """
251
+
252
+ def __init__(self, *filters: Filter):
253
+ """
254
+ Initialize the All filter with a variable number of filters.
255
+
256
+ Args:
257
+ *filters (Filter): Variable number of filters to be combined.
258
+ """
259
+ self.predicates = tuple(
260
+ to_predicate(collection_filter) for collection_filter in filters
261
+ )
262
+
263
+ def __call__(self, path: PathParts, x: typing.Any) -> bool:
264
+ """
265
+ Apply the composite filter to the given path and object.
266
+
267
+ Args:
268
+ path (PathParts): The path to the current object.
269
+ x (typing.Any): The object to be filtered.
270
+
271
+ Returns:
272
+ bool: True if all of the constituent predicates return True, False otherwise.
273
+ """
274
+ return all(predicate(path, x) for predicate in self.predicates)
275
+
276
+ def __repr__(self) -> str:
277
+ """
278
+ Return a string representation of the All filter.
279
+
280
+ Returns:
281
+ str: A string representation of the All filter, including its predicates.
282
+ """
283
+ return f'All({", ".join(map(repr, self.predicates))})'
284
+
285
+ def __eq__(self, other) -> bool:
286
+ """
287
+ Check if this All filter is equal to another object.
288
+
289
+ Args:
290
+ other: The object to compare with.
291
+
292
+ Returns:
293
+ bool: True if the other object is an All filter with the same predicates, False otherwise.
294
+ """
295
+ return isinstance(other, All) and self.predicates == other.predicates
296
+
297
+ def __hash__(self) -> int:
298
+ """
299
+ Compute the hash value for this All filter.
300
+
301
+ Returns:
302
+ int: The hash value of the predicates tuple.
303
+ """
304
+ return hash(self.predicates)
305
+
306
+
307
+ class Not:
308
+ """
309
+ A filter class that negates the result of another filter.
310
+
311
+ This class creates a new filter that returns the opposite boolean value
312
+ of the filter it wraps.
313
+
314
+ Attributes:
315
+ predicate (Predicate): The predicate function converted from the input filter.
316
+ """
317
+
318
+ def __init__(self, collection_filter: Filter, /):
319
+ """
320
+ Initialize the Not filter with another filter.
321
+
322
+ Args:
323
+ collection_filter (Filter): The filter to be negated.
324
+ """
325
+ self.predicate = to_predicate(collection_filter)
326
+
327
+ def __call__(self, path: PathParts, x: typing.Any) -> bool:
328
+ """
329
+ Apply the negated filter to the given path and object.
330
+
331
+ Args:
332
+ path (PathParts): The path to the current object.
333
+ x (typing.Any): The object to be filtered.
334
+
335
+ Returns:
336
+ bool: The negation of the result from the wrapped predicate.
337
+ """
338
+ return not self.predicate(path, x)
339
+
340
+ def __repr__(self) -> str:
341
+ """
342
+ Return a string representation of the Not filter.
343
+
344
+ Returns:
345
+ str: A string representation of the Not filter, including its predicate.
346
+ """
347
+ return f'Not({self.predicate!r})'
348
+
349
+ def __eq__(self, other) -> bool:
350
+ """
351
+ Check if this Not filter is equal to another object.
352
+
353
+ Args:
354
+ other: The object to compare with.
355
+
356
+ Returns:
357
+ bool: True if the other object is a Not filter with the same predicate, False otherwise.
358
+ """
359
+ return isinstance(other, Not) and self.predicate == other.predicate
360
+
361
+ def __hash__(self) -> int:
362
+ """
363
+ Compute the hash value for this Not filter.
364
+
365
+ Returns:
366
+ int: The hash value of the predicate.
367
+ """
368
+ return hash(self.predicate)
369
+
370
+
371
+ class Everything:
372
+ """
373
+ A filter class that always returns True for any input.
374
+
375
+ This class represents a filter that matches everything, effectively
376
+ allowing all objects to pass through without any filtering.
377
+ """
378
+
379
+ def __call__(self, path: PathParts, x: typing.Any) -> bool:
380
+ """
381
+ Always return True, regardless of the input.
382
+
383
+ Args:
384
+ path (PathParts): The path to the current object (not used).
385
+ x (typing.Any): The object to be filtered (not used).
386
+
387
+ Returns:
388
+ bool: Always returns True.
389
+ """
390
+ return True
391
+
392
+ def __repr__(self) -> str:
393
+ """
394
+ Return a string representation of the Everything filter.
395
+
396
+ Returns:
397
+ str: The string 'Everything()'.
398
+ """
399
+ return 'Everything()'
400
+
401
+ def __eq__(self, other) -> bool:
402
+ """
403
+ Check if this Everything filter is equal to another object.
404
+
405
+ Args:
406
+ other: The object to compare with.
407
+
408
+ Returns:
409
+ bool: True if the other object is an instance of Everything, False otherwise.
410
+ """
411
+ return isinstance(other, Everything)
412
+
413
+ def __hash__(self) -> int:
414
+ """
415
+ Compute the hash value for this Everything filter.
416
+
417
+ Returns:
418
+ int: The hash value of the Everything class.
419
+ """
420
+ return hash(Everything)
421
+
422
+
423
+ class Nothing:
424
+ """
425
+ A filter class that always returns False for any input.
426
+
427
+ This class represents a filter that matches nothing, effectively
428
+ filtering out all objects.
429
+ """
430
+
431
+ def __call__(self, path: PathParts, x: typing.Any) -> bool:
432
+ """
433
+ Always return False, regardless of the input.
434
+
435
+ Args:
436
+ path (PathParts): The path to the current object (not used).
437
+ x (typing.Any): The object to be filtered (not used).
438
+
439
+ Returns:
440
+ bool: Always returns False.
441
+ """
442
+ return False
443
+
444
+ def __repr__(self) -> str:
445
+ """
446
+ Return a string representation of the Nothing filter.
447
+
448
+ Returns:
449
+ str: The string 'Nothing()'.
450
+ """
451
+ return 'Nothing()'
452
+
453
+ def __eq__(self, other) -> bool:
454
+ """
455
+ Check if this Nothing filter is equal to another object.
456
+
457
+ Args:
458
+ other: The object to compare with.
459
+
460
+ Returns:
461
+ bool: True if the other object is an instance of Nothing, False otherwise.
462
+ """
463
+ return isinstance(other, Nothing)
464
+
465
+ def __hash__(self) -> int:
466
+ """
467
+ Compute the hash value for this Nothing filter.
468
+
469
+ Returns:
470
+ int: The hash value of the Nothing class.
471
+ """
472
+ return hash(Nothing)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: brainstate
3
- Version: 0.1.0.post20250211
3
+ Version: 0.1.0.post20250216
4
4
  Summary: A ``State``-based Transformation System for Program Compilation and Augmentation.
5
5
  Home-page: https://github.com/chaobrain/brainstate
6
6
  Author: BrainState Developers
@@ -48,7 +48,7 @@ Requires-Dist: jaxlib[tpu] ; extra == 'tpu'
48
48
 
49
49
 
50
50
  <p align="center">
51
- <img alt="Header image of brainstate." src="https://github.com/chaobrain/brainstate/blob/main/docs/_static/brainstate.png" width=50%>
51
+ <img alt="Header image of brainstate." src="https://github.com/chaobrain/brainstate/blob/main/docs/_static/brainstate.png" width=40%>
52
52
  </p>
53
53
 
54
54