rnba 0.2.0__tar.gz → 0.2.2__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.
- {rnba-0.2.0 → rnba-0.2.2}/PKG-INFO +12 -7
- {rnba-0.2.0 → rnba-0.2.2}/README.md +11 -6
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/include/parser.hpp +10 -29
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/src/main.cpp +5 -9
- rnba-0.2.2/cpp-sim/src/parser.cpp +366 -0
- rnba-0.2.2/cpp-sim/tests/test_parser.cpp +196 -0
- rnba-0.2.2/docs/analysis/schematic_inclusion_matrix.md +52 -0
- rnba-0.2.2/docs/bindings-file-format.md +61 -0
- rnba-0.2.2/docs/instruction-file-formats.md +218 -0
- rnba-0.2.2/docs/templates/schematic_template_manifest.md +44 -0
- {rnba-0.2.0 → rnba-0.2.2}/pyproject.toml +1 -1
- {rnba-0.2.0 → rnba-0.2.2}/rnba/__init__.py +11 -3
- {rnba-0.2.0 → rnba-0.2.2}/rnba/architecture.py +163 -115
- {rnba-0.2.0 → rnba-0.2.2}/rnba/simulation.py +10 -41
- {rnba-0.2.0 → rnba-0.2.2}/rnba/visualize_activity_flow.py +252 -42
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_cpp_ncv_golden.py +16 -5
- rnba-0.2.2/tests/test_instruction_unified.py +196 -0
- rnba-0.2.2/tests/test_rnba_arch_converter.py +207 -0
- rnba-0.2.0/cpp-sim/src/parser.cpp +0 -426
- rnba-0.2.0/cpp-sim/tests/test_parser.cpp +0 -167
- rnba-0.2.0/docs/instruction-file-formats.md +0 -216
- rnba-0.2.0/docs/templates/schematic_template_manifest.md +0 -29
- {rnba-0.2.0 → rnba-0.2.2}/.gitignore +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/LICENSE +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/NBA24_QA_sentence_input.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/NBA_SGall_sentence_input.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/NBA_read_architecture.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/.gitignore +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/CMakeLists.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/CMakePresets.json +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/README.md +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/data/README.md +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/data/example_snb1_contract.json +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/data/model_smoke_contract.json +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/data/network.schema.json +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/data/snb15_contract.json +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/data/snb20_contract.json +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/include/model.hpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/include/output.hpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/include/simulator.hpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/include/sparse_matrix.hpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/src/model.cpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/src/output.cpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/src/simulator.cpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/src/sparse_matrix.cpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tests/CMakeLists.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tests/data/model_smoke_contract.json +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tests/data/snb20_contract.json +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tests/test_golden.cpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tests/test_simulator.cpp +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tools/export_network.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tools/plot_cpp_qa.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tools/plot_ncv_output.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/cpp-sim/tools/plot_trace_comparison.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/docs/analysis/QA_GENERATED_SIMULATION_WALKTHROUGH.md +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/docs/analysis/README.md +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/docs/analysis/collapse_verification.md +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/docs/analysis/rowviz.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/docs/analysis/visualize_core_circuits.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/docs/snb15_population_categories.md +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/Collapsed_network_force.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/Collapsed_network_force_2026-05-22_noring_v1.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/Example_01_WIlsonCowan.ipynb +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/Example_02_BasicCircuits.ipynb +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/collapse_bgates.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/colour_gexf.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/demo_network_visualization.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/example_2.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/example_3.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/example_clamp.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/example_nCV_generated.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/example_network_creation.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/example_qa_create_network.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/example_qa_generated.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/example_qa_simulation.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_circle_16k.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_circle_ei_32k.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_ei_grey_32k.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_ei_grey_seeded_32k.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_force_16k.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_force_ei_32k.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_grey_16k.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_sfdp.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_sfdp_ei.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_sfdp_grey.gephi +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_network_sfdp_rows12.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/full_scale_sfdp_visualization.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/gexf_roles.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/layout_rows.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/render_collapsed.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/render_gexf.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/rnba_roles_row1.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/rnba_roles_snb15.gexf +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/row_visualization.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/seed_circle_ext.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/seed_ei_gexf.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/examples/visualize_architecture.sh +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/_sim.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/blackboard.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/circuit.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/circuits/__init__.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/circuits/core.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/cli.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/constants.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/converters/__init__.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/converters/architecture.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/inputs.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/network.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/nodes.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/plotting.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/schematic_visualization.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/rnba/visualize.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/QA_3NVN_O_fail.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/QA_3NVN_O_success.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/RNBA_SGall_SNB15_Bindings_Sentence_3NVN.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/RNBA_arch_SGall_SNB15.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/RNBA_arch_SGall_SNB20.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/RNBA_arch_SGall_SNB30.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/Sentence_2CV.txt +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/conftest.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/integration/__init__.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/integration/test_architecture_vs_gexf.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_architecture.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_basic.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_blackboard.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_circuit.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_circuits.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_clamp_integration.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_instruction_window_semantics.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_network.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_nodes.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_rnba_simulation_launcher.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_simulation_engines.py +0 -0
- {rnba-0.2.0 → rnba-0.2.2}/tests/test_visualize.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: rnba
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
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
|
|
@@ -74,8 +74,8 @@ Plotting and visualization extras: `pip install "rnba[viz]"` (matplotlib + graph
|
|
|
74
74
|
This is the typical use of the package. A simulation needs:
|
|
75
75
|
|
|
76
76
|
1. an **architecture file** describing the complete neural blackboard (e.g. `tests/RNBA_arch_SGall_SNB15.txt`);
|
|
77
|
-
2. an **instruction file** giving the timings at which populations are switched on and off — word inputs, gating populations, etc. Both instruction
|
|
78
|
-
3. optionally a **bindings file**, which pre-stores bindings in the network, emulating sentences that were read into the blackboard previously (e.g. `tests/RNBA_SGall_SNB15_Bindings_Sentence_3NVN.txt`).
|
|
77
|
+
2. an **instruction file** giving the timings at which populations are switched on and off — word inputs, gating populations, etc. Both simulators read the unified instruction format (legacy QA and SGall files are valid subsets) with strict parsing; it is fully documented in [docs/instruction-file-formats.md](docs/instruction-file-formats.md);
|
|
78
|
+
3. optionally a **bindings file**, which pre-stores bindings in the network, emulating sentences that were read into the blackboard previously (e.g. `tests/RNBA_SGall_SNB15_Bindings_Sentence_3NVN.txt`); the format is documented in [docs/bindings-file-format.md](docs/bindings-file-format.md).
|
|
79
79
|
|
|
80
80
|
The output is a CSV with one row per timestep (`h = 0.1` ms) and one column per population.
|
|
81
81
|
|
|
@@ -161,6 +161,10 @@ python -m rnba.visualize_activity_flow \
|
|
|
161
161
|
--fps 25 --step-ms 1.0
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
To zoom in on the top of the blackboard, add `--first-rows N` (e.g. `--first-rows 5`): the view is cropped to the first N rows and markers are enlarged for legibility, while the layout and trace mapping stay identical to the full 15-row view. This works for both the single-frame preview (`--preview-time-ms`) and full video rendering, and mirrors the `--first-rows` option of the static schematic renderer (see below).
|
|
165
|
+
|
|
166
|
+
Frame rendering dominates the runtime. Pass `--workers N` to render frames across N processes (each holds its own copy of the base schematic); output is identical to a sequential run. `--dpi` trades resolution for size but has little effect on speed, since most per-frame cost is drawing the schematic rather than rasterising pixels.
|
|
167
|
+
|
|
164
168
|
See `python -m rnba.visualize_activity_flow --help` for the full option set (codec, styling, frame selection).
|
|
165
169
|
|
|
166
170
|
## Quick Start: Replicating the QA Simulation
|
|
@@ -222,10 +226,11 @@ RNBA uses **two complementary naming schemes**:
|
|
|
222
226
|
## Documentation
|
|
223
227
|
|
|
224
228
|
Project documentation lives in the docs/ folder:
|
|
225
|
-
- Instruction file
|
|
226
|
-
-
|
|
227
|
-
-
|
|
228
|
-
-
|
|
229
|
+
- Instruction file format (unified; legacy QA and SGall dialects): [docs/instruction-file-formats.md](docs/instruction-file-formats.md)
|
|
230
|
+
- Bindings file format: [docs/bindings-file-format.md](docs/bindings-file-format.md)
|
|
231
|
+
- Analysis: [docs/analysis/](docs/analysis/)
|
|
232
|
+
- Design notes: [docs/design/](docs/design/)
|
|
233
|
+
- Circuit library design and implementation status: [docs/design/CIRCUIT_LIBRARY_DESIGN.md](docs/design/CIRCUIT_LIBRARY_DESIGN.md)
|
|
229
234
|
|
|
230
235
|
### Circuit Factory Functions
|
|
231
236
|
|
|
@@ -37,8 +37,8 @@ Plotting and visualization extras: `pip install "rnba[viz]"` (matplotlib + graph
|
|
|
37
37
|
This is the typical use of the package. A simulation needs:
|
|
38
38
|
|
|
39
39
|
1. an **architecture file** describing the complete neural blackboard (e.g. `tests/RNBA_arch_SGall_SNB15.txt`);
|
|
40
|
-
2. an **instruction file** giving the timings at which populations are switched on and off — word inputs, gating populations, etc. Both instruction
|
|
41
|
-
3. optionally a **bindings file**, which pre-stores bindings in the network, emulating sentences that were read into the blackboard previously (e.g. `tests/RNBA_SGall_SNB15_Bindings_Sentence_3NVN.txt`).
|
|
40
|
+
2. an **instruction file** giving the timings at which populations are switched on and off — word inputs, gating populations, etc. Both simulators read the unified instruction format (legacy QA and SGall files are valid subsets) with strict parsing; it is fully documented in [docs/instruction-file-formats.md](docs/instruction-file-formats.md);
|
|
41
|
+
3. optionally a **bindings file**, which pre-stores bindings in the network, emulating sentences that were read into the blackboard previously (e.g. `tests/RNBA_SGall_SNB15_Bindings_Sentence_3NVN.txt`); the format is documented in [docs/bindings-file-format.md](docs/bindings-file-format.md).
|
|
42
42
|
|
|
43
43
|
The output is a CSV with one row per timestep (`h = 0.1` ms) and one column per population.
|
|
44
44
|
|
|
@@ -124,6 +124,10 @@ python -m rnba.visualize_activity_flow \
|
|
|
124
124
|
--fps 25 --step-ms 1.0
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
To zoom in on the top of the blackboard, add `--first-rows N` (e.g. `--first-rows 5`): the view is cropped to the first N rows and markers are enlarged for legibility, while the layout and trace mapping stay identical to the full 15-row view. This works for both the single-frame preview (`--preview-time-ms`) and full video rendering, and mirrors the `--first-rows` option of the static schematic renderer (see below).
|
|
128
|
+
|
|
129
|
+
Frame rendering dominates the runtime. Pass `--workers N` to render frames across N processes (each holds its own copy of the base schematic); output is identical to a sequential run. `--dpi` trades resolution for size but has little effect on speed, since most per-frame cost is drawing the schematic rather than rasterising pixels.
|
|
130
|
+
|
|
127
131
|
See `python -m rnba.visualize_activity_flow --help` for the full option set (codec, styling, frame selection).
|
|
128
132
|
|
|
129
133
|
## Quick Start: Replicating the QA Simulation
|
|
@@ -185,10 +189,11 @@ RNBA uses **two complementary naming schemes**:
|
|
|
185
189
|
## Documentation
|
|
186
190
|
|
|
187
191
|
Project documentation lives in the docs/ folder:
|
|
188
|
-
- Instruction file
|
|
189
|
-
-
|
|
190
|
-
-
|
|
191
|
-
-
|
|
192
|
+
- Instruction file format (unified; legacy QA and SGall dialects): [docs/instruction-file-formats.md](docs/instruction-file-formats.md)
|
|
193
|
+
- Bindings file format: [docs/bindings-file-format.md](docs/bindings-file-format.md)
|
|
194
|
+
- Analysis: [docs/analysis/](docs/analysis/)
|
|
195
|
+
- Design notes: [docs/design/](docs/design/)
|
|
196
|
+
- Circuit library design and implementation status: [docs/design/CIRCUIT_LIBRARY_DESIGN.md](docs/design/CIRCUIT_LIBRARY_DESIGN.md)
|
|
192
197
|
|
|
193
198
|
### Circuit Factory Functions
|
|
194
199
|
|
|
@@ -17,42 +17,23 @@ struct ScheduleWindow {
|
|
|
17
17
|
double amplitude;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
enum class InstructionFormat {
|
|
21
|
-
SGall,
|
|
22
|
-
QA,
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
struct ParsedInstructionSchedule {
|
|
26
|
-
InstructionFormat format;
|
|
27
|
-
std::map<std::string, std::vector<ScheduleWindow>> schedule;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
20
|
/**
|
|
31
|
-
* Parse instruction file (
|
|
21
|
+
* Parse an instruction file in the unified format (see
|
|
22
|
+
* docs/instruction-file-formats.md). Legacy SGall and QA files are
|
|
23
|
+
* valid subsets and parse unchanged.
|
|
24
|
+
*
|
|
25
|
+
* Parsing is strict: unknown commands, wrong arities, out-of-range word
|
|
26
|
+
* indices, misplaced directives and degenerate windows all throw
|
|
27
|
+
* std::runtime_error with the offending file/line.
|
|
28
|
+
*
|
|
29
|
+
* Times in the file are integer timestep indices (multiply by h=0.1 ms).
|
|
32
30
|
* Returns map from pop_id → list of schedule windows.
|
|
33
|
-
*
|
|
34
|
-
* File format (line-oriented):
|
|
35
|
-
* t_start t_end pop_id
|
|
36
|
-
* ...
|
|
37
|
-
* where times are in timestep indices (multiply by h=0.1 ms).
|
|
38
31
|
*/
|
|
39
32
|
std::map<std::string, std::vector<ScheduleWindow>>
|
|
40
33
|
parse_instruction_file(
|
|
41
34
|
const std::string& filepath,
|
|
42
35
|
double h = 0.1,
|
|
43
|
-
double amplitude = 100.0
|
|
44
|
-
bool strict = false
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Parse instruction file and return both detected format and schedule.
|
|
49
|
-
*/
|
|
50
|
-
ParsedInstructionSchedule
|
|
51
|
-
parse_instruction_file_with_format(
|
|
52
|
-
const std::string& filepath,
|
|
53
|
-
double h = 0.1,
|
|
54
|
-
double amplitude = 100.0,
|
|
55
|
-
bool strict = false
|
|
36
|
+
double amplitude = 100.0
|
|
56
37
|
);
|
|
57
38
|
|
|
58
39
|
/**
|
|
@@ -30,7 +30,8 @@ void print_usage(const char* prog) {
|
|
|
30
30
|
<< " --t-end N*0.1. Mutually exclusive with --t-end.\n\n"
|
|
31
31
|
<< "Optional arguments:\n"
|
|
32
32
|
<< " --instructions FILE\n"
|
|
33
|
-
<< "
|
|
33
|
+
<< " Instruction file in the unified RNBA text format (legacy QA and\n"
|
|
34
|
+
<< " SGall files are valid subsets; parsed strictly). If provided,\n"
|
|
34
35
|
<< " instruction windows are mapped onto model input populations.\n"
|
|
35
36
|
<< " --bindings FILE\n"
|
|
36
37
|
<< " WM bindings file in the existing RNBA text format. If provided,\n"
|
|
@@ -168,18 +169,13 @@ int main(int argc, char* argv[]) {
|
|
|
168
169
|
// Parse and apply instruction schedule
|
|
169
170
|
if (!instr_file.empty()) {
|
|
170
171
|
std::cout << "Parsing instruction file: " << instr_file << "\n";
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (parsed.format == rnba::InstructionFormat::SGall) {
|
|
174
|
-
std::cout << " Detected instruction format: SGall (drive)\n";
|
|
175
|
-
} else {
|
|
176
|
-
std::cout << " Detected instruction format: QA (drive)\n";
|
|
177
|
-
}
|
|
172
|
+
std::cout << " Instruction format: unified (strict parsing)\n";
|
|
173
|
+
const auto schedule = rnba::parse_instruction_file(instr_file, 0.1, 100.0);
|
|
178
174
|
|
|
179
175
|
int applied = 0;
|
|
180
176
|
int missing = 0;
|
|
181
177
|
|
|
182
|
-
for (const auto& kv :
|
|
178
|
+
for (const auto& kv : schedule) {
|
|
183
179
|
const auto it = input_name_to_idx.find(kv.first);
|
|
184
180
|
if (it == input_name_to_idx.end()) {
|
|
185
181
|
missing += 1;
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
#include "parser.hpp"
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
#include <fstream>
|
|
4
|
+
#include <iostream>
|
|
5
|
+
#include <map>
|
|
6
|
+
#include <sstream>
|
|
7
|
+
#include <stdexcept>
|
|
8
|
+
#include <string>
|
|
9
|
+
#include <unordered_map>
|
|
10
|
+
#include <vector>
|
|
11
|
+
|
|
12
|
+
namespace rnba {
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
std::vector<std::string> split_ws(const std::string& s) {
|
|
17
|
+
std::istringstream iss(s);
|
|
18
|
+
std::vector<std::string> out;
|
|
19
|
+
std::string tok;
|
|
20
|
+
while (iss >> tok) {
|
|
21
|
+
out.push_back(tok);
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
std::unordered_map<std::string, std::string> build_command_map() {
|
|
27
|
+
std::unordered_map<std::string, std::string> m;
|
|
28
|
+
m["Word_inhib"] = "EX0a_inhib_W_all";
|
|
29
|
+
|
|
30
|
+
const std::vector<std::string> lma_short = {
|
|
31
|
+
"Adj", "Adv", "C", "Co", "Det", "N", "P", "Pr", "V"
|
|
32
|
+
};
|
|
33
|
+
const std::vector<std::string> lma_full = {
|
|
34
|
+
"Adjective", "Adverb", "Clause", "Coordinator", "Determiner",
|
|
35
|
+
"Noun", "Preposition", "Pronoun", "Verb"
|
|
36
|
+
};
|
|
37
|
+
for (size_t i = 0; i < lma_short.size(); ++i) {
|
|
38
|
+
const std::string& sh = lma_short[i];
|
|
39
|
+
const std::string& fu = lma_full[i];
|
|
40
|
+
m[fu] = "EX2_Bgate_W_" + sh;
|
|
41
|
+
m[fu + "_H"] = "EX3H_Gate_" + sh + "_H";
|
|
42
|
+
m[fu + "_D"] = "EX3D_Gate_" + sh + "_D";
|
|
43
|
+
m["CMtoW_" + fu + "_H"] = "EX8H_LMA_out_" + sh + "_H";
|
|
44
|
+
m["CMtoW_" + fu + "_D"] = "EX8D_LMA_out_" + sh + "_D";
|
|
45
|
+
m[fu + "_inhib"] = "EX0_inhib_" + sh;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const std::vector<std::string> sg_names = {
|
|
49
|
+
"SG1Verb", "SG2Subject", "SG3Object", "SG4Det", "SG5Compl",
|
|
50
|
+
"SG6Mod", "SG7Adjunct", "SG8PC", "SG9Ex_Mod", "SG10Coord", "SG11GAP"
|
|
51
|
+
};
|
|
52
|
+
for (const auto& sg : sg_names) {
|
|
53
|
+
m["binding_" + sg + "_H"] = "EX4H_Bgate_LMA_" + sg + "_H";
|
|
54
|
+
m["binding_" + sg + "_D"] = "EX4D_Bgate_LMA_" + sg + "_D";
|
|
55
|
+
m[sg + "_H"] = "EX6H_Gate_" + sg + "_CM_H";
|
|
56
|
+
m[sg + "_D"] = "EX6D_Gate_" + sg + "_CM_D";
|
|
57
|
+
m[sg + "_W_H"] = "EX5H_Gate_" + sg + "_W_H";
|
|
58
|
+
m[sg + "_W_D"] = "EX5D_Gate_" + sg + "_W_D";
|
|
59
|
+
m["inhibcomp_" + sg + "_H"] = "EX7H_SG_comp_inhib_" + sg + "_H";
|
|
60
|
+
m["inhibcomp_" + sg + "_D"] = "EX7D_SG_comp_inhib_" + sg + "_D";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return m;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const std::unordered_map<std::string, std::string> kCommandMap = build_command_map();
|
|
67
|
+
|
|
68
|
+
constexpr const char* kFormatDirectiveName = "rnba-instructions";
|
|
69
|
+
constexpr int kFormatDirectiveVersion = 1;
|
|
70
|
+
|
|
71
|
+
[[noreturn]] void parse_error(
|
|
72
|
+
const std::string& filepath,
|
|
73
|
+
int line_no,
|
|
74
|
+
const std::string& message
|
|
75
|
+
) {
|
|
76
|
+
throw std::runtime_error(
|
|
77
|
+
filepath + ":" + std::to_string(line_no) + ": " + message
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
int parse_int(
|
|
82
|
+
const std::string& token,
|
|
83
|
+
const std::string& filepath,
|
|
84
|
+
int line_no,
|
|
85
|
+
const std::string& what
|
|
86
|
+
) {
|
|
87
|
+
try {
|
|
88
|
+
size_t consumed = 0;
|
|
89
|
+
const int value = std::stoi(token, &consumed);
|
|
90
|
+
if (consumed != token.size()) {
|
|
91
|
+
parse_error(filepath, line_no,
|
|
92
|
+
what + " must be an integer, got '" + token + "'");
|
|
93
|
+
}
|
|
94
|
+
return value;
|
|
95
|
+
} catch (const std::invalid_argument&) {
|
|
96
|
+
parse_error(filepath, line_no,
|
|
97
|
+
what + " must be an integer, got '" + token + "'");
|
|
98
|
+
} catch (const std::out_of_range&) {
|
|
99
|
+
parse_error(filepath, line_no,
|
|
100
|
+
what + " out of integer range: '" + token + "'");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
} // namespace
|
|
105
|
+
|
|
106
|
+
// ─── Public Parser Interface ─────────────────────────────────────────────────
|
|
107
|
+
// Unified strict instruction parser (see docs/instruction-file-formats.md).
|
|
108
|
+
// Legacy SGall and QA files are valid subsets and parse unchanged; word
|
|
109
|
+
// windows without an explicit end take their length from fixed_time.
|
|
110
|
+
std::map<std::string, std::vector<ScheduleWindow>>
|
|
111
|
+
parse_instruction_file(
|
|
112
|
+
const std::string& filepath,
|
|
113
|
+
double h,
|
|
114
|
+
double amplitude
|
|
115
|
+
) {
|
|
116
|
+
std::map<std::string, std::vector<ScheduleWindow>> result;
|
|
117
|
+
|
|
118
|
+
std::ifstream in(filepath);
|
|
119
|
+
if (!in) {
|
|
120
|
+
throw std::runtime_error("Failed to open instruction file: " + filepath);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
bool have_word_list = false;
|
|
124
|
+
std::vector<std::string> word_list;
|
|
125
|
+
bool have_fixed_time = false;
|
|
126
|
+
int fixed_time = 0;
|
|
127
|
+
int step_size = 0;
|
|
128
|
+
bool seen_command = false;
|
|
129
|
+
bool seen_format = false;
|
|
130
|
+
bool seen_fixed_time = false;
|
|
131
|
+
bool seen_num_of_words = false;
|
|
132
|
+
|
|
133
|
+
std::string raw_line;
|
|
134
|
+
int line_no = 0;
|
|
135
|
+
while (std::getline(in, raw_line)) {
|
|
136
|
+
line_no += 1;
|
|
137
|
+
const auto parts = split_ws(raw_line);
|
|
138
|
+
if (parts.empty()) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// Comments occupy whole lines; leading whitespace is allowed.
|
|
142
|
+
if (!parts[0].empty() && parts[0][0] == '#') {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const std::string& cmd = parts[0];
|
|
147
|
+
|
|
148
|
+
if (cmd == "format" || cmd == "fixed_time" || cmd == "Num_of_words") {
|
|
149
|
+
if (seen_command) {
|
|
150
|
+
parse_error(filepath, line_no,
|
|
151
|
+
"directive '" + cmd +
|
|
152
|
+
"' must appear before the first command");
|
|
153
|
+
}
|
|
154
|
+
bool& seen_flag = (cmd == "format") ? seen_format
|
|
155
|
+
: (cmd == "fixed_time") ? seen_fixed_time
|
|
156
|
+
: seen_num_of_words;
|
|
157
|
+
if (seen_flag) {
|
|
158
|
+
parse_error(filepath, line_no, "duplicate directive '" + cmd + "'");
|
|
159
|
+
}
|
|
160
|
+
seen_flag = true;
|
|
161
|
+
|
|
162
|
+
if (cmd == "format") {
|
|
163
|
+
if (parts.size() != 3 || parts[1] != kFormatDirectiveName) {
|
|
164
|
+
parse_error(filepath, line_no,
|
|
165
|
+
std::string("format directive must read 'format ") +
|
|
166
|
+
kFormatDirectiveName + " " +
|
|
167
|
+
std::to_string(kFormatDirectiveVersion) + "'");
|
|
168
|
+
}
|
|
169
|
+
const int version = parse_int(parts[2], filepath, line_no, "format version");
|
|
170
|
+
if (version != kFormatDirectiveVersion) {
|
|
171
|
+
parse_error(filepath, line_no,
|
|
172
|
+
"unsupported format version " + std::to_string(version) +
|
|
173
|
+
" (supported: " + std::to_string(kFormatDirectiveVersion) + ")");
|
|
174
|
+
}
|
|
175
|
+
} else if (cmd == "fixed_time") {
|
|
176
|
+
if (parts.size() != 2) {
|
|
177
|
+
parse_error(filepath, line_no, "fixed_time takes exactly one value");
|
|
178
|
+
}
|
|
179
|
+
fixed_time = parse_int(parts[1], filepath, line_no, "fixed_time");
|
|
180
|
+
if (fixed_time <= 0) {
|
|
181
|
+
parse_error(filepath, line_no, "fixed_time must be positive");
|
|
182
|
+
}
|
|
183
|
+
have_fixed_time = true;
|
|
184
|
+
} else { // Num_of_words
|
|
185
|
+
if (parts.size() != 2) {
|
|
186
|
+
parse_error(filepath, line_no, "Num_of_words takes exactly one value");
|
|
187
|
+
}
|
|
188
|
+
const int n_words = parse_int(parts[1], filepath, line_no, "Num_of_words");
|
|
189
|
+
if (n_words < 0) {
|
|
190
|
+
parse_error(filepath, line_no, "Num_of_words must be non-negative");
|
|
191
|
+
}
|
|
192
|
+
word_list.clear();
|
|
193
|
+
word_list.reserve(static_cast<size_t>(n_words));
|
|
194
|
+
for (int k = 0; k < n_words; ++k) {
|
|
195
|
+
word_list.push_back("EX1_input_W" + std::to_string(k));
|
|
196
|
+
}
|
|
197
|
+
have_word_list = true;
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
seen_command = true;
|
|
203
|
+
|
|
204
|
+
int start = 0;
|
|
205
|
+
int end = 0;
|
|
206
|
+
std::string pop_id;
|
|
207
|
+
|
|
208
|
+
if (cmd == "Word") {
|
|
209
|
+
if (!have_word_list) {
|
|
210
|
+
parse_error(filepath, line_no, "'Word' encountered before Num_of_words");
|
|
211
|
+
}
|
|
212
|
+
int word_idx = 0;
|
|
213
|
+
if (parts.size() == 3) {
|
|
214
|
+
if (!have_fixed_time) {
|
|
215
|
+
parse_error(filepath, line_no,
|
|
216
|
+
"2-argument 'Word' requires a fixed_time directive; "
|
|
217
|
+
"use 'Word <rel_start> <rel_end> <word_idx>' for an explicit end");
|
|
218
|
+
}
|
|
219
|
+
const int rel_start = parse_int(parts[1], filepath, line_no, "rel_start");
|
|
220
|
+
word_idx = parse_int(parts[2], filepath, line_no, "word index");
|
|
221
|
+
start = step_size + rel_start;
|
|
222
|
+
step_size = start;
|
|
223
|
+
end = start + fixed_time;
|
|
224
|
+
} else if (parts.size() == 4) {
|
|
225
|
+
const int rel_start = parse_int(parts[1], filepath, line_no, "rel_start");
|
|
226
|
+
const int rel_end = parse_int(parts[2], filepath, line_no, "rel_end");
|
|
227
|
+
word_idx = parse_int(parts[3], filepath, line_no, "word index");
|
|
228
|
+
start = step_size + rel_start;
|
|
229
|
+
step_size = start;
|
|
230
|
+
end = start + rel_end;
|
|
231
|
+
} else {
|
|
232
|
+
parse_error(filepath, line_no,
|
|
233
|
+
"'Word' takes 2 arguments (rel_start word_idx, with fixed_time) "
|
|
234
|
+
"or 3 (rel_start rel_end word_idx)");
|
|
235
|
+
}
|
|
236
|
+
if (word_idx < 0 || word_idx >= static_cast<int>(word_list.size())) {
|
|
237
|
+
parse_error(filepath, line_no,
|
|
238
|
+
"word index " + std::to_string(word_idx) +
|
|
239
|
+
" out of range (Num_of_words = " +
|
|
240
|
+
std::to_string(word_list.size()) + ")");
|
|
241
|
+
}
|
|
242
|
+
pop_id = word_list[static_cast<size_t>(word_idx)];
|
|
243
|
+
} else {
|
|
244
|
+
const auto it = kCommandMap.find(cmd);
|
|
245
|
+
if (it == kCommandMap.end()) {
|
|
246
|
+
parse_error(filepath, line_no, "unknown command '" + cmd + "'");
|
|
247
|
+
}
|
|
248
|
+
if (parts.size() == 2) {
|
|
249
|
+
if (!have_fixed_time) {
|
|
250
|
+
parse_error(filepath, line_no,
|
|
251
|
+
"1-argument '" + cmd + "' requires a fixed_time directive; "
|
|
252
|
+
"use '" + cmd + " <rel_start> <rel_end>' for an explicit end");
|
|
253
|
+
}
|
|
254
|
+
start = step_size + parse_int(parts[1], filepath, line_no, "rel_start");
|
|
255
|
+
end = start + fixed_time;
|
|
256
|
+
} else if (parts.size() == 3) {
|
|
257
|
+
start = step_size + parse_int(parts[1], filepath, line_no, "rel_start");
|
|
258
|
+
end = step_size + parse_int(parts[2], filepath, line_no, "rel_end");
|
|
259
|
+
} else {
|
|
260
|
+
parse_error(filepath, line_no,
|
|
261
|
+
"'" + cmd + "' takes 1 argument (rel_start, with fixed_time) "
|
|
262
|
+
"or 2 (rel_start rel_end)");
|
|
263
|
+
}
|
|
264
|
+
pop_id = it->second;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (start < 0) {
|
|
268
|
+
parse_error(filepath, line_no,
|
|
269
|
+
"window start " + std::to_string(start) + " is negative");
|
|
270
|
+
}
|
|
271
|
+
if (end <= start) {
|
|
272
|
+
parse_error(filepath, line_no,
|
|
273
|
+
"window end " + std::to_string(end) +
|
|
274
|
+
" does not lie after start " + std::to_string(start));
|
|
275
|
+
}
|
|
276
|
+
result[pop_id].push_back(ScheduleWindow{start * h, end * h, amplitude});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
std::vector<int>
|
|
283
|
+
parse_bindings_file(const std::string& filepath) {
|
|
284
|
+
std::vector<int> result;
|
|
285
|
+
|
|
286
|
+
std::ifstream in(filepath);
|
|
287
|
+
if (!in) {
|
|
288
|
+
throw std::runtime_error("Failed to open bindings file: " + filepath);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
std::string raw_line;
|
|
292
|
+
while (std::getline(in, raw_line)) {
|
|
293
|
+
const auto parts = split_ws(raw_line);
|
|
294
|
+
if (parts.empty()) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
// Skip comments and non-data headings.
|
|
298
|
+
if (!parts[0].empty() && parts[0][0] == '#') {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Data lines begin with p_index as first integer token.
|
|
303
|
+
// Headings like "p_index ..." are skipped by failed stoi.
|
|
304
|
+
try {
|
|
305
|
+
const int idx = std::stoi(parts[0]);
|
|
306
|
+
if (idx < 0) {
|
|
307
|
+
throw std::runtime_error("Negative binding index encountered: " + std::to_string(idx));
|
|
308
|
+
}
|
|
309
|
+
result.push_back(idx);
|
|
310
|
+
} catch (const std::invalid_argument&) {
|
|
311
|
+
// Ignore non-numeric first token lines.
|
|
312
|
+
continue;
|
|
313
|
+
} catch (const std::out_of_range&) {
|
|
314
|
+
throw std::runtime_error("Binding index out of range in line: " + raw_line);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
std::function<double(double)>
|
|
322
|
+
make_piecewise_activation(const std::vector<ScheduleWindow>& windows) {
|
|
323
|
+
return [windows](double t) -> double {
|
|
324
|
+
for (const auto& win : windows) {
|
|
325
|
+
if (t >= win.t_start && t <= win.t_end) {
|
|
326
|
+
return win.amplitude;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return 0.0;
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
std::function<double(double)>
|
|
334
|
+
make_piecewise_activation_legacy_window(
|
|
335
|
+
const std::vector<ScheduleWindow>& windows,
|
|
336
|
+
double h
|
|
337
|
+
) {
|
|
338
|
+
const double eps = std::max(1e-12, 0.1 * h);
|
|
339
|
+
return [windows, eps](double t) -> double {
|
|
340
|
+
for (const auto& win : windows) {
|
|
341
|
+
if ((t - win.t_start) > eps && (win.t_end - t) >= -eps) {
|
|
342
|
+
return win.amplitude;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
return 0.0;
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
std::function<double(double)>
|
|
350
|
+
make_clamp_function(
|
|
351
|
+
const std::vector<int>& clamped_nodes,
|
|
352
|
+
double t_start,
|
|
353
|
+
double t_end,
|
|
354
|
+
double value
|
|
355
|
+
) {
|
|
356
|
+
(void)clamped_nodes;
|
|
357
|
+
const double eps = 1e-12;
|
|
358
|
+
return [t_start, t_end, value, eps](double t) -> double {
|
|
359
|
+
if (t + eps >= t_start && t <= t_end + eps) {
|
|
360
|
+
return value;
|
|
361
|
+
}
|
|
362
|
+
return 0.0;
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
} // namespace rnba
|