rnba 0.2.2__tar.gz → 0.2.3__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.
Files changed (133) hide show
  1. {rnba-0.2.2 → rnba-0.2.3}/.gitignore +7 -0
  2. {rnba-0.2.2 → rnba-0.2.3}/PKG-INFO +2 -1
  3. {rnba-0.2.2 → rnba-0.2.3}/README.md +1 -0
  4. rnba-0.2.3/docs/activity-flow-colour-coding.md +344 -0
  5. rnba-0.2.3/docs/simulation-and-visualization-workflow.tex +208 -0
  6. rnba-0.2.3/docs/snb15-circuits-and-links.md +240 -0
  7. {rnba-0.2.2 → rnba-0.2.3}/docs/snb15_population_categories.md +1 -1
  8. {rnba-0.2.2 → rnba-0.2.3}/pyproject.toml +1 -1
  9. {rnba-0.2.2 → rnba-0.2.3}/rnba/__init__.py +1 -1
  10. {rnba-0.2.2 → rnba-0.2.3}/rnba/visualize_activity_flow.py +9 -6
  11. {rnba-0.2.2 → rnba-0.2.3}/LICENSE +0 -0
  12. {rnba-0.2.2 → rnba-0.2.3}/NBA24_QA_sentence_input.py +0 -0
  13. {rnba-0.2.2 → rnba-0.2.3}/NBA_SGall_sentence_input.py +0 -0
  14. {rnba-0.2.2 → rnba-0.2.3}/NBA_read_architecture.py +0 -0
  15. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/.gitignore +0 -0
  16. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/CMakeLists.txt +0 -0
  17. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/CMakePresets.json +0 -0
  18. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/README.md +0 -0
  19. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/data/README.md +0 -0
  20. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/data/example_snb1_contract.json +0 -0
  21. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/data/model_smoke_contract.json +0 -0
  22. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/data/network.schema.json +0 -0
  23. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/data/snb15_contract.json +0 -0
  24. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/data/snb20_contract.json +0 -0
  25. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/include/model.hpp +0 -0
  26. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/include/output.hpp +0 -0
  27. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/include/parser.hpp +0 -0
  28. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/include/simulator.hpp +0 -0
  29. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/include/sparse_matrix.hpp +0 -0
  30. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/src/main.cpp +0 -0
  31. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/src/model.cpp +0 -0
  32. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/src/output.cpp +0 -0
  33. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/src/parser.cpp +0 -0
  34. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/src/simulator.cpp +0 -0
  35. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/src/sparse_matrix.cpp +0 -0
  36. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tests/CMakeLists.txt +0 -0
  37. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tests/data/model_smoke_contract.json +0 -0
  38. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tests/data/snb20_contract.json +0 -0
  39. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tests/test_golden.cpp +0 -0
  40. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tests/test_parser.cpp +0 -0
  41. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tests/test_simulator.cpp +0 -0
  42. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tools/export_network.py +0 -0
  43. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tools/plot_cpp_qa.py +0 -0
  44. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tools/plot_ncv_output.py +0 -0
  45. {rnba-0.2.2 → rnba-0.2.3}/cpp-sim/tools/plot_trace_comparison.py +0 -0
  46. {rnba-0.2.2 → rnba-0.2.3}/docs/analysis/QA_GENERATED_SIMULATION_WALKTHROUGH.md +0 -0
  47. {rnba-0.2.2 → rnba-0.2.3}/docs/analysis/README.md +0 -0
  48. {rnba-0.2.2 → rnba-0.2.3}/docs/analysis/collapse_verification.md +0 -0
  49. {rnba-0.2.2 → rnba-0.2.3}/docs/analysis/rowviz.py +0 -0
  50. {rnba-0.2.2 → rnba-0.2.3}/docs/analysis/schematic_inclusion_matrix.md +0 -0
  51. {rnba-0.2.2 → rnba-0.2.3}/docs/analysis/visualize_core_circuits.py +0 -0
  52. {rnba-0.2.2 → rnba-0.2.3}/docs/bindings-file-format.md +0 -0
  53. {rnba-0.2.2 → rnba-0.2.3}/docs/instruction-file-formats.md +0 -0
  54. {rnba-0.2.2 → rnba-0.2.3}/docs/templates/schematic_template_manifest.md +0 -0
  55. {rnba-0.2.2 → rnba-0.2.3}/examples/Collapsed_network_force.gexf +0 -0
  56. {rnba-0.2.2 → rnba-0.2.3}/examples/Collapsed_network_force_2026-05-22_noring_v1.gexf +0 -0
  57. {rnba-0.2.2 → rnba-0.2.3}/examples/Example_01_WIlsonCowan.ipynb +0 -0
  58. {rnba-0.2.2 → rnba-0.2.3}/examples/Example_02_BasicCircuits.ipynb +0 -0
  59. {rnba-0.2.2 → rnba-0.2.3}/examples/collapse_bgates.py +0 -0
  60. {rnba-0.2.2 → rnba-0.2.3}/examples/colour_gexf.py +0 -0
  61. {rnba-0.2.2 → rnba-0.2.3}/examples/demo_network_visualization.py +0 -0
  62. {rnba-0.2.2 → rnba-0.2.3}/examples/example_2.py +0 -0
  63. {rnba-0.2.2 → rnba-0.2.3}/examples/example_3.py +0 -0
  64. {rnba-0.2.2 → rnba-0.2.3}/examples/example_clamp.py +0 -0
  65. {rnba-0.2.2 → rnba-0.2.3}/examples/example_nCV_generated.py +0 -0
  66. {rnba-0.2.2 → rnba-0.2.3}/examples/example_network_creation.py +0 -0
  67. {rnba-0.2.2 → rnba-0.2.3}/examples/example_qa_create_network.py +0 -0
  68. {rnba-0.2.2 → rnba-0.2.3}/examples/example_qa_generated.py +0 -0
  69. {rnba-0.2.2 → rnba-0.2.3}/examples/example_qa_simulation.py +0 -0
  70. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_circle_16k.gexf +0 -0
  71. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_circle_ei_32k.gexf +0 -0
  72. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_ei_grey_32k.gexf +0 -0
  73. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_ei_grey_seeded_32k.gexf +0 -0
  74. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_force_16k.gexf +0 -0
  75. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_force_ei_32k.gexf +0 -0
  76. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_grey_16k.gexf +0 -0
  77. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_sfdp.gexf +0 -0
  78. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_sfdp_ei.gexf +0 -0
  79. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_sfdp_grey.gephi +0 -0
  80. {rnba-0.2.2 → rnba-0.2.3}/examples/full_network_sfdp_rows12.gexf +0 -0
  81. {rnba-0.2.2 → rnba-0.2.3}/examples/full_scale_sfdp_visualization.py +0 -0
  82. {rnba-0.2.2 → rnba-0.2.3}/examples/gexf_roles.py +0 -0
  83. {rnba-0.2.2 → rnba-0.2.3}/examples/layout_rows.py +0 -0
  84. {rnba-0.2.2 → rnba-0.2.3}/examples/render_collapsed.py +0 -0
  85. {rnba-0.2.2 → rnba-0.2.3}/examples/render_gexf.py +0 -0
  86. {rnba-0.2.2 → rnba-0.2.3}/examples/rnba_roles_row1.gexf +0 -0
  87. {rnba-0.2.2 → rnba-0.2.3}/examples/rnba_roles_snb15.gexf +0 -0
  88. {rnba-0.2.2 → rnba-0.2.3}/examples/row_visualization.py +0 -0
  89. {rnba-0.2.2 → rnba-0.2.3}/examples/seed_circle_ext.py +0 -0
  90. {rnba-0.2.2 → rnba-0.2.3}/examples/seed_ei_gexf.py +0 -0
  91. {rnba-0.2.2 → rnba-0.2.3}/examples/visualize_architecture.sh +0 -0
  92. {rnba-0.2.2 → rnba-0.2.3}/rnba/_sim.py +0 -0
  93. {rnba-0.2.2 → rnba-0.2.3}/rnba/architecture.py +0 -0
  94. {rnba-0.2.2 → rnba-0.2.3}/rnba/blackboard.py +0 -0
  95. {rnba-0.2.2 → rnba-0.2.3}/rnba/circuit.py +0 -0
  96. {rnba-0.2.2 → rnba-0.2.3}/rnba/circuits/__init__.py +0 -0
  97. {rnba-0.2.2 → rnba-0.2.3}/rnba/circuits/core.py +0 -0
  98. {rnba-0.2.2 → rnba-0.2.3}/rnba/cli.py +0 -0
  99. {rnba-0.2.2 → rnba-0.2.3}/rnba/constants.py +0 -0
  100. {rnba-0.2.2 → rnba-0.2.3}/rnba/converters/__init__.py +0 -0
  101. {rnba-0.2.2 → rnba-0.2.3}/rnba/converters/architecture.py +0 -0
  102. {rnba-0.2.2 → rnba-0.2.3}/rnba/inputs.py +0 -0
  103. {rnba-0.2.2 → rnba-0.2.3}/rnba/network.py +0 -0
  104. {rnba-0.2.2 → rnba-0.2.3}/rnba/nodes.py +0 -0
  105. {rnba-0.2.2 → rnba-0.2.3}/rnba/plotting.py +0 -0
  106. {rnba-0.2.2 → rnba-0.2.3}/rnba/schematic_visualization.py +0 -0
  107. {rnba-0.2.2 → rnba-0.2.3}/rnba/simulation.py +0 -0
  108. {rnba-0.2.2 → rnba-0.2.3}/rnba/visualize.py +0 -0
  109. {rnba-0.2.2 → rnba-0.2.3}/tests/QA_3NVN_O_fail.txt +0 -0
  110. {rnba-0.2.2 → rnba-0.2.3}/tests/QA_3NVN_O_success.txt +0 -0
  111. {rnba-0.2.2 → rnba-0.2.3}/tests/RNBA_SGall_SNB15_Bindings_Sentence_3NVN.txt +0 -0
  112. {rnba-0.2.2 → rnba-0.2.3}/tests/RNBA_arch_SGall_SNB15.txt +0 -0
  113. {rnba-0.2.2 → rnba-0.2.3}/tests/RNBA_arch_SGall_SNB20.txt +0 -0
  114. {rnba-0.2.2 → rnba-0.2.3}/tests/RNBA_arch_SGall_SNB30.txt +0 -0
  115. {rnba-0.2.2 → rnba-0.2.3}/tests/Sentence_2CV.txt +0 -0
  116. {rnba-0.2.2 → rnba-0.2.3}/tests/conftest.py +0 -0
  117. {rnba-0.2.2 → rnba-0.2.3}/tests/integration/__init__.py +0 -0
  118. {rnba-0.2.2 → rnba-0.2.3}/tests/integration/test_architecture_vs_gexf.py +0 -0
  119. {rnba-0.2.2 → rnba-0.2.3}/tests/test_architecture.py +0 -0
  120. {rnba-0.2.2 → rnba-0.2.3}/tests/test_basic.py +0 -0
  121. {rnba-0.2.2 → rnba-0.2.3}/tests/test_blackboard.py +0 -0
  122. {rnba-0.2.2 → rnba-0.2.3}/tests/test_circuit.py +0 -0
  123. {rnba-0.2.2 → rnba-0.2.3}/tests/test_circuits.py +0 -0
  124. {rnba-0.2.2 → rnba-0.2.3}/tests/test_clamp_integration.py +0 -0
  125. {rnba-0.2.2 → rnba-0.2.3}/tests/test_cpp_ncv_golden.py +0 -0
  126. {rnba-0.2.2 → rnba-0.2.3}/tests/test_instruction_unified.py +0 -0
  127. {rnba-0.2.2 → rnba-0.2.3}/tests/test_instruction_window_semantics.py +0 -0
  128. {rnba-0.2.2 → rnba-0.2.3}/tests/test_network.py +0 -0
  129. {rnba-0.2.2 → rnba-0.2.3}/tests/test_nodes.py +0 -0
  130. {rnba-0.2.2 → rnba-0.2.3}/tests/test_rnba_arch_converter.py +0 -0
  131. {rnba-0.2.2 → rnba-0.2.3}/tests/test_rnba_simulation_launcher.py +0 -0
  132. {rnba-0.2.2 → rnba-0.2.3}/tests/test_simulation_engines.py +0 -0
  133. {rnba-0.2.2 → rnba-0.2.3}/tests/test_visualize.py +0 -0
