braintrace 0.2.6__tar.gz → 0.2.7__tar.gz
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.
- {braintrace-0.2.6 → braintrace-0.2.7}/PKG-INFO +4 -4
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/base.py +7 -2
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/d_rtrl_test.py +90 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/io_dim_vjp.py +38 -7
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/io_dim_vjp_test.py +21 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/param_dim_vjp.py +29 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/random_projection_vjp.py +33 -11
- braintrace-0.2.7/braintrace/_algorithm/tests/output_bypass_test.py +362 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/scan_descent_support_test.py +8 -13
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/while_support_test.py +116 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/vjp_base.py +115 -14
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/vjp_graph_executor.py +45 -6
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compatible_imports.py +24 -2
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/base.py +8 -7
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/canonicalize.py +4 -3
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/diagnostics.py +11 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/graph.py +22 -1
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/hid_param_op.py +14 -10
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/hidden_group.py +22 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/hidden_group_test.py +113 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/hidden_pertubation.py +239 -19
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/hidden_pertubation_test.py +62 -19
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/module_info.py +21 -8
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/module_info_test.py +83 -0
- braintrace-0.2.7/braintrace/_compiler/output_bypass.py +277 -0
- braintrace-0.2.7/braintrace/_compiler/output_bypass_test.py +220 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/scan_descent.py +15 -14
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/oracle_models_test.py +9 -6
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_version.py +1 -1
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace.egg-info/PKG-INFO +4 -4
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace.egg-info/SOURCES.txt +3 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace.egg-info/requires.txt +1 -1
- {braintrace-0.2.6 → braintrace-0.2.7}/pyproject.toml +5 -5
- {braintrace-0.2.6 → braintrace-0.2.7}/LICENSE +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/README.md +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/__init__.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/__init___test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/__init__.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/_common.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/_common_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/axes.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/axes_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/base_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/d_rtrl.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/dni.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/dni_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/e_prop.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/e_prop_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/graph_executor.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/graph_executor_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/ostl.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/ostl_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/param_dim_vjp_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/pp_prop.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/pp_prop_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/random_projection_vjp_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/sequence.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/sequence_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/snap_n.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/snap_n_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/approx_correctness_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/axis_acceptance_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/axis_discrimination_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/axis_golden_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/conv_vmap_correctness_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/cross_check_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/diagnostic_exploration_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/exact_correctness_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/public_api_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/scan_fusion_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/snn_model_correctness_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/tests/transform_correctness_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/three_factor.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/three_factor_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/uoro.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/uoro_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/vjp_base_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_algorithm/vjp_graph_executor_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compatible_imports_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compile.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compile_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/__init__.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/base_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/canonicalize_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/diagnostics_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/graph_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/hid_param_op_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/jaxpr_graph.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/jaxpr_graph_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/position_graph.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/position_graph_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/report.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/report_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/scan_descent_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/tests/cell_relation_guardrail_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/tests/compiler_oracle_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_compiler/tests/compiler_property_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_grad_exponential.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_grad_exponential_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_input_data.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_input_data_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_legacy/__init__.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_legacy/_ops.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_legacy/_ops_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_legacy/_params.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_legacy/_params_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_misc.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_misc_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/__init__.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/_primitive.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/_primitive_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/_registries.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/_registries_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/conv.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/conv_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/dense.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/dense_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/einsum.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/einsum_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/elemwise.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/elemwise_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/embedding.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/embedding_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/grouped.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/grouped_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/lora.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/lora_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/op_rule_oracle.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/op_rule_oracle_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/sparse.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_op/sparse_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_state_management.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_state_management_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/__init__.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/compiler_models.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/models.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/oracle.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/oracle_models.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/oracle_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/scenario_catalog.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_testing/scenario_catalog_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_typing.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_typing_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/_version_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/__init__.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/__init___test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_conv.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_conv_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_embedding.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_embedding_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_linear.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_linear_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_readout.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_readout_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_rnn.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/nn/_rnn_test.py +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace/py.typed +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace.egg-info/dependency_links.txt +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/braintrace.egg-info/top_level.txt +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/setup.cfg +0 -0
- {braintrace-0.2.6 → braintrace-0.2.7}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: braintrace
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Enabling Scalable Online Learning for Brain Dynamics.
|
|
5
5
|
Author-email: BrainTrace Developers <chao.brain@qq.com>
|
|
6
6
|
License: Apache-2.0 license
|
|
@@ -13,20 +13,20 @@ Classifier: Natural Language :: English
|
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
14
|
Classifier: Programming Language :: Python
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
20
20
|
Classifier: Intended Audience :: Science/Research
|
|
21
21
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
22
22
|
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
23
23
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
24
24
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
25
25
|
Classifier: Topic :: Software Development :: Libraries
|
|
26
|
-
Requires-Python: >=3.
|
|
26
|
+
Requires-Python: >=3.12
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
License-File: LICENSE
|
|
29
|
-
Requires-Dist: jax>=0.
|
|
29
|
+
Requires-Dist: jax>=0.9.0
|
|
30
30
|
Requires-Dist: brainstate>=0.5.2
|
|
31
31
|
Requires-Dist: brainunit
|
|
32
32
|
Requires-Dist: brainevent>=0.1.2
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
from __future__ import annotations
|
|
23
23
|
|
|
24
|
-
from typing import Dict, Any, Optional
|
|
24
|
+
from typing import Dict, Any, Optional, Tuple, cast
|
|
25
25
|
|
|
26
26
|
import brainstate
|
|
27
27
|
|
|
@@ -257,11 +257,16 @@ class ETraceAlgorithm(SequenceDriverMixin, brainstate.nn.Module):
|
|
|
257
257
|
# - the weight states, which is invariant during the training process
|
|
258
258
|
# - the hidden states, the recurrent states, which may be changed between different training epochs
|
|
259
259
|
# - the other states, which may be changed between different training epochs
|
|
260
|
+
# ``FlattedDict.split`` returns one ``FlattedDict`` per filter (the
|
|
261
|
+
# stubs only promise a ``NestedDict``)
|
|
260
262
|
(
|
|
261
263
|
self._param_states,
|
|
262
264
|
self._hidden_states,
|
|
263
265
|
self._other_states
|
|
264
|
-
) =
|
|
266
|
+
) = cast(
|
|
267
|
+
Tuple[brainstate.util.FlattedDict, brainstate.util.FlattedDict, brainstate.util.FlattedDict],
|
|
268
|
+
self.graph.module_info.retrieved_model_states.split(brainstate.ParamState, brainstate.HiddenState, ...),
|
|
269
|
+
)
|
|
265
270
|
|
|
266
271
|
def compile_graph(self, *args: Any) -> None:
|
|
267
272
|
r"""
|
|
@@ -1112,3 +1112,93 @@ class TestCoupledTraceBoundedness:
|
|
|
1112
1112
|
assert all(bool(jnp.all(jnp.isfinite(leaf))) for leaf in leaves)
|
|
1113
1113
|
max_abs = max(float(jnp.max(jnp.abs(leaf))) for leaf in leaves)
|
|
1114
1114
|
assert max_abs < 1e3, f'eligibility trace grew to {max_abs}'
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
# ---------------------------------------------------------------------------
|
|
1118
|
+
# Hidden-to-hidden edges without an equation (#184)
|
|
1119
|
+
# ---------------------------------------------------------------------------
|
|
1120
|
+
|
|
1121
|
+
class _LatchModel(brainstate.nn.Module):
|
|
1122
|
+
"""``latch`` depends only on the input; ``V`` reads the old latch.
|
|
1123
|
+
|
|
1124
|
+
``workaround=True`` writes ``latch <- new + 0 * latch`` (the pre-fix
|
|
1125
|
+
workaround), which keeps a hidden-to-hidden edge in the jaxpr.
|
|
1126
|
+
"""
|
|
1127
|
+
|
|
1128
|
+
def __init__(self, workaround=False, n_in=3, n=4):
|
|
1129
|
+
super().__init__()
|
|
1130
|
+
self.workaround = workaround
|
|
1131
|
+
self.n = n
|
|
1132
|
+
self.w = brainstate.ParamState(0.5 * jax.random.normal(jax.random.PRNGKey(0), (n_in, n)))
|
|
1133
|
+
|
|
1134
|
+
def init_state(self, batch_size=None, **kwargs):
|
|
1135
|
+
shape = (self.n,) if batch_size is None else (batch_size, self.n)
|
|
1136
|
+
self.latch = brainstate.HiddenState(jnp.zeros(shape))
|
|
1137
|
+
self.V = brainstate.HiddenState(jnp.zeros(shape))
|
|
1138
|
+
|
|
1139
|
+
def update(self, x):
|
|
1140
|
+
I = self.latch.value
|
|
1141
|
+
new = braintrace.matmul(x, self.w.value)
|
|
1142
|
+
self.latch.value = new + 0 * self.latch.value if self.workaround else new
|
|
1143
|
+
self.V.value = 0.9 * self.V.value + I
|
|
1144
|
+
return jnp.tanh(self.V.value)
|
|
1145
|
+
|
|
1146
|
+
|
|
1147
|
+
class _DelayLineModel(brainstate.nn.Module):
|
|
1148
|
+
"""Two-step delay line ``h2^t = h1^{t-1}`` (a pure copy) feeding ``V``."""
|
|
1149
|
+
|
|
1150
|
+
def __init__(self, workaround=False, n_in=3, n=4):
|
|
1151
|
+
super().__init__()
|
|
1152
|
+
self.workaround = workaround
|
|
1153
|
+
self.n = n
|
|
1154
|
+
self.w = brainstate.ParamState(0.5 * jax.random.normal(jax.random.PRNGKey(1), (n_in, n)))
|
|
1155
|
+
|
|
1156
|
+
def init_state(self, batch_size=None, **kwargs):
|
|
1157
|
+
shape = (self.n,) if batch_size is None else (batch_size, self.n)
|
|
1158
|
+
self.h1 = brainstate.HiddenState(jnp.zeros(shape))
|
|
1159
|
+
self.h2 = brainstate.HiddenState(jnp.zeros(shape))
|
|
1160
|
+
self.V = brainstate.HiddenState(jnp.zeros(shape))
|
|
1161
|
+
|
|
1162
|
+
def update(self, x):
|
|
1163
|
+
old1, old2 = self.h1.value, self.h2.value
|
|
1164
|
+
self.V.value = 0.9 * self.V.value + old2
|
|
1165
|
+
self.h1.value = 0.5 * old1 + braintrace.matmul(x, self.w.value)
|
|
1166
|
+
self.h2.value = old1 + 0 * old2 if self.workaround else old1
|
|
1167
|
+
return jnp.tanh(self.V.value)
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
class TestEquationFreeHiddenEdges:
|
|
1171
|
+
"""Input-only latches and pure copies train exactly under D-RTRL (#184)."""
|
|
1172
|
+
|
|
1173
|
+
xs = jax.random.uniform(jax.random.PRNGKey(7), (8, 1, 3))
|
|
1174
|
+
|
|
1175
|
+
@pytest.mark.parametrize('model_cls', [_LatchModel, _DelayLineModel])
|
|
1176
|
+
@pytest.mark.parametrize('chunk_size', [1, 3])
|
|
1177
|
+
def test_d_rtrl_matches_bptt(self, model_cls, chunk_size):
|
|
1178
|
+
from braintrace._testing.oracle import (
|
|
1179
|
+
assert_model_is_live, assert_param_gradients_close,
|
|
1180
|
+
bptt_param_gradients, chunked_online_param_gradients,
|
|
1181
|
+
)
|
|
1182
|
+
assert_model_is_live(model_cls, self.xs)
|
|
1183
|
+
online = chunked_online_param_gradients(
|
|
1184
|
+
model_cls, self.xs, chunk_size=chunk_size,
|
|
1185
|
+
algo_factory=lambda m: braintrace.D_RTRL(m, vjp_method='multi-step'))
|
|
1186
|
+
assert_param_gradients_close(online, bptt_param_gradients(model_cls, self.xs), atol=1e-5)
|
|
1187
|
+
|
|
1188
|
+
@pytest.mark.parametrize('model_cls', [_LatchModel, _DelayLineModel])
|
|
1189
|
+
@pytest.mark.parametrize('algo', ['d_rtrl', 'eprop'])
|
|
1190
|
+
def test_matches_workaround(self, model_cls, algo):
|
|
1191
|
+
xs = self.xs[:, 0]
|
|
1192
|
+
ys = jnp.full((xs.shape[0], 4), 0.3)
|
|
1193
|
+
|
|
1194
|
+
def grads(workaround):
|
|
1195
|
+
net = model_cls(workaround)
|
|
1196
|
+
brainstate.nn.init_all_states(net)
|
|
1197
|
+
learner = braintrace.compile(net, algo, xs[0])
|
|
1198
|
+
return learner.etrace_grad(
|
|
1199
|
+
xs, ys, step_fn=lambda x, y: jnp.mean((learner(x) - y) ** 2))
|
|
1200
|
+
|
|
1201
|
+
fixed, ref = grads(False), grads(True)
|
|
1202
|
+
for k in ref:
|
|
1203
|
+
assert float(jnp.abs(ref[k]).sum()) > 1e-6
|
|
1204
|
+
npt.assert_allclose(fixed[k], ref[k], atol=1e-6)
|
|
@@ -34,6 +34,7 @@ import jax
|
|
|
34
34
|
import jax.numpy as jnp
|
|
35
35
|
import brainunit as u
|
|
36
36
|
|
|
37
|
+
from braintrace._compatible_imports import shaped_aval
|
|
37
38
|
from braintrace._compiler import ControlFlowPolicy, HiddenGroup, HiddenParamOpRelation
|
|
38
39
|
from braintrace._op import (
|
|
39
40
|
etp_elemwise_p,
|
|
@@ -262,8 +263,8 @@ def _init_IO_dim_state(
|
|
|
262
263
|
if x_key not in etrace_xs:
|
|
263
264
|
x_repr_fn = get_pp_x_repr(relation.primitive)
|
|
264
265
|
if x_repr_fn is None:
|
|
265
|
-
shape = relation.x_var.
|
|
266
|
-
dtype = relation.x_var.
|
|
266
|
+
shape = shaped_aval(relation.x_var).shape
|
|
267
|
+
dtype = shaped_aval(relation.x_var).dtype
|
|
267
268
|
else:
|
|
268
269
|
# the trace filters the primitive's x *representation*
|
|
269
270
|
# (e.g. embedding: the one-hot encoding of its integer
|
|
@@ -272,12 +273,12 @@ def _init_IO_dim_state(
|
|
|
272
273
|
x_aval = jax.eval_shape(
|
|
273
274
|
lambda x_, _fn=x_repr_fn, _w=weight_avals: _fn(x_, _w),
|
|
274
275
|
jax.ShapeDtypeStruct(
|
|
275
|
-
relation.x_var.
|
|
276
|
+
shaped_aval(relation.x_var).shape, shaped_aval(relation.x_var).dtype),
|
|
276
277
|
)
|
|
277
278
|
shape, dtype = x_aval.shape, x_aval.dtype
|
|
278
279
|
etrace_xs[x_key] = EligibilityTrace(u.math.zeros(shape, dtype))
|
|
279
280
|
|
|
280
|
-
y_shape = relation.y_var.
|
|
281
|
+
y_shape = shaped_aval(relation.y_var).shape
|
|
281
282
|
group: HiddenGroup
|
|
282
283
|
for group in relation.hidden_groups:
|
|
283
284
|
# Exact match required, or (elemwise only) allow trailing-dim match
|
|
@@ -613,7 +614,7 @@ def _solve_IO_dim_weight_gradients(
|
|
|
613
614
|
# gate the branch.)
|
|
614
615
|
elemwise_batched = (
|
|
615
616
|
relation.primitive is etp_elemwise_p
|
|
616
|
-
and len(group.varshape) > relation.y_var.
|
|
617
|
+
and len(group.varshape) > shaped_aval(relation.y_var).ndim
|
|
617
618
|
)
|
|
618
619
|
|
|
619
620
|
if fast_solve:
|
|
@@ -926,8 +927,9 @@ class IODimVjpAlgorithm(ETraceVjpAlgorithm):
|
|
|
926
927
|
find_this_weight = True
|
|
927
928
|
|
|
928
929
|
# get the weight_op input
|
|
929
|
-
|
|
930
|
-
if
|
|
930
|
+
# (an element-wise relation has no x trace)
|
|
931
|
+
if relation.x_var is not None:
|
|
932
|
+
wx_var = etrace_x_key(relation.x_var)
|
|
931
933
|
etrace_xs[wx_var] = self.etrace_xs[wx_var].value
|
|
932
934
|
|
|
933
935
|
# get the weight_op df
|
|
@@ -1106,6 +1108,35 @@ class IODimVjpAlgorithm(ETraceVjpAlgorithm):
|
|
|
1106
1108
|
|
|
1107
1109
|
return hist_etrace_vals
|
|
1108
1110
|
|
|
1111
|
+
def _solve_previous_trace_gradients(
|
|
1112
|
+
self,
|
|
1113
|
+
running_index: int,
|
|
1114
|
+
etrace_h2w_at_t_minus_1: Tuple[
|
|
1115
|
+
Dict[ETraceX_Key, jax.Array],
|
|
1116
|
+
Dict[ETraceDF_Key, jax.Array]
|
|
1117
|
+
],
|
|
1118
|
+
dl_to_hidden_groups: Sequence[jax.Array],
|
|
1119
|
+
weight_vals: Dict[Path, PyTree],
|
|
1120
|
+
) -> Dict[Path, PyTree]:
|
|
1121
|
+
"""Contract the previous trace with the hidden output-bypass signal.
|
|
1122
|
+
|
|
1123
|
+
See :meth:`ETraceVjpAlgorithm._solve_previous_trace_gradients`. The
|
|
1124
|
+
previous trace is one update younger, so its warm-up bias correction
|
|
1125
|
+
is indexed by ``running_index - 1``.
|
|
1126
|
+
"""
|
|
1127
|
+
dG_weights: Dict[Path, Any] = {path: None for path in self.param_states.keys()}
|
|
1128
|
+
_solve_IO_dim_weight_gradients(
|
|
1129
|
+
etrace_h2w_at_t_minus_1,
|
|
1130
|
+
dG_weights,
|
|
1131
|
+
dl_to_hidden_groups,
|
|
1132
|
+
self.graph.hidden_param_op_relations,
|
|
1133
|
+
weight_vals,
|
|
1134
|
+
running_index - 1,
|
|
1135
|
+
self.decay_f,
|
|
1136
|
+
fast_solve=self.fast_solve,
|
|
1137
|
+
)
|
|
1138
|
+
return dG_weights
|
|
1139
|
+
|
|
1109
1140
|
def _solve_weight_gradients(
|
|
1110
1141
|
self,
|
|
1111
1142
|
running_index: int,
|
|
@@ -247,6 +247,27 @@ class TestStateLifecycle:
|
|
|
247
247
|
assert isinstance(result, tuple)
|
|
248
248
|
assert len(result) == 2
|
|
249
249
|
|
|
250
|
+
def test_get_etrace_of_elementwise_weight(self):
|
|
251
|
+
# An element-wise relation has no x trace; only its df traces are returned.
|
|
252
|
+
class Leaky(brainstate.nn.Module):
|
|
253
|
+
def __init__(self):
|
|
254
|
+
super().__init__()
|
|
255
|
+
self.w = brainstate.ParamState(jnp.full((3,), 0.5))
|
|
256
|
+
|
|
257
|
+
def init_state(self, *args, **kwargs):
|
|
258
|
+
self.h = brainstate.HiddenState(jnp.zeros(3))
|
|
259
|
+
|
|
260
|
+
def update(self, x):
|
|
261
|
+
self.h.value = 0.9 * self.h.value + braintrace.element_wise(self.w.value) * x
|
|
262
|
+
return self.h.value
|
|
263
|
+
|
|
264
|
+
model = Leaky()
|
|
265
|
+
brainstate.nn.init_all_states(model)
|
|
266
|
+
algo = _compiled(model)
|
|
267
|
+
xs, dfs = algo.get_etrace_of(model.w)
|
|
268
|
+
assert xs == {}
|
|
269
|
+
assert len(dfs) == 1
|
|
270
|
+
|
|
250
271
|
def test_get_etrace_of_plain_weight_raises(self):
|
|
251
272
|
model = _build(om.tanh_rnn)
|
|
252
273
|
algo = _compiled(model)
|
|
@@ -1408,6 +1408,35 @@ class ParamDimVjpAlgorithm(ETraceVjpAlgorithm):
|
|
|
1408
1408
|
|
|
1409
1409
|
return new_etrace
|
|
1410
1410
|
|
|
1411
|
+
def _solve_previous_trace_gradients(
|
|
1412
|
+
self,
|
|
1413
|
+
running_index: int,
|
|
1414
|
+
etrace_h2w_at_t_minus_1: Dict[ETraceWG_Key, PyTree],
|
|
1415
|
+
dl_to_hidden_groups: Sequence[jax.Array],
|
|
1416
|
+
weight_vals: Dict[Path, PyTree],
|
|
1417
|
+
) -> Dict[Path, PyTree]:
|
|
1418
|
+
"""Contract the previous trace with the hidden output-bypass signal.
|
|
1419
|
+
|
|
1420
|
+
See :meth:`ETraceVjpAlgorithm._solve_previous_trace_gradients`. With
|
|
1421
|
+
trace filters active, the previous *filtered* trace (the filter's
|
|
1422
|
+
current value, not yet advanced this step) is the one paired.
|
|
1423
|
+
"""
|
|
1424
|
+
etrace = dict(etrace_h2w_at_t_minus_1)
|
|
1425
|
+
if self._trace_filters:
|
|
1426
|
+
for key, flt in self._trace_filters.items():
|
|
1427
|
+
if key in etrace:
|
|
1428
|
+
etrace[key] = flt.value
|
|
1429
|
+
dG_weights: Dict[Path, Any] = {path: None for path in self.param_states}
|
|
1430
|
+
_solve_param_dim_weight_gradients(
|
|
1431
|
+
etrace,
|
|
1432
|
+
dG_weights,
|
|
1433
|
+
dl_to_hidden_groups,
|
|
1434
|
+
self.graph.hidden_param_op_relations,
|
|
1435
|
+
weight_vals,
|
|
1436
|
+
fast_solve=self.fast_solve,
|
|
1437
|
+
)
|
|
1438
|
+
return dG_weights
|
|
1439
|
+
|
|
1411
1440
|
def _solve_weight_gradients(
|
|
1412
1441
|
self,
|
|
1413
1442
|
running_index: int,
|
|
@@ -665,6 +665,36 @@ class RandomProjectionVjpAlgorithm(ETraceVjpAlgorithm):
|
|
|
665
665
|
# the boundary contraction
|
|
666
666
|
# ------------------------------------------------------------------ #
|
|
667
667
|
|
|
668
|
+
def _solve_previous_trace_gradients(
|
|
669
|
+
self,
|
|
670
|
+
running_index: int,
|
|
671
|
+
etrace_h2w_at_t_minus_1: Dict[str, Any],
|
|
672
|
+
dl_to_hidden_groups: Sequence[jax.Array],
|
|
673
|
+
weight_vals: Dict[Path, PyTree],
|
|
674
|
+
) -> Dict[Path, PyTree]:
|
|
675
|
+
"""Contract the previous trace with the hidden output-bypass signal.
|
|
676
|
+
|
|
677
|
+
See :meth:`ETraceVjpAlgorithm._solve_previous_trace_gradients`.
|
|
678
|
+
"""
|
|
679
|
+
return self._contract_trace(etrace_h2w_at_t_minus_1, dl_to_hidden_groups)
|
|
680
|
+
|
|
681
|
+
def _contract_trace(
|
|
682
|
+
self,
|
|
683
|
+
etrace: Dict[str, Any],
|
|
684
|
+
dl_to_hidden_groups: Sequence[jax.Array],
|
|
685
|
+
) -> Dict[Path, Any]:
|
|
686
|
+
# ``sum_g (signal_g . s_tilde_g) * theta_tilde_g``
|
|
687
|
+
dG_weights: Dict[Path, Any] = {path: None for path in self.param_states}
|
|
688
|
+
for group in self.graph.hidden_groups:
|
|
689
|
+
gi = group.index
|
|
690
|
+
signal = u.get_mantissa(dl_to_hidden_groups[gi])
|
|
691
|
+
s_tilde = etrace[_S_TILDE][gi]
|
|
692
|
+
scale = jnp.sum(signal * s_tilde)
|
|
693
|
+
for path in self._etp_paths_of_group(group):
|
|
694
|
+
theta = etrace[_THETA_TILDE][(gi, path)]
|
|
695
|
+
_update_dict(dG_weights, path, _scale_tree(theta, scale))
|
|
696
|
+
return dG_weights
|
|
697
|
+
|
|
668
698
|
def _solve_weight_gradients(
|
|
669
699
|
self,
|
|
670
700
|
running_index: int,
|
|
@@ -679,22 +709,14 @@ class RandomProjectionVjpAlgorithm(ETraceVjpAlgorithm):
|
|
|
679
709
|
A scalar per group times a parameter-shaped array — the whole reason the
|
|
680
710
|
parameter factor carries no hidden axis.
|
|
681
711
|
"""
|
|
682
|
-
dG_weights
|
|
683
|
-
|
|
684
|
-
for group in self.graph.hidden_groups:
|
|
685
|
-
gi = group.index
|
|
686
|
-
signal = u.get_mantissa(dl_to_hidden_groups[gi])
|
|
687
|
-
s_tilde = etrace_h2w_at_t[_S_TILDE][gi]
|
|
688
|
-
scale = jnp.sum(signal * s_tilde)
|
|
689
|
-
for path in self._etp_paths_of_group(group):
|
|
690
|
-
theta = etrace_h2w_at_t[_THETA_TILDE][(gi, path)]
|
|
691
|
-
_update_dict(dG_weights, path, _scale_tree(theta, scale))
|
|
712
|
+
dG_weights = self._contract_trace(etrace_h2w_at_t, dl_to_hidden_groups)
|
|
692
713
|
|
|
693
714
|
# the non-etrace weight gradients (reverse-AD, in-window)
|
|
694
715
|
for path, dg in dl_to_nonetws_at_t.items():
|
|
695
716
|
_update_dict(dG_weights, path, dg)
|
|
696
717
|
|
|
697
|
-
# the in-window direct term for etrace parameters (multi-step
|
|
718
|
+
# the in-window direct term for etrace parameters (multi-step), or
|
|
719
|
+
# the same-step output-bypass term (single-step, #185)
|
|
698
720
|
if dl_to_etws_at_t is not None:
|
|
699
721
|
for path, dg in dl_to_etws_at_t.items():
|
|
700
722
|
_update_dict(dG_weights, path, dg, error_when_no_key=True)
|