braintrace 0.2.8__tar.gz → 0.3.1__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.8 → braintrace-0.3.1}/PKG-INFO +2 -2
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/base.py +27 -16
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/e_prop_test.py +90 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/param_dim_vjp.py +376 -97
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/random_projection_vjp.py +9 -11
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/sequence.py +34 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/sequence_test.py +57 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/snap_n.py +4 -3
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/snap_n_test.py +300 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/conv_vmap_correctness_test.py +81 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/exact_correctness_test.py +94 -0
- braintrace-0.3.1/braintrace/_algorithm/tests/input_check_test.py +597 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/output_bypass_test.py +58 -10
- braintrace-0.3.1/braintrace/_algorithm/tests/scan_descent_hint_test.py +205 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/scan_descent_support_test.py +20 -13
- braintrace-0.3.1/braintrace/_algorithm/tests/trace_broadcast_test.py +674 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/three_factor.py +3 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/three_factor_test.py +13 -1
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/vjp_base.py +318 -30
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/vjp_graph_executor.py +17 -3
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compile_test.py +46 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/base.py +28 -8
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/base_test.py +37 -3
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/canonicalize.py +227 -14
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/canonicalize_test.py +658 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/diagnostics.py +66 -3
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/graph.py +240 -21
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/hid_param_op.py +257 -89
- braintrace-0.3.1/braintrace/_compiler/hid_param_op_test.py +415 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/hidden_group.py +798 -169
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/hidden_group_test.py +1330 -4
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/hidden_pertubation.py +609 -10
- braintrace-0.3.1/braintrace/_compiler/hidden_pertubation_fuzz_test.py +428 -0
- braintrace-0.3.1/braintrace/_compiler/hidden_pertubation_test.py +1990 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/jaxpr_graph.py +144 -17
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/jaxpr_graph_test.py +139 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/module_info.py +30 -0
- braintrace-0.3.1/braintrace/_compiler/online_credit.py +570 -0
- braintrace-0.3.1/braintrace/_compiler/online_credit_test.py +800 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/output_bypass.py +2 -5
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/output_bypass_test.py +73 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/position_graph.py +389 -47
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/position_graph_test.py +301 -1
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/scan_descent.py +461 -44
- braintrace-0.3.1/braintrace/_compiler/scan_descent_leaves_test.py +315 -0
- braintrace-0.3.1/braintrace/_compiler/scan_descent_test.py +1550 -0
- braintrace-0.3.1/braintrace/_input_data.py +536 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/conv_test.py +11 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/dense_test.py +9 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/__init__.py +10 -0
- braintrace-0.3.1/braintrace/_testing/census.py +550 -0
- braintrace-0.3.1/braintrace/_testing/census_test.py +397 -0
- braintrace-0.3.1/braintrace/_testing/graph_hash.py +608 -0
- braintrace-0.3.1/braintrace/_testing/graph_hash_test.py +398 -0
- braintrace-0.3.1/braintrace/_testing/precondition_models.py +560 -0
- braintrace-0.3.1/braintrace/_testing/precondition_models_test.py +154 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/scenario_catalog_test.py +6 -3
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_version.py +1 -1
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_linear_test.py +36 -34
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace.egg-info/PKG-INFO +2 -2
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace.egg-info/SOURCES.txt +13 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/pyproject.toml +6 -1
- braintrace-0.2.8/braintrace/_compiler/hid_param_op_test.py +0 -226
- braintrace-0.2.8/braintrace/_compiler/hidden_pertubation_test.py +0 -551
- braintrace-0.2.8/braintrace/_compiler/scan_descent_test.py +0 -495
- braintrace-0.2.8/braintrace/_input_data.py +0 -259
- {braintrace-0.2.8 → braintrace-0.3.1}/LICENSE +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/README.md +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/__init__.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/__init___test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/__init__.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/_common.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/_common_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/axes.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/axes_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/base_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/d_rtrl.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/d_rtrl_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/dni.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/dni_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/e_prop.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/graph_executor.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/graph_executor_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/io_dim_vjp.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/io_dim_vjp_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/ostl.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/ostl_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/param_dim_vjp_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/pp_prop.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/pp_prop_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/random_projection_vjp_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/approx_correctness_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/axis_acceptance_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/axis_discrimination_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/axis_golden_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/cross_check_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/diagnostic_exploration_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/public_api_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/scan_fusion_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/snn_model_correctness_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/transform_correctness_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/tests/while_support_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/uoro.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/uoro_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/vjp_base_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_algorithm/vjp_graph_executor_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compatible_imports.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compatible_imports_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compile.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/__init__.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/diagnostics_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/graph_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/module_info_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/report.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/report_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/tests/cell_relation_guardrail_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/tests/compiler_oracle_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_compiler/tests/compiler_property_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_grad_exponential.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_grad_exponential_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_input_data_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_legacy/__init__.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_legacy/_ops.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_legacy/_ops_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_legacy/_params.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_legacy/_params_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_misc.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_misc_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/__init__.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/_primitive.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/_primitive_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/_registries.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/_registries_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/conv.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/dense.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/einsum.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/einsum_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/elemwise.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/elemwise_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/embedding.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/embedding_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/grouped.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/grouped_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/lora.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/lora_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/op_rule_oracle.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/op_rule_oracle_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/sparse.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_op/sparse_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_state_management.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_state_management_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/compiler_models.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/models.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/oracle.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/oracle_models.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/oracle_models_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/oracle_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_testing/scenario_catalog.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_typing.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_typing_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/_version_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/__init__.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/__init___test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_conv.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_conv_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_embedding.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_embedding_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_linear.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_readout.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_readout_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_rnn.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/nn/_rnn_test.py +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace/py.typed +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace.egg-info/dependency_links.txt +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace.egg-info/requires.txt +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/braintrace.egg-info/top_level.txt +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/setup.cfg +0 -0
- {braintrace-0.2.8 → braintrace-0.3.1}/setup.py +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: braintrace
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Enabling Scalable Online Learning for Brain Dynamics.
|
|
5
|
-
Author-email: BrainTrace Developers <
|
|
5
|
+
Author-email: BrainTrace Developers <chaobrain@outlook.com>
|
|
6
6
|
License: Apache-2.0 license
|
|
7
7
|
Project-URL: Homepage, https://github.com/chaobrain/braintrace
|
|
8
8
|
Project-URL: Bug Tracker, https://github.com/chaobrain/braintrace/issues
|
|
@@ -66,6 +66,26 @@ class EligibilityTrace(brainstate.ShortTermState):
|
|
|
66
66
|
__module__ = 'braintrace'
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
def descended_scan_length(graph: ETraceGraph) -> Optional[int]:
|
|
70
|
+
"""The length of the longest scan that *graph* descends, or ``None``.
|
|
71
|
+
|
|
72
|
+
A descended scan holds an ETP relation or a hidden group in its body; a
|
|
73
|
+
group can be descended without any relation (all body weights used
|
|
74
|
+
through plain ops), so both are read. Unrolling the scan, with
|
|
75
|
+
``ControlFlowPolicy(scan_unroll_limit=length)``, removes the descent.
|
|
76
|
+
"""
|
|
77
|
+
lengths = [r.control_flow_context.scan.length
|
|
78
|
+
for r in graph.hidden_param_op_relations
|
|
79
|
+
if r.control_flow_context is not None]
|
|
80
|
+
lengths += [g.descent.scan.length for g in graph.hidden_groups if g.descent is not None]
|
|
81
|
+
return max(lengths) if lengths else None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
#: The algorithms the scan-descent refusal names: they run a descended scan
|
|
85
|
+
#: with their default configuration (``scan_descent_hint_test`` checks it).
|
|
86
|
+
DESCENT_ALGORITHMS: Tuple[str, ...] = ('D_RTRL', 'EProp')
|
|
87
|
+
|
|
88
|
+
|
|
69
89
|
class ETraceAlgorithm(SequenceDriverMixin, brainstate.nn.Module):
|
|
70
90
|
r"""Provide the base interface for eligibility-trace algorithms.
|
|
71
91
|
|
|
@@ -314,19 +334,8 @@ class ETraceAlgorithm(SequenceDriverMixin, brainstate.nn.Module):
|
|
|
314
334
|
# (all body weights routed through plain ops), so check both.
|
|
315
335
|
# Gate here — after the graph is built, before any trace state
|
|
316
336
|
# is initialized against it.
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
any(
|
|
320
|
-
r.control_flow_context is not None
|
|
321
|
-
for r in self.graph.hidden_param_op_relations
|
|
322
|
-
)
|
|
323
|
-
or any(
|
|
324
|
-
g.descent is not None
|
|
325
|
-
for g in self.graph.hidden_groups
|
|
326
|
-
)
|
|
327
|
-
)
|
|
328
|
-
and not getattr(self, '_supports_scan_descent', False)
|
|
329
|
-
):
|
|
337
|
+
length = descended_scan_length(self.graph)
|
|
338
|
+
if length is not None and not getattr(self, '_supports_scan_descent', False):
|
|
330
339
|
# Engines whose refusal has an axis-level reason (rather than
|
|
331
340
|
# merely missing support) state it via this hook, so the message
|
|
332
341
|
# names the coordinate instead of only the mechanism.
|
|
@@ -334,9 +343,11 @@ class ETraceAlgorithm(SequenceDriverMixin, brainstate.nn.Module):
|
|
|
334
343
|
raise NotImplementedError(
|
|
335
344
|
f'{type(self).__name__} does not support structured scan '
|
|
336
345
|
f'descent yet (an ETP relation or hidden group was '
|
|
337
|
-
f'discovered inside a scan
|
|
338
|
-
f'supports scan descent
|
|
339
|
-
f"
|
|
346
|
+
f'discovered inside the body of a length-{length} scan). Use '
|
|
347
|
+
f'an algorithm that supports scan descent '
|
|
348
|
+
f'({" or ".join(DESCENT_ALGORITHMS)}), or unroll the '
|
|
349
|
+
f'scan with ControlFlowPolicy(scan_unroll_limit={length}) '
|
|
350
|
+
f'(compile time grows with the length).'
|
|
340
351
|
+ (f' {reason}' if reason else '')
|
|
341
352
|
)
|
|
342
353
|
|
|
@@ -15,9 +15,13 @@
|
|
|
15
15
|
|
|
16
16
|
import unittest
|
|
17
17
|
|
|
18
|
+
import brainevent
|
|
18
19
|
import brainstate
|
|
19
20
|
import jax
|
|
20
21
|
import jax.numpy as jnp
|
|
22
|
+
import numpy as np
|
|
23
|
+
import numpy.testing as npt
|
|
24
|
+
import pytest
|
|
21
25
|
|
|
22
26
|
import braintrace
|
|
23
27
|
from braintrace._algorithm.e_prop import EProp
|
|
@@ -443,3 +447,89 @@ def test_docstring_compile_example_runs():
|
|
|
443
447
|
assert y.shape == (1,)
|
|
444
448
|
assert bool(jnp.all(jnp.isfinite(y)))
|
|
445
449
|
assert len(learner.graph.hidden_param_op_relations) >= 1
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
# ----------------------------------------------------------------------------
|
|
453
|
+
# #257: the kappa trace filter under compile(..., vmap=True)
|
|
454
|
+
# ----------------------------------------------------------------------------
|
|
455
|
+
|
|
456
|
+
_B, _N, _T = 3, 5, 6
|
|
457
|
+
_rng = np.random.RandomState(0)
|
|
458
|
+
_DENSE = ((_rng.rand(_N, _N) < 0.5) * _rng.randn(_N, _N)).astype('float32')
|
|
459
|
+
_XS = jnp.asarray(_rng.randn(_T, _B, _N).astype('float32'))
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
class _FilterNet(brainstate.nn.Module):
|
|
463
|
+
"""The issue's model: a leaky recurrent layer through one ETP weight."""
|
|
464
|
+
|
|
465
|
+
def __init__(self, kind):
|
|
466
|
+
super().__init__()
|
|
467
|
+
if kind == 'sparse':
|
|
468
|
+
self.w = braintrace.nn.SparseLinear(
|
|
469
|
+
brainevent.CSR.fromdense(jnp.asarray(_DENSE)))
|
|
470
|
+
else:
|
|
471
|
+
self.w = braintrace.nn.Linear(_N, _N, w_init=jnp.asarray(_DENSE))
|
|
472
|
+
|
|
473
|
+
def init_state(self, *args, **kwargs):
|
|
474
|
+
self.h = brainstate.HiddenState(jnp.zeros(_N))
|
|
475
|
+
|
|
476
|
+
def update(self, x):
|
|
477
|
+
self.h.value = 0.9 * self.h.value + self.w(jnp.tanh(self.h.value) + x)
|
|
478
|
+
return self.h.value
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def _filtered_grad(kind, kappa, xs, **kw):
|
|
482
|
+
learner = braintrace.compile(_FilterNet(kind), 'eprop', xs[0],
|
|
483
|
+
kappa_filter_decay=kappa, **kw)
|
|
484
|
+
g = learner.etrace_grad(xs, step_fn=lambda x: jnp.sum(learner(x) ** 2))
|
|
485
|
+
return learner, g
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
@pytest.mark.parametrize('vjp_method', ['single-step', 'multi-step'])
|
|
489
|
+
@pytest.mark.parametrize('kind', ['dense', 'sparse'])
|
|
490
|
+
def test_kappa_filter_under_vmap_is_the_sum_of_unbatched_lanes(kind, vjp_method):
|
|
491
|
+
# The batched objective sums the lanes' losses, so its gradient of the
|
|
492
|
+
# shared weights is the sum of the B unbatched gradients (f32 tolerance:
|
|
493
|
+
# the batched path reassociates the lane sum).
|
|
494
|
+
batched, g_b = _filtered_grad(kind, 0.5, _XS, batch_size=_B, vmap=True,
|
|
495
|
+
vjp_method=vjp_method)
|
|
496
|
+
lanes = [_filtered_grad(kind, 0.5, _XS[:, b], vjp_method=vjp_method)
|
|
497
|
+
for b in range(_B)]
|
|
498
|
+
g_ref = jax.tree.map(lambda *g: sum(g), *[g for _, g in lanes])
|
|
499
|
+
jax.tree.map(lambda a, r: npt.assert_allclose(a, r, rtol=1e-5, atol=1e-5),
|
|
500
|
+
g_b, g_ref)
|
|
501
|
+
# the filter contributes: kappa=0 gives a different gradient
|
|
502
|
+
_, g_0 = _filtered_grad(kind, 0.0, _XS, batch_size=_B, vmap=True,
|
|
503
|
+
vjp_method=vjp_method)
|
|
504
|
+
diff = max(float(jnp.max(jnp.abs(a - b)))
|
|
505
|
+
for a, b in zip(jax.tree.leaves(g_b), jax.tree.leaves(g_0)))
|
|
506
|
+
assert diff > 1e-2
|
|
507
|
+
# each lane's filter state is that lane's unbatched filter
|
|
508
|
+
flt_b = [f.value for f in batched.module._trace_filters.values()]
|
|
509
|
+
for b, (lane, _) in enumerate(lanes):
|
|
510
|
+
flt_l = [f.value for f in lane._trace_filters.values()]
|
|
511
|
+
jax.tree.map(lambda a, r: npt.assert_allclose(a[b], r, rtol=1e-5, atol=1e-6),
|
|
512
|
+
flt_b, flt_l)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
@pytest.mark.parametrize('vjp_method', ['single-step', 'multi-step'])
|
|
516
|
+
def test_kappa_filter_advances_in_the_forward_pass(vjp_method):
|
|
517
|
+
# A step that is not differentiated advances the filter exactly as a
|
|
518
|
+
# differentiated one does: the filter is a function of the trace sequence.
|
|
519
|
+
learners = []
|
|
520
|
+
for differentiate in (False, True):
|
|
521
|
+
learner = braintrace.compile(_FilterNet('dense'), 'eprop', _XS[0, 0],
|
|
522
|
+
kappa_filter_decay=0.5, vjp_method=vjp_method)
|
|
523
|
+
|
|
524
|
+
def step(x, learner=learner):
|
|
525
|
+
return jnp.sum(learner(x) ** 2)
|
|
526
|
+
|
|
527
|
+
for t in range(3):
|
|
528
|
+
if differentiate:
|
|
529
|
+
brainstate.transform.grad(step, learner.param_states)(_XS[t, 0])
|
|
530
|
+
else:
|
|
531
|
+
step(_XS[t, 0])
|
|
532
|
+
learners.append(learner)
|
|
533
|
+
plain, diffed = ([f.value for f in l._trace_filters.values()] for l in learners)
|
|
534
|
+
jax.tree.map(npt.assert_array_equal, plain, diffed)
|
|
535
|
+
assert max(float(jnp.max(jnp.abs(v))) for v in jax.tree.leaves(plain)) > 1e-2
|