@@ -62,6 +62,13 @@ bla.py
62
62
  /*.pdf
63
63
  examples/*.pdf
64
64
 
65
+ # LaTeX build artifacts from docs/ sources (docs/templates PDFs stay tracked;
66
+ # these globs match the docs/ root only, not subdirectories)
67
+ docs/*.aux
68
+ docs/*.log
69
+ docs/*.out
70
+ docs/simulation-and-visualization-workflow.pdf
71
+
65
72
  # Local experiment outputs in tracked_experiments (only the scripts and
66
73
  # sentence inputs there are tracked)
67
74
  tracked_experiments/activity_flow_*/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: rnba
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Recurrent Neural Blackboard Architecture - a simulation framework for neuro-cognitive modelling.
5
5
  Author-Email: RNBA Contributors <m.dekamps@leeds.ac.uk>
6
6
  License: MIT
@@ -226,6 +226,7 @@ RNBA uses **two complementary naming schemes**:
226
226
  ## Documentation
227
227
 
228
228
  Project documentation lives in the docs/ folder:
229
+ - Simulation-to-video workflow (LaTeX): [docs/simulation-and-visualization-workflow.tex](docs/simulation-and-visualization-workflow.tex)
229
230
  - Instruction file format (unified; legacy QA and SGall dialects): [docs/instruction-file-formats.md](docs/instruction-file-formats.md)
