flybrainer 0.1.0__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.
- flybrainer-0.1.0/.gitignore +35 -0
- flybrainer-0.1.0/LICENSE +25 -0
- flybrainer-0.1.0/PKG-INFO +180 -0
- flybrainer-0.1.0/README.md +115 -0
- flybrainer-0.1.0/examples/quickstart.py +161 -0
- flybrainer-0.1.0/examples/real_brain.py +354 -0
- flybrainer-0.1.0/examples/real_malecns_demo.py +195 -0
- flybrainer-0.1.0/pyproject.toml +78 -0
- flybrainer-0.1.0/src/flybrainer/__init__.py +77 -0
- flybrainer-0.1.0/src/flybrainer/brain.py +523 -0
- flybrainer-0.1.0/src/flybrainer/connectome/__init__.py +59 -0
- flybrainer-0.1.0/src/flybrainer/connectome/compile.py +469 -0
- flybrainer-0.1.0/src/flybrainer/connectome/download.py +122 -0
- flybrainer-0.1.0/src/flybrainer/connectome/normalize.py +299 -0
- flybrainer-0.1.0/src/flybrainer/connectome/sources.py +61 -0
- flybrainer-0.1.0/src/flybrainer/connectome/verify.py +193 -0
- flybrainer-0.1.0/src/flybrainer/encoders.py +546 -0
- flybrainer-0.1.0/src/flybrainer/eyemap.py +133 -0
- flybrainer-0.1.0/src/flybrainer/interfaces.py +268 -0
- flybrainer-0.1.0/src/flybrainer/kernel.py +423 -0
- flybrainer-0.1.0/src/flybrainer/paths.py +53 -0
- flybrainer-0.1.0/src/flybrainer/plasticity.py +210 -0
- flybrainer-0.1.0/src/flybrainer/readout/__init__.py +5 -0
- flybrainer-0.1.0/src/flybrainer/readout/fixed.py +176 -0
- flybrainer-0.1.0/src/flybrainer/readout/reservoir.py +361 -0
- flybrainer-0.1.0/tests/test_imports.py +188 -0
- flybrainer-0.1.0/tests/test_pipeline.py +157 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# ─── Python ──────────────────────────────────────────────────────────────
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
*.egg
|
|
8
|
+
|
|
9
|
+
# ─── Virtual envs ────────────────────────────────────────────────────────
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# ─── IDE / OS ────────────────────────────────────────────────────────────
|
|
15
|
+
.idea/
|
|
16
|
+
.vscode/
|
|
17
|
+
.DS_Store
|
|
18
|
+
Thumbs.db
|
|
19
|
+
desktop.ini
|
|
20
|
+
|
|
21
|
+
# ─── Tooling caches ──────────────────────────────────────────────────────
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.ruff_cache/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
|
|
28
|
+
# ─── Foliko runtime ──────────────────────────────────────────────────────
|
|
29
|
+
.foliko/
|
|
30
|
+
|
|
31
|
+
# ─── Flybrain local data (~/.flybrain/) ──────────────────────────────────
|
|
32
|
+
# Compiled graphs and downloaded datasets are user-specific and big.
|
|
33
|
+
# Users get these via `flybrain.connectome.compile.compile_graph()`.
|
|
34
|
+
*.npz.lock
|
|
35
|
+
*.npz.tmp
|
flybrainer-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenFly contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
NOTE — The MaleCNS connectome data set (downloaded via the [download] extra)
|
|
24
|
+
is licensed under CC-BY 4.0 by the FlyEM / MaleCNS team. See
|
|
25
|
+
https://www.malecns.org for terms.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: flybrainer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A reusable, Numba-JIT accelerated fruit-fly connectome (MaleCNS v1.0) extracted from OpenFly
|
|
5
|
+
Project-URL: Homepage, https://github.com/chnak/flybrain
|
|
6
|
+
Project-URL: Source, https://github.com/chnak/flybrain
|
|
7
|
+
Project-URL: Issues, https://github.com/chnak/flybrain/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/chnak/flybrain/blob/main/docs/USAGE.zh.md
|
|
9
|
+
Author: OpenFly contributors
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 OpenFly contributors
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
|
|
32
|
+
NOTE — The MaleCNS connectome data set (downloaded via the [download] extra)
|
|
33
|
+
is licensed under CC-BY 4.0 by the FlyEM / MaleCNS team. See
|
|
34
|
+
https://www.malecns.org for terms.
|
|
35
|
+
License-File: LICENSE
|
|
36
|
+
Keywords: connectomics,fruit-fly,neural-simulation,neuroscience,numba
|
|
37
|
+
Classifier: Development Status :: 3 - Alpha
|
|
38
|
+
Classifier: Intended Audience :: Science/Research
|
|
39
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
40
|
+
Classifier: Operating System :: MacOS
|
|
41
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
42
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
45
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Life
|
|
46
|
+
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
47
|
+
Requires-Python: <3.14,>=3.12
|
|
48
|
+
Requires-Dist: numba>=0.61
|
|
49
|
+
Requires-Dist: numpy<2.5,>=2.2
|
|
50
|
+
Requires-Dist: pillow>=10
|
|
51
|
+
Provides-Extra: dev
|
|
52
|
+
Requires-Dist: pytest-cov>=5; extra == 'dev'
|
|
53
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
54
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
55
|
+
Provides-Extra: download
|
|
56
|
+
Requires-Dist: requests>=2.32; extra == 'download'
|
|
57
|
+
Provides-Extra: feather
|
|
58
|
+
Requires-Dist: pandas>=2.2; extra == 'feather'
|
|
59
|
+
Requires-Dist: pyarrow>=17; extra == 'feather'
|
|
60
|
+
Provides-Extra: readout
|
|
61
|
+
Requires-Dist: joblib>=1.4; extra == 'readout'
|
|
62
|
+
Requires-Dist: scikit-learn>=1.5; extra == 'readout'
|
|
63
|
+
Requires-Dist: scipy>=1.14; extra == 'readout'
|
|
64
|
+
Description-Content-Type: text/markdown
|
|
65
|
+
|
|
66
|
+
# flybrainer
|
|
67
|
+
|
|
68
|
+
A reusable, Numba-JIT accelerated **fruit-fly connectome** simulator —
|
|
69
|
+
extracted from [OpenFly](https://github.com/marketcalls/openfly).
|
|
70
|
+
|
|
71
|
+
* **Zero heavy deps** by default: `numpy` + `numba` + `pillow` only.
|
|
72
|
+
* **MaleCNS v1.0 connectome** loadable via `[feather,download]` extras.
|
|
73
|
+
* **3 encoders** + **2 readouts** + **optional plasticity**.
|
|
74
|
+
* **Closable LIF** kernel (Numba `@njit`), 18M+ spikes/sec.
|
|
75
|
+
|
|
76
|
+
## Why?
|
|
77
|
+
|
|
78
|
+
OpenFly is a trading system. The brain model inside it is excellent but
|
|
79
|
+
inseparable from the broker / market / front-end code. This package
|
|
80
|
+
isolates the brain so it can be reused for **any** sensor → readout task:
|
|
81
|
+
trading, robotics, audio classification, RL environments, etc.
|
|
82
|
+
|
|
83
|
+
## Install
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
# Minimum (no MaleCNS, no sklearn):
|
|
87
|
+
pip install flybrainer
|
|
88
|
+
|
|
89
|
+
# With everything:
|
|
90
|
+
pip install flybrainer[feather,readout,download]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 30-second example
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import numpy as np
|
|
97
|
+
from flybrainer import Brain, Stimulus
|
|
98
|
+
|
|
99
|
+
# Build a tiny synthetic graph (no MaleCNS required)
|
|
100
|
+
n = 100
|
|
101
|
+
g = dict(
|
|
102
|
+
ptr=np.array([0, n], dtype=np.int64),
|
|
103
|
+
post=np.random.randint(0, n, size=n).astype(np.int32),
|
|
104
|
+
weight=np.where(np.random.rand(n) > 0.5, 0.275, -0.275),
|
|
105
|
+
modulatory=np.zeros(n, dtype=bool),
|
|
106
|
+
type=np.array(["L1"]*90 + ["KC"]*4 + ["MBON07"]*3 + ["MBON11"]*3),
|
|
107
|
+
superclass=np.array(["cb_int"]*n),
|
|
108
|
+
r16_uv=np.zeros((20, 2), dtype=np.float32),
|
|
109
|
+
r8_uv=np.zeros((20, 2), dtype=np.float32),
|
|
110
|
+
r16_eye=np.zeros(20, dtype=np.int8),
|
|
111
|
+
r8_eye=np.zeros(20, dtype=np.int8),
|
|
112
|
+
r8_channel=np.ones(20, dtype=np.int8),
|
|
113
|
+
r16=np.arange(20, dtype=np.int32),
|
|
114
|
+
r8=np.arange(20, dtype=np.int32),
|
|
115
|
+
)
|
|
116
|
+
brain = Brain(graph_path="<test>", graph=g)
|
|
117
|
+
stim = Stimulus(r16=np.random.rand(20), r8=np.random.rand(20))
|
|
118
|
+
result = brain.observe(stim, neural_ms=500.0)
|
|
119
|
+
print("total spikes:", result.counts.sum())
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
See `examples/` for more.
|
|
123
|
+
|
|
124
|
+
## Architecture
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
src/flybrainer/
|
|
128
|
+
├── interfaces.py # zero-dep contracts (Stimulus, BrainProtocol, SensorFrame)
|
|
129
|
+
├── eyemap.py # EyeMap dataclass + resolver
|
|
130
|
+
├── encoders.py # ChartEncoder, BarsEncoder, FeatureEncoder + make_encoder()
|
|
131
|
+
├── kernel.py # Numba @njit LIF kernel + KernelState
|
|
132
|
+
├── plasticity.py # KC→MBON plasticity (Ormond-style Oja)
|
|
133
|
+
├── brain.py # Brain class (the orchestrator)
|
|
134
|
+
├── readout/
|
|
135
|
+
│ ├── fixed.py # FixedDecoder (no training)
|
|
136
|
+
│ └── reservoir.py # ReservoirReadout (ridge / logistic; needs [readout])
|
|
137
|
+
└── connectome/
|
|
138
|
+
├── normalize.py # NT sign rules (feather source → arrays; needs [feather])
|
|
139
|
+
├── compile.py # arrays → graph.npz (multi-platform)
|
|
140
|
+
├── sources.py # MaleCNS v1.0 metadata + SHA256
|
|
141
|
+
├── download.py # resumable download (urllib only)
|
|
142
|
+
└── verify.py # sha256 file/array helpers
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Optional dependencies
|
|
146
|
+
|
|
147
|
+
| extra | adds | when needed |
|
|
148
|
+
|---|---|---|
|
|
149
|
+
| `feather` | `pandas`, `pyarrow` | `from flybrainer.connectome import normalize, compile_graph` |
|
|
150
|
+
| `readout` | `scikit-learn`, `scipy`, `joblib` | `from flybrainer.readout import ReservoirReadout` |
|
|
151
|
+
| `download` | `requests` | `download_source(...)` |
|
|
152
|
+
| `dev` | `pytest`, `pytest-cov`, `ruff` | testing |
|
|
153
|
+
|
|
154
|
+
Without `[feather]`, `compile_graph` still works if `.pre` arrays are
|
|
155
|
+
already on disk (you just can't re-derive from raw `.feather` files).
|
|
156
|
+
|
|
157
|
+
## Tests
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
pip install flybrainer[feather,readout,dev]
|
|
161
|
+
pytest tests/ -v
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Citation
|
|
165
|
+
|
|
166
|
+
* Brain model: Aso & Gorinov — *bioRxiv 2024*
|
|
167
|
+
* Connectome: MaleCNS v1.0 — *https://www.malecns.org*
|
|
168
|
+
* This extraction: OpenFly — *github.com/marketcalls/openfly*
|
|
169
|
+
|
|
170
|
+
## Documentation
|
|
171
|
+
|
|
172
|
+
Detailed Chinese user manual (API contracts, 7 worked scenarios, FAQ):
|
|
173
|
+
|
|
174
|
+
* [`docs/USAGE.zh.md`](docs/USAGE.zh.md) — 991 lines / 10 chapters
|
|
175
|
+
|
|
176
|
+
Quick look-up order: 第 3 章 (core concepts) → 第 5 章 (full API) → 第 6 章 (worked scenarios) → 第 10 章 (FAQ).
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT. The MaleCNS dataset is CC-BY 4.0 (separate license).
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# flybrainer
|
|
2
|
+
|
|
3
|
+
A reusable, Numba-JIT accelerated **fruit-fly connectome** simulator —
|
|
4
|
+
extracted from [OpenFly](https://github.com/marketcalls/openfly).
|
|
5
|
+
|
|
6
|
+
* **Zero heavy deps** by default: `numpy` + `numba` + `pillow` only.
|
|
7
|
+
* **MaleCNS v1.0 connectome** loadable via `[feather,download]` extras.
|
|
8
|
+
* **3 encoders** + **2 readouts** + **optional plasticity**.
|
|
9
|
+
* **Closable LIF** kernel (Numba `@njit`), 18M+ spikes/sec.
|
|
10
|
+
|
|
11
|
+
## Why?
|
|
12
|
+
|
|
13
|
+
OpenFly is a trading system. The brain model inside it is excellent but
|
|
14
|
+
inseparable from the broker / market / front-end code. This package
|
|
15
|
+
isolates the brain so it can be reused for **any** sensor → readout task:
|
|
16
|
+
trading, robotics, audio classification, RL environments, etc.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Minimum (no MaleCNS, no sklearn):
|
|
22
|
+
pip install flybrainer
|
|
23
|
+
|
|
24
|
+
# With everything:
|
|
25
|
+
pip install flybrainer[feather,readout,download]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 30-second example
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import numpy as np
|
|
32
|
+
from flybrainer import Brain, Stimulus
|
|
33
|
+
|
|
34
|
+
# Build a tiny synthetic graph (no MaleCNS required)
|
|
35
|
+
n = 100
|
|
36
|
+
g = dict(
|
|
37
|
+
ptr=np.array([0, n], dtype=np.int64),
|
|
38
|
+
post=np.random.randint(0, n, size=n).astype(np.int32),
|
|
39
|
+
weight=np.where(np.random.rand(n) > 0.5, 0.275, -0.275),
|
|
40
|
+
modulatory=np.zeros(n, dtype=bool),
|
|
41
|
+
type=np.array(["L1"]*90 + ["KC"]*4 + ["MBON07"]*3 + ["MBON11"]*3),
|
|
42
|
+
superclass=np.array(["cb_int"]*n),
|
|
43
|
+
r16_uv=np.zeros((20, 2), dtype=np.float32),
|
|
44
|
+
r8_uv=np.zeros((20, 2), dtype=np.float32),
|
|
45
|
+
r16_eye=np.zeros(20, dtype=np.int8),
|
|
46
|
+
r8_eye=np.zeros(20, dtype=np.int8),
|
|
47
|
+
r8_channel=np.ones(20, dtype=np.int8),
|
|
48
|
+
r16=np.arange(20, dtype=np.int32),
|
|
49
|
+
r8=np.arange(20, dtype=np.int32),
|
|
50
|
+
)
|
|
51
|
+
brain = Brain(graph_path="<test>", graph=g)
|
|
52
|
+
stim = Stimulus(r16=np.random.rand(20), r8=np.random.rand(20))
|
|
53
|
+
result = brain.observe(stim, neural_ms=500.0)
|
|
54
|
+
print("total spikes:", result.counts.sum())
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
See `examples/` for more.
|
|
58
|
+
|
|
59
|
+
## Architecture
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
src/flybrainer/
|
|
63
|
+
├── interfaces.py # zero-dep contracts (Stimulus, BrainProtocol, SensorFrame)
|
|
64
|
+
├── eyemap.py # EyeMap dataclass + resolver
|
|
65
|
+
├── encoders.py # ChartEncoder, BarsEncoder, FeatureEncoder + make_encoder()
|
|
66
|
+
├── kernel.py # Numba @njit LIF kernel + KernelState
|
|
67
|
+
├── plasticity.py # KC→MBON plasticity (Ormond-style Oja)
|
|
68
|
+
├── brain.py # Brain class (the orchestrator)
|
|
69
|
+
├── readout/
|
|
70
|
+
│ ├── fixed.py # FixedDecoder (no training)
|
|
71
|
+
│ └── reservoir.py # ReservoirReadout (ridge / logistic; needs [readout])
|
|
72
|
+
└── connectome/
|
|
73
|
+
├── normalize.py # NT sign rules (feather source → arrays; needs [feather])
|
|
74
|
+
├── compile.py # arrays → graph.npz (multi-platform)
|
|
75
|
+
├── sources.py # MaleCNS v1.0 metadata + SHA256
|
|
76
|
+
├── download.py # resumable download (urllib only)
|
|
77
|
+
└── verify.py # sha256 file/array helpers
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Optional dependencies
|
|
81
|
+
|
|
82
|
+
| extra | adds | when needed |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| `feather` | `pandas`, `pyarrow` | `from flybrainer.connectome import normalize, compile_graph` |
|
|
85
|
+
| `readout` | `scikit-learn`, `scipy`, `joblib` | `from flybrainer.readout import ReservoirReadout` |
|
|
86
|
+
| `download` | `requests` | `download_source(...)` |
|
|
87
|
+
| `dev` | `pytest`, `pytest-cov`, `ruff` | testing |
|
|
88
|
+
|
|
89
|
+
Without `[feather]`, `compile_graph` still works if `.pre` arrays are
|
|
90
|
+
already on disk (you just can't re-derive from raw `.feather` files).
|
|
91
|
+
|
|
92
|
+
## Tests
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pip install flybrainer[feather,readout,dev]
|
|
96
|
+
pytest tests/ -v
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Citation
|
|
100
|
+
|
|
101
|
+
* Brain model: Aso & Gorinov — *bioRxiv 2024*
|
|
102
|
+
* Connectome: MaleCNS v1.0 — *https://www.malecns.org*
|
|
103
|
+
* This extraction: OpenFly — *github.com/marketcalls/openfly*
|
|
104
|
+
|
|
105
|
+
## Documentation
|
|
106
|
+
|
|
107
|
+
Detailed Chinese user manual (API contracts, 7 worked scenarios, FAQ):
|
|
108
|
+
|
|
109
|
+
* [`docs/USAGE.zh.md`](docs/USAGE.zh.md) — 991 lines / 10 chapters
|
|
110
|
+
|
|
111
|
+
Quick look-up order: 第 3 章 (core concepts) → 第 5 章 (full API) → 第 6 章 (worked scenarios) → 第 10 章 (FAQ).
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT. The MaleCNS dataset is CC-BY 4.0 (separate license).
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""End-to-end flybrainer example using a stub Brain (no MaleCNS download).
|
|
2
|
+
|
|
3
|
+
This file is runnable as:
|
|
4
|
+
|
|
5
|
+
python examples/quickstart.py
|
|
6
|
+
|
|
7
|
+
It demonstrates the full pipeline:
|
|
8
|
+
SensorFrame --[Encoder]--> Stimulus --[Brain]--> counts --[Decoder]--> Prediction
|
|
9
|
+
|
|
10
|
+
The "Brain" here is a 256-neuron stub that satisfies ``BrainProtocol`` and
|
|
11
|
+
produces deterministic-ish Poisson counts. Replace it with the real
|
|
12
|
+
``Brain.load_graph(...)`` once you have compiled a graph from the
|
|
13
|
+
MaleCNS dataset.
|
|
14
|
+
"""
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from datetime import datetime, timedelta, timezone
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
from flybrainer import (
|
|
24
|
+
REQUIRED_POPULATIONS,
|
|
25
|
+
BrainProtocol,
|
|
26
|
+
Decision,
|
|
27
|
+
ObservationResult,
|
|
28
|
+
Prediction,
|
|
29
|
+
SensorFrame,
|
|
30
|
+
Stimulus,
|
|
31
|
+
)
|
|
32
|
+
from flybrainer.encoders import make_encoder
|
|
33
|
+
from flybrainer.readout.fixed import FixedDecoder
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Stub implementation of BrainProtocol. Replace with:
|
|
37
|
+
# from flybrainer import Brain, load_graph
|
|
38
|
+
# brain = Brain(graph_path="connectome/...")
|
|
39
|
+
# once you have a real compiled graph.
|
|
40
|
+
# ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class StubBrain(BrainProtocol):
|
|
45
|
+
"""Tiny stub that satisfies BrainProtocol without a real connectome."""
|
|
46
|
+
|
|
47
|
+
n_neurons: int = 256
|
|
48
|
+
seed: int = 42
|
|
49
|
+
|
|
50
|
+
def __post_init__(self) -> None:
|
|
51
|
+
self.n: int = int(self.n_neurons)
|
|
52
|
+
self._rng = np.random.default_rng(self.seed)
|
|
53
|
+
self.populations: dict[str, np.ndarray] = {}
|
|
54
|
+
# Carve the neuron range into contiguous slices, one per population.
|
|
55
|
+
cursor = 0
|
|
56
|
+
for name in REQUIRED_POPULATIONS:
|
|
57
|
+
size = max(8, self.n // 32)
|
|
58
|
+
if cursor + size > self.n:
|
|
59
|
+
size = max(1, self.n - cursor)
|
|
60
|
+
self.populations[name] = np.arange(cursor, cursor + size, dtype=np.int32)
|
|
61
|
+
cursor += size
|
|
62
|
+
if cursor >= self.n:
|
|
63
|
+
cursor = self.n - 1
|
|
64
|
+
|
|
65
|
+
def observe(self, stimulus: Stimulus, neural_ms: float) -> ObservationResult:
|
|
66
|
+
"""Fake 'spike' counts proportional to the stimulus drive."""
|
|
67
|
+
r16 = float(np.mean(stimulus.r16)) if stimulus.r16.size else 0.0
|
|
68
|
+
r8 = float(np.mean(stimulus.r8)) if stimulus.r8.size else 0.0
|
|
69
|
+
drive = 0.5 * (r16 + r8)
|
|
70
|
+
seconds = max(neural_ms, 1e-9) / 1000.0
|
|
71
|
+
lam = (2.0 + 25.0 * drive) * seconds
|
|
72
|
+
counts = self._rng.poisson(lam, size=self.n).astype(np.int32)
|
|
73
|
+
return ObservationResult(
|
|
74
|
+
counts=counts,
|
|
75
|
+
neural_ms=float(neural_ms),
|
|
76
|
+
sim_ms=0.0,
|
|
77
|
+
compute_seconds=0.0,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def checkpoint(self, path: str) -> None:
|
|
81
|
+
# Stub: nothing to do
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
def restore(self, path: str) -> None:
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
def provenance(self) -> dict[str, Any]:
|
|
88
|
+
return {"stub": True, "n": self.n}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ---------------------------------------------------------------------------
|
|
92
|
+
# Build a synthetic SensorFrame (past-only, frozen).
|
|
93
|
+
# ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def make_sensor_frame() -> SensorFrame:
|
|
97
|
+
IST = timezone(timedelta(hours=5, minutes=30))
|
|
98
|
+
bars = tuple(
|
|
99
|
+
Bar( # type: ignore[name-defined]
|
|
100
|
+
timestamp=datetime(2024, 1, 15, 9, 25 + i, tzinfo=IST),
|
|
101
|
+
open=21600 + i * 5,
|
|
102
|
+
high=21650 + i * 5,
|
|
103
|
+
low=21580 + i * 5,
|
|
104
|
+
close=21630 + i * 5,
|
|
105
|
+
volume=1000 + i * 50,
|
|
106
|
+
)
|
|
107
|
+
for i in range(3)
|
|
108
|
+
)
|
|
109
|
+
return SensorFrame(
|
|
110
|
+
timestamp=datetime(2024, 1, 15, 9, 30, tzinfo=IST),
|
|
111
|
+
index_bars=bars,
|
|
112
|
+
vix=14.2,
|
|
113
|
+
vix_bars=bars,
|
|
114
|
+
straddle_premium=120.5,
|
|
115
|
+
entry_credit=None,
|
|
116
|
+
days_to_expiry=4.0,
|
|
117
|
+
minutes_since_open=10,
|
|
118
|
+
position_lots=0,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# Need Bar from interfaces to build the sensor frame
|
|
123
|
+
from flybrainer.interfaces import Bar # noqa: E402
|
|
124
|
+
|
|
125
|
+
# ---------------------------------------------------------------------------
|
|
126
|
+
# Pipeline
|
|
127
|
+
# ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def main() -> Prediction:
|
|
131
|
+
# ---- 1. Set up the brain ----------------------------------------------
|
|
132
|
+
brain = StubBrain(n_neurons=256)
|
|
133
|
+
|
|
134
|
+
# ---- 2. Encode the market snapshot into photoreceptor currents --------
|
|
135
|
+
encoder = make_encoder("bars")
|
|
136
|
+
sf = make_sensor_frame()
|
|
137
|
+
stimulus = encoder.encode(sf, brain)
|
|
138
|
+
print(f"Stimulus: r16={stimulus.r16.shape} r8={stimulus.r8.shape}")
|
|
139
|
+
|
|
140
|
+
# ---- 3. Run 200 ms of neural dynamics --------------------------------
|
|
141
|
+
result = brain.observe(stimulus, neural_ms=200.0)
|
|
142
|
+
total_spikes = int(result.counts.sum())
|
|
143
|
+
print(f"Observed: {total_spikes} spikes across {brain.n} neurons "
|
|
144
|
+
f"in {result.neural_ms:.0f} ms")
|
|
145
|
+
|
|
146
|
+
# ---- 4. Decode into a Decision ---------------------------------------
|
|
147
|
+
decoder = FixedDecoder(neural_ms=200.0, threshold_hz=2.0)
|
|
148
|
+
pred = decoder.predict(result.counts, brain, sf)
|
|
149
|
+
print(f"Prediction: decision={pred.decision.value} "
|
|
150
|
+
f"roi={pred.realized_over_implied:.3f} "
|
|
151
|
+
f"confidence={pred.confidence:.3f}")
|
|
152
|
+
|
|
153
|
+
# Sanity check on the protocol types
|
|
154
|
+
assert isinstance(pred, Prediction)
|
|
155
|
+
assert pred.decision in {Decision.ENTER, Decision.EXIT, Decision.HOLD}
|
|
156
|
+
|
|
157
|
+
return pred
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if __name__ == "__main__":
|
|
161
|
+
main()
|