230
231
  - Bindings file format: [docs/bindings-file-format.md](docs/bindings-file-format.md)
231
232
  - Analysis: [docs/analysis/](docs/analysis/)
@@ -189,6 +189,7 @@ RNBA uses **two complementary naming schemes**:
189
189
  ## Documentation
190
190
 
191
191
  Project documentation lives in the docs/ folder:
192
+ - Simulation-to-video workflow (LaTeX): [docs/simulation-and-visualization-workflow.tex](docs/simulation-and-visualization-workflow.tex)
192
193
  - Instruction file format (unified; legacy QA and SGall dialects): [docs/instruction-file-formats.md](docs/instruction-file-formats.md)
193
194
  - Bindings file format: [docs/bindings-file-format.md](docs/bindings-file-format.md)
194
195
  - Analysis: [docs/analysis/](docs/analysis/)
@@ -0,0 +1,344 @@
1
+ # Activity-flow video: colour coding
2
+
3
+ How `rnba.visualize_activity_flow` colours the compressed schematic, what
4
+ that colouring expresses in terms of the published RNBA model, and where
5
+ the implementation currently diverges.
6
+
7
+ The reference is van der Velde, *The recursive neural blackboard
8
+ architecture for combinatorial sentence structures in the brain*
9
+ (`FvdV-RNBA26-article.pdf`) and its Supplementary Material
10
+ (`FvdV-RNBA26-SM.pdf`, cited below as **SM**).
11
+
12
+ ## 1. The compressed representation
13
+
14
+ The video does not draw one marker per population. It draws a *template*
15
+ schematic in which each marker stands for a circuit — several populations
16
+ collapsed into one symbol. Each symbol therefore needs a population
17
+ chosen to speak for it:
18
+
19
+ - the **activity representative** — the population whose trace stands for
20
+ the circuit's activity. For gating circuits this is the gate output,
21
+ `GC_gate_G4`.
22
+ - the **WM representative** — the circuit's working-memory pair,
23
+ `WM1_Apop` / `WM2_Bpop`. Some circuits have one; most do not.
24
+
25
+ SM A.2: a WM population is *simulated* as two interacting populations A
26
+ and B, of which "population A is seen and referred to as the WM
27
+ population, because it alone receives input activity from the rest of the
28
+ architecture and generates excitatory output"; the activities of A and B
29
+ "are summarized as WM activity in the simulations."
30
+
31
+ ## 2. Semantics, derived from SM I.1
32
+
33
+ SM I.1 partitions every SNB population into three categories:
34
+
35
+ - **Gating** — all populations in gating and linked-binding circuits,
36
+ *except* the WM populations in the latter.
37
+ - **WM** — all A and B populations forming WM populations.
38
+ - **Other** — all remaining SNB populations, "such as the word
39
+ populations".
40
+
41
+ This is the model's own distinction between **control** and **stored
42
+ structure**, and it is load-bearing in the paper: Figures 4B/4C plot
43
+ Gating, WM and Other as separate curves, and the central result is that
44
+ Total activity "is dominated by Gating, related to the control of
45
+ sentence processing," while the contribution of WM — "related to storing
46
+ a sentence/constituent structure" — is "small and 'flat'."
47
+
48
+ **The colour scheme is the spatial analogue of that plot:**
49
+
50
+ | Colour | Meaning | SM I.1 category |
51
+ | --- | --- | --- |
52
+ | **Red** | control activity — a gate is passing signal | Gating |
53
+ | **Yellow** | stored structure — a WM is latched | WM |
54
+ | **Orange** | both at once | Gating + WM |
55
+ | Grey | below threshold; inactive baseline | — |
56
+
57
+ Yellow is the signature of persistence. Per SM D.1.1, an LMA *is* a WM
58
+ population and "remains active until a new LMA of the same type is
59
+ activated" — so a latched LMA should read yellow for as long as it holds
60
+ its content.
61
+
62
+ ### Category assignment is mechanical
63
+
64
+ A population's category follows from its `pop_id` alone:
65
+
66
+ | `pop_id` | Category |
67
+ | --- | --- |
68
+ | `WM1_Apop`, `WM2_Bpop` | WM |
69
+ | `GC_start_G1`, `GC_disinhibition_G2`, `GC_inhibition_G3`, `GC_gate_G4` | Gating |
70
+ | anything else | Other |
71
+
72
+ Applying that rule to `RNBA_arch_SGall_SNB15.txt` reproduces SM table S15
73
+ exactly:
74
+
75
+ | Category | This repo | SM table S15 (SNB15) |
76
+ | --- | ---: | ---: |
77
+ | Gating | 10980 | 10980 ✓ |
78
+ | WM | 2970 | 2970 ✓ |
79
+ | Other | 2190 | 2190 ✓ |
80
+ | Total | 16140 | 16140 ✓ |
81
+ | External | 158 | 158 ✓ |
82
+ | Total in simulation | 16298 | 16298 ✓ |
83
+
84
+ (16298 is also the `lenlist` in the architecture file header.) The 2970 WM
85
+ populations are **1485 WM pairs**.
86
+
87
+ ### Blue
88
+
89
+ `WORD_INHIB` symbols and the single global competition node take a
90
+ separate branch and render blue. This has no counterpart in SM I.1 —
91
+ their representatives (`MA_Word_inhib_A1a`) are **Other** populations. See
92
+ §6, finding D.
93
+
94
+ ## 3. As implemented
95
+
96
+ Each symbol gets two independent intensities per frame.
97
+
98
+ **Activity representative** — `_value_for_mapping`
99
+ (`rnba/visualize_activity_flow.py:1152`): the `primary_index` row, or
100
+ `max(primary, secondary)` when the mapping mode is not `direct`.
101
+
102
+ **WM representative** — `_value_for_wm` (`:1160`): `max(wm1, wm2)`, or
103
+ `0.0` when either index is `-1`. (SM A.2 says the two are "summarized";
104
+ the code takes the max.)
105
+
106
+ Both pass through `_normalize_activity` (`:1166`): values below
107
+ `--activation-threshold` (default `1.0`) clamp to zero, the remainder
108
+ rescales against `--vmax` (default `100.0`) and is raised to
109
+ `--activation-gamma` (default `0.7`).
110
+
111
+ The two are decomposed (`:1418-1421`) and mixed (`:1432-1435`):
112
+
113
+ ```python
114
+ overlap = min(t_rep, t_wm)
115
+ rep_only = t_rep - overlap
116
+ wm_only = t_wm - overlap
117
+
118
+ r = 1.00 * rep_only + 1.00 * overlap + 0.95 * wm_only
119
+ g = 0.04 * rep_only + 0.58 * overlap + 0.95 * wm_only
120
+ b = 0.06 # constant
121
+ alpha = 0.40 + 0.60 * max(t_rep, t_wm)
122
+ ```
123
+
124
+ The explicit decomposition exists so overlap reads orange rather than
125
+ collapsing to yellow. Blue is a separate branch (`:1424-1429`) using the
126
+ activity representative only. Marker size scales with
127
+ `max(t_rep, t_wm)` (`:1440`); `WORD` symbols draw at double size.
128
+
129
+ **Readability.** `b` is pinned at `0.06`, so red, orange and yellow differ
130
+ *only in the green channel*. At low `--dpi` and small markers they are
131
+ easy to confuse, and a latched-but-dim symbol is both faint (alpha floor
132
+ `0.40`) and colour-ambiguous. `--first-rows N` helps: it scales overlay
133
+ markers 3x (`:1296`).
134
+
135
+ > The constants above were hand-tuned, not derived. The commit that
136
+ > established the current rendering path (`82691ab`) says "Colors and
137
+ > transparency still need further tuning." Sections 1–2 are the
138
+ > specification; §3 is what the code currently does.
139
+
140
+ ## 4. Conformance table
141
+
142
+ Audited against a full SNB15 run (2064 symbols, all mapped).
143
+
144
+ | Family | N | Representative | SM category | WM bound | Reachable colours |
145
+ | --- | ---: | --- | --- | :-: | --- |
146
+ | `CM_CELL` | 225 | `Bgate_HtoD_C1::GC_gate_G4` | Gating | yes | red, yellow, orange |
147
+ | `SG_H` | 165 | `SA_SG<t>_B4::WM1_Apop` | **WM** | yes | orange → yellow |
148
+ | `SG_D` | 165 | `SA_SG<t>_B4::WM1_Apop` | **WM** | yes | orange → yellow |
149
+ | `LMA_OUT_H` | 135 | `Gate_LMA_W_<wt>_B2a::GC_gate_G4` | Gating | yes | red, yellow, orange |
150
+ | `LMA_OUT_D` | 135 | `Gate_LMA_W_<wt>_B2a::GC_gate_G4` | Gating | yes | red, yellow, orange |
151
+ | `SGIN_H` | 165 | `Gate_SG_CM_SG<t>_B8::GC_gate_G4` | Gating | no | red only |
152
+ | `SGIN_D` | 165 | `Gate_SG_CM_SG<t>_B8::GC_gate_G4` | Gating | no | red only |
153
+ | `SG_OUT_H` | 165 | `Gate_SG_W_SG<t>_B12::GC_gate_G4` | Gating | no | red only |
154
+ | `SG_OUT_D` | 165 | `Gate_SG_W_SG<t>_B12::GC_gate_G4` | Gating | no | red only |
155
+ | `B2_LINK_H` | 165 | `L_Bgate_SG<t>_W_B2::GC_gate_G4` | Gating | yes | red, yellow, orange |
156
+ | `B2_LINK_D` | 165 | `L_Bgate_SG<t>_W_B2::GC_gate_G4` | Gating | yes | red, yellow, orange |
157
+ | `WORD` | 15 | `MA_Word_A1` | **Other** | no | red only |
158
+ | `H_RELAY` | 15 | `HD_Row_CM_in_B10` | **Other** | no | red only |
159
+ | `D_RELAY` | 15 | `HD_Row_CM_in_B10` | **Other** | no | red only |
160
+ | `CM_OUT_H` | 15 | `HD_Row_CM_out_B11` | **Other** | no | red only |
161
+ | `CM_OUT_D` | 15 | `HD_Row_CM_out_B11` | **Other** | no | red only |
162
+ | `WORD_INHIB` | 15 | `MA_Word_inhib_A1a` | **Other** | no | blue |
163
+ | `GLOBAL_INHIB` | 1 | none — synthesised `max` over `WORDINHIB_*` (`:1354-1361`) | — | no | blue |
164
+ | `INPUT` | 158 | external input rows | External | no | red only |
165
+
166
+ Symbols by SM category of their representative: **Gating 1485, WM 330,
167
+ Other 90, External 158, synthesised 1**.
168
+
169
+ ### WM coverage
170
+
171
+ A WM must be attributed to the circuit that *owns* it, not to the
172
+ `circ_id` it happens to be filed under. Per SM A.4 a linked-binding
173
+ circuit is **two gating circuits sharing one WM population**; the row map
174
+ files that shared WM under the forward gate, so reading ownership off
175
+ `circ_id` double-counts and mislabels.
176
+
177
+ Verified in the architecture — `Bgate_SG1Verb_B1::WM1_Apop` (row 349)
178
+ outputs to `Bgate_SG1Verb_B1::GC_disinhibition_G2` (352) **and**
179
+ `L_Bgate_SG1Verb_W_B2::GC_disinhibition_G2` (416). Same pattern for
180
+ `Bgate_W_C_A2::WM1_Apop` (172) → (175) and (223).
181
+
182
+ Of the architecture's **1485 WM pairs**, symbols are bound to
183
+ **1020 (69%)**:
184
+
185
+ | WM owner | Pairs | Gates sharing it | Symbol |
186
+ | --- | ---: | --- | --- |
187
+ | CM binding cell | 225 | `Bgate_HtoD_C1` + `L_Bgate_DtoH_C2` | `CM_CELL` ✓ |
188
+ | LMA→SG linked-binding | 330 | `Bgate_SG<t>_B1` + `L_Bgate_SG<t>_W_B2` | `B2_LINK_H/D` ✓ (§6 B) |
189
+ | SG sub-assembly (SA) | 330 | standalone WM in the SG circuit | `SG_H`/`SG_D` ✓ |
190
+ | LMA (`MA_<wt>_A3`) | 135 | standalone WM population | `LMA_OUT_H/D` ✓ (§6 A) |
191
+ | SA-WM (`SA_WM_SG<t>_B9`) | 330 | standalone WM in the SG circuit | **none** (§6 C) |
192
+ | LMA↔W linked-binding | 135 | `Bgate_W_<wt>_A2` + `L_Bgate_LMA_W_<wt>_A2a` | **none** (§6 C) |
193
+
194
+ The video therefore shows 69% of the model's stored sentence structure —
195
+ the quantity the paper isolates as the WM category.
196
+
197
+ ## 5. Design notes (not defects)
198
+
199
+ **SG symbols represent a WM population, by design.** SM D.1.5: "The core
200
+ population of an SG circuit is a WM population referred to as a
201
+ sub-assembly (SA)." So for all 330 `SG_H`/`SG_D` symbols
202
+ `primary_index == wm1_index` — the SA *is* both the representative and
203
+ the WM. Consequently `rep_only` is structurally zero and these symbols
204
+ never render pure red; they sit orange and drift yellow as `WM2_Bpop`
205
+ outruns `WM1_Apop`. That reflects the architecture rather than
206
+ contradicting it. It does mean the red/yellow contrast carries no
207
+ information on SG nodes: they are WM nodes throughout.
208
+
209
+ **`Gate_LMA_W_<wt>_B2a` is not the LMA.** Despite the `LMA_OUT_*` family
210
+ name it is the LMA-selective gating circuit on the CM→W *output* path
211
+ (`D-N-out` / `H-V-out` in SM figure S9), disinhibited by
212
+ `EX8H/D_LMA_out_<wt>_<arm>`. In a run with no retrieval phase it
213
+ correctly stays silent for the whole simulation.
214
+
215
+ ## 6. Defects
216
+
217
+ ### A — LMA symbols had no WM binding — **FIXED**
218
+
219
+ `_build_mappings` did attempt a WM pair for `LMA_OUT_H`/`LMA_OUT_D`, but
220
+ searched the wrong circuit:
221
+
222
+ ```python
223
+ circ = f"Gate_LMA_W_{s.sg_type}_B2a"
224
+ wm1, wm2 = _find_wm_pair(rows, circ_id=circ, ..., mat_id="S_G") # was
225
+ ```
226
+
227
+ `Gate_LMA_W_<wt>_B2a` contains only the four `GC_*` gate populations — no
228
+ WM at all. The LMA lives in a different circuit, `MA_<wt>_A3`, which holds
229
+ exactly `WM1_Apop`/`WM2_Bpop`. The lookup silently returned `(-1, -1)`,
230
+ so `t_wm = 0` and yellow was arithmetically unreachable for all 270 LMA
231
+ symbols — defeating the defining property in SM D.1.1.
232
+
233
+ The lookup is now keyed on the LMA circuit and the word group:
234
+
235
+ ```python
236
+ wm1, wm2 = _find_wm_pair(rows, circ_id=f"MA_{s.sg_type}_A3",
237
+ mat_id=f"W_G{s.row}") # now
238
+ ```
239
+
240
+ All 270 LMA symbols now resolve a WM pair, verified correct for every
241
+ word type and row. Because `Gate_LMA_W_<wt>_B2a` exists per arm (270)
242
+ while `MA_<wt>_A3` is per row (135), the H and D symbols of a row share
243
+ one latch and light together.
244
+
245
+ Worked example — "Sue eats pizza", the C LMA of row 1:
246
+
247
+ | Row | Circuit :: population | Peak | Before | After |
248
+ | --- | --- | ---: | --- | --- |
249
+ | 254 | `MA_C_A3::WM1_Apop` | 99.99 | unmapped | **bound as `wm1`** |
250
+ | 255 | `MA_C_A3::WM2_Bpop` | 100.00 | unmapped | **bound as `wm2`** |
251
+ | 714 | `Gate_LMA_W_C_B2a::GC_gate_G4` (H) | 0.005 | representative | representative |
252
+ | 1059 | `Gate_LMA_W_C_B2a::GC_gate_G4` (D) | 0.005 | representative | representative |
253
+ | 158 | `MA_Word_A1[W_G0]` | 98.17 @ 10 ms | red flash, gone by ~25 ms | unchanged |
254
+
255
+ The C LMA holds `WM2 = 100` from ~15 ms until inhibited at 140 ms by
256
+ `MA_C_inhib_A3a` (the `Clause_inhib` instruction). Colour of
257
+ `LMAOUT_H_r00_C` after the fix, computed with the renderer's own maths:
258
+
259
+ | t (ms) | rep | WM | `t_rep` | `t_wm` | Colour |
260
+ | ---: | ---: | ---: | ---: | ---: | --- |
261
+ | 0 | 0.00 | 0.00 | 0.000 | 0.000 | grey |
262
+ | 20 | 0.00 | 99.89 | 0.000 | 0.999 | **yellow** |
263
+ | 60 | 0.00 | 100.00 | 0.000 | 1.000 | **yellow** |
264
+ | 100 | 0.00 | 100.00 | 0.000 | 1.000 | **yellow** |
265
+ | 140 | 0.00 | 100.00 | 0.000 | 1.000 | **yellow** |
266
+ | 160 | 0.00 | 0.20 | 0.000 | 0.000 | grey |
267
+ | 200 | 0.00 | 0.00 | 0.000 | 0.000 | grey |
268
+
269
+ Yellow across exactly the latched stretch, extinguishing with the
270
+ commanded inhibition — the SM D.1.1 signature.
271
+
272
+ ### B — `B2_LINK` symbols had no WM binding — **FIXED**
273
+
274
+ The same class of error as A. `B2_LINK_H`/`B2_LINK_D` draw the reverse
275
+ gate of the LMA→SG linked-binding circuit, and the WM lookup searched
276
+ that gate's own `circ_id`:
277
+
278
+ ```python
279
+ circ = f"L_Bgate_{s.sg_type}_W_B2"
280
+ wm1, wm2 = _find_wm_pair(rows, circ_id=circ, ..., mat_id="S_G") # was
281
+ ```
282
+
283
+ The shared WM is filed under the *forward* gate. The row/`mat_id` keys
284
+ were already correct; only the circuit was wrong:
285
+
286
+ ```python
287
+ wm1, wm2 = _find_wm_pair(rows, circ_id=f"Bgate_{s.sg_type}_B1",
288
+ h_row=..., d_row=..., mat_id="S_G") # now
289
+ ```
290
+
291
+ All 330 `B2_LINK` symbols now resolve a WM pair, verified correct for
292
+ every SG type and row. Combined with A, WM coverage rises 555 → 1020 of
293
+ 1485 pairs (37% → 69%).
294
+
295
+ ### C — Two WM owners have no symbol at all
296
+
297
+ - **LMA↔W linked-binding** (135 pairs). *Neither* of its gates —
298
+ `Bgate_W_<wt>_A2` or `L_Bgate_LMA_W_<wt>_A2a` — is on the schematic,
299
+ even though Figure 1B draws this binding prominently as the black
300
+ diamond between the word population and the LMA. In the §6 A worked
301
+ example it latched at `WM1≈50`, `WM2≈100` and held past the end of the
302
+ run, entirely unseen.
303
+ - **SA-WM** (`SA_WM_SG<t>_B9`, 330 pairs). A standalone WM inside the SG
304
+ circuit (SM D.1.5, figure S8), with no symbol of its own.
305
+
306
+ Unlike A and B these cannot be fixed in the mapper: there is no symbol to
307
+ attach a WM to. Closing them requires **new symbols in the schematic
308
+ template** (`rnba/schematic_visualization.py`, and correspondingly the
309
+ reference PDFs under `docs/templates/`), which changes the canonical
310
+ static schematic as well as the video. That is a modelling/design
311
+ decision, not a bug fix.
312
+
313
+ ### D — The Other category is split arbitrarily between red and blue
314
+
315
+ SM I.1 makes Other a first-class category, plotted separately in figure
316
+ 4B. In the renderer its 90 symbols are split: 15 (`WORD_INHIB`) render
317
+ blue, the other 75 (`WORD`, `H_RELAY`, `D_RELAY`, `CM_OUT_H`, `CM_OUT_D`)
318
+ render red and are indistinguishable from Gating symbols. Neither the
319
+ blue branch nor the red default corresponds to a category boundary in the
320
+ model.
321
+
322
+ ### E — Cosmetic
323
+
324
+ `raw_rep` at `:1409` is computed and discarded; `t_rep` comes from the
325
+ precomputed `src_activity` map at `:1412`. Same value, redundant work.
326
+
327
+ ## 7. Reproducing the audit
328
+
329
+ Render any run with `--out-dir DIR`; the mapper writes
330
+ `DIR/representative_mapping.csv` (`symbol_id, family, mode,
331
+ primary_index, secondary_index, aux_index, wm1_index, wm2_index,
332
+ source`). Join its index columns against the simulator's
333
+ `--output-row-map` CSV to recover `circ_id`/`pop_id` per symbol, and
334
+ classify with the `pop_id` rule in §2.
335
+
336
+ - `wm1_index == -1` → the symbol can never render yellow.
337
+ - `primary_index == wm1_index` → the symbol can never render red.
338
+
339
+ ## See also
340
+
341
+ - `docs/simulation-and-visualization-workflow.tex` — the simulate-to-video workflow
342
+ - `docs/snb15_population_categories.md` — the 30-category population classification
343
+ - `docs/instruction-file-formats.md` — instruction times are timestep indices, `h = 0.1` ms
344
+ - `rnba/schematic_visualization.py` — the base schematic the overlay is drawn on
@@ -0,0 +1,208 @@
1
+ \documentclass[11pt]{article}
2
+
3
+ \usepackage[margin=1in]{geometry}
4
+ \usepackage{hyperref}
5
+ \usepackage{enumitem}
6
+ \usepackage{listings}
7
+ \usepackage{xcolor}
8
+
9
+ \lstset{
10
+ basicstyle=\ttfamily\small,
11
+ breaklines=true,
12
+ frame=single,
13
+ columns=fullflexible,
14
+ keepspaces=true,
15
+ showstringspaces=false,
16
+ }
17
+
18
+ \newcommand{\opt}[1]{\texttt{#1}}
19
+
20
+ \title{RNBA: From Architecture to Activity Video}
21
+ \author{RNBA documentation}
22
+ \date{}
23
+
24
+ \begin{document}
25
+ \maketitle
26
+
27
+ \noindent
28
+ This note describes the two-step workflow that turns an RNBA blackboard
29
+ architecture and an instruction file into a rendered activity-flow video.
30
+ It corresponds to the commands in \texttt{\textasciitilde/NBAExperiments/start}
31
+ and explains every option used there. The example presents the sentence
32
+ \emph{``Sue eats pizza''} to the SNB15 network and renders the resulting
33
+ dynamics.
34
+
35
+ \section*{Overview}
36
+
37
+ The workflow has two stages, each a separate command:
38
+
39
+ \begin{enumerate}[leftmargin=*]
40
+ \item \textbf{Simulate} with \texttt{rnba-run} (the Python simulator).
41
+ It reads the architecture and instruction files, integrates the
42
+ network dynamics, and writes the population traces to CSV.
43
+ \item \textbf{Visualise} with \texttt{python -m rnba.visualize\_activity\_flow}.
44
+ It overlays those traces on the canonical SNB15 schematic and
45
+ renders an MP4 video.
46
+ \end{enumerate}
47
+
48
+ The stages communicate through the CSV files written in step~1 and read
49
+ back in step~2. All times inside RNBA are integer timestep indices with
50
+ a fixed step $h = 0.1$~ms.
51
+
52
+ \section*{Environment setup}
53
+
54
+ \begin{lstlisting}
55
+ python3 -m venv .venv
56
+ source .venv/bin/activate
57
+ \end{lstlisting}
58
+
59
+ \noindent
60
+ Creates and activates a virtual environment. Install the package into it
61
+ with \texttt{pip install rnba} (version 0.2.2 or later is required for the
62
+ \opt{--workers} option in step~2).
63
+
64
+ \section*{Step 1: Run the simulation}
65
+
66
+ \begin{lstlisting}
67
+ rnba-run \
68
+ --architecture ~/NBAExperiments/Sue_Eats_Pizza/RNBA_arch_SGall_SNB15.txt \
69
+ --instruction ~/NBAExperiments/Sue_Eats_Pizza/instruction.txt \
70
+ --output ~/NBAExperiments/Sue_Eats_Pizza/RNBA_simulation_output.csv \
71
+ --output-row-map ~/NBAExperiments/Sue_Eats_Pizza/RNBA_row_map.csv \
72
+ --output-named \
73
+ --output-inputs ~/NBAExperiments/Sue_Eats_Pizza/inputs.csv
74
+ \end{lstlisting}
75
+
76
+ \begin{description}[leftmargin=0pt,style=nextline]
77
+ \item[\opt{--architecture} FILE]
78
+ The blackboard architecture (here the SNB15 configuration). It
79
+ describes every population and its wiring; \texttt{rnba-run} builds
80
+ the network from it. Required.
81
+
82
+ \item[\opt{--instruction} FILE]
83
+ The instruction file giving the timings at which external inputs
84
+ (word, gating, and binding populations) switch on and off. It uses
85
+ the unified instruction format and is parsed strictly. The
86
+ simulation duration is derived automatically from the schedule (plus
87
+ a settling tail; see \opt{--tail-steps}).
88
+
89
+ \item[\opt{--output} FILE]
90
+ Destination CSV for the population traces: one row per timestep, one
91
+ column per population, with a \texttt{time\_ms} first column.
92
+
93
+ \item[\opt{--output-row-map} FILE]
94
+ Writes the mapping between CSV column (row index) and population
95
+ metadata (\texttt{pop\_id}, type, circuit, etc.). The visualiser and
96
+ other downstream tools use it to know which column is which
97
+ population.
98
+
99
+ \item[\opt{--output-named}]
100
+ Also writes a second copy of the trace CSV whose column headers are
101
+ the human-readable \texttt{pop\_id[row]} names rather than bare row
102
+ indices. With no argument it defaults to \texttt{<output>\_named.csv}.
103
+ Because \opt{--output-inputs} is also given, a named copy of the
104
+ input traces is written alongside it as well.
105
+
106
+ \item[\opt{--output-inputs} FILE]
107
+ Writes the external input traces (the driven activation of the input
108
+ populations) to a separate CSV. Step~2 reads this to display the
109
+ inputs in the video.
110
+ \end{description}
111
+
112
+ \paragraph{Duration.}
113
+ No duration flag is passed, so the length is computed from the
114
+ instruction schedule plus \opt{--tail-steps} (default 500 settling
115
+ steps). Override with \opt{--t-end} MS or \opt{--n-steps} N if a fixed
116
+ length is needed.
117
+
118
+ \section*{Step 2: Render the activity-flow video}
119
+
120
+ \begin{lstlisting}
121
+ python -m rnba.visualize_activity_flow \
122
+ --dpi 75 --workers 6 \
123
+ --architecture RNBA_arch_SGall_SNB15.txt \
124
+ --trace-csv RNBA_simulation_output.csv \
125
+ --inputs-csv inputs.csv \
126
+ --out-dir activity_flow_out \
127
+ --render-video \
128
+ --frames-dir activity_flow_out/frames \
129
+ --video-out Sue_Eats_Pizza_activity.mp4 \
130
+ --fps 25 --step-ms 1.0
131
+ \end{lstlisting}
132
+
133
+ \noindent
134
+ This command is run from inside the \texttt{Sue\_Eats\_Pizza} directory,
135
+ so the file arguments are relative paths referring to the outputs of
136
+ step~1.
137
+
138
+ \begin{description}[leftmargin=0pt,style=nextline]
139
+ \item[\opt{--dpi} 75]
140
+ Rendering resolution. The figure canvas is $13\times18$ inches, so
141
+ \opt{--dpi}~75 yields $975\times1350$-pixel frames. Lower DPI gives
142
+ smaller files but only marginally faster rendering, because the cost
143
+ is dominated by drawing the schematic rather than rasterising pixels.
144
+
145
+ \item[\opt{--workers} 6]
146
+ Number of parallel worker processes for frame rendering. Each worker
147
+ builds its own copy of the base schematic and renders a share of the
148
+ frames; the output is identical to a sequential run. Choose a value
149
+ up to the CPU core count (each worker holds a full figure, so watch
150
+ memory). Requires rnba 0.2.2 or later.
151
+
152
+ \item[\opt{--architecture} FILE]
153
+ The same architecture file as step~1. The visualiser uses it to lay
154
+ out the schematic and to map trace columns onto schematic symbols.
155
+
156
+ \item[\opt{--trace-csv} FILE]
157
+ The population-trace CSV produced by step~1 (\opt{--output}). Its
158
+ values drive the brightness and edge intensity of the overlay.
159
+
160
+ \item[\opt{--inputs-csv} FILE]
161
+ The external-input trace CSV produced by step~1
162
+ (\opt{--output-inputs}). Merged into each frame so the driving
163
+ inputs are shown alongside the network activity.
164
+
165
+ \item[\opt{--out-dir} DIR]
166
+ Output directory for the renderer's auxiliary artifacts (symbol
167
+ catalogue, representative mapping, and the single-frame preview PNG).
168
+
169
+ \item[\opt{--render-video}]
170
+ Enables the full video pass. Without it the tool writes only a
171
+ single static preview frame. With it, frames are sampled across the
172
+ whole trace and assembled into an MP4 with \texttt{ffmpeg} (which
173
+ must be on \texttt{PATH}).
174
+
175
+ \item[\opt{--frames-dir} DIR]
176
+ Directory for the individual PNG frames. Any existing
177
+ \texttt{frame\_*.png} files there are cleared first, then one PNG is
178
+ written per sampled timestep before muxing.
179
+
180
+ \item[\opt{--video-out} FILE]
181
+ Path of the final MP4. Encoded with \texttt{libx264} / \texttt{yuv444p}
182
+ for broad player compatibility.
183
+
184
+ \item[\opt{--fps} 25]
185
+ Frames per second of the output video (playback rate).
186
+
187
+ \item[\opt{--step-ms} 1.0]
188
+ Sampling stride over the trace, in milliseconds: one frame is
189
+ rendered per 1~ms of simulated time. Smaller values give smoother,
190
+ longer videos with more frames; larger values render faster.
191
+ \end{description}
192
+
193
+ \paragraph{Result.}
194
+ \texttt{Sue\_Eats\_Pizza\_activity.mp4} shows the activity flowing
195
+ through the blackboard as the sentence is presented, with the auxiliary
196
+ CSVs and preview PNG left in \texttt{activity\_flow\_out/} for
197
+ inspection.
198
+
199
+ \section*{Related documentation}
200
+
201
+ \begin{itemize}[leftmargin=*]
202
+ \item Instruction file format: \texttt{docs/instruction-file-formats.md}
203
+ \item Bindings file format: \texttt{docs/bindings-file-format.md}
204
+ \item Full option lists: \texttt{rnba-run -{}-help} and
205
+ \texttt{python -m rnba.visualize\_activity\_flow -{}-help}
206
+ \end{itemize}
207
+
208
+ \end{document}