Instructions to use percepta-ai/spotlight-vm with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use percepta-ai/spotlight-vm with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="percepta-ai/spotlight-vm", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("percepta-ai/spotlight-vm", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use percepta-ai/spotlight-vm with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "percepta-ai/spotlight-vm" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "percepta-ai/spotlight-vm", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/percepta-ai/spotlight-vm
- SGLang
How to use percepta-ai/spotlight-vm with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "percepta-ai/spotlight-vm" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "percepta-ai/spotlight-vm", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "percepta-ai/spotlight-vm" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "percepta-ai/spotlight-vm", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use percepta-ai/spotlight-vm with Docker Model Runner:
docker model run hf.co/percepta-ai/spotlight-vm
Spotlight VM: the model and the programs from the post
Browse files- LICENSE.md +248 -0
- README.md +39 -0
- config.json +1 -0
- examples/euler1.py +12 -0
- examples/euler2.py +24 -0
- examples/euler3.py +11 -0
- examples/memory_loader.py +4 -0
- examples/mnist_digit.py +4 -0
- examples/tax_brackets/1_create.py +10 -0
- examples/tax_brackets/2_use.py +3 -0
- examples/tax_brackets/3_update.py +10 -0
- examples/tax_brackets/4_use_again.py +3 -0
- intelligence/config.json +1 -0
- intelligence/model.safetensors +3 -0
- intelligence/vocab.json +1 -0
- memory/core.safetensors +3 -0
- memory/mnist.safetensors +3 -0
- memory/sortedcontainers.safetensors +3 -0
- spotlight.c +304 -0
- spotlight.py +76 -0
LICENSE.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# License
|
| 2 |
+
|
| 3 |
+
Spotlight VM is licensed under the Apache License, Version 2.0.
|
| 4 |
+
|
| 5 |
+
```text
|
| 6 |
+
Apache License
|
| 7 |
+
Version 2.0, January 2004
|
| 8 |
+
http://www.apache.org/licenses/
|
| 9 |
+
|
| 10 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 11 |
+
|
| 12 |
+
1. Definitions.
|
| 13 |
+
|
| 14 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 15 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 16 |
+
|
| 17 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 18 |
+
the copyright owner that is granting the License.
|
| 19 |
+
|
| 20 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 21 |
+
other entities that control, are controlled by, or are under common
|
| 22 |
+
control with that entity. For the purposes of this definition,
|
| 23 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 24 |
+
direction or management of such entity, whether by contract or
|
| 25 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 26 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 27 |
+
|
| 28 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 29 |
+
exercising permissions granted by this License.
|
| 30 |
+
|
| 31 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 32 |
+
including but not limited to software source code, documentation
|
| 33 |
+
source, and configuration files.
|
| 34 |
+
|
| 35 |
+
"Object" form shall mean any form resulting from mechanical
|
| 36 |
+
transformation or translation of a Source form, including but
|
| 37 |
+
not limited to compiled object code, generated documentation,
|
| 38 |
+
and conversions to other media types.
|
| 39 |
+
|
| 40 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 41 |
+
Object form, made available under the License, as indicated by a
|
| 42 |
+
copyright notice that is included in or attached to the work
|
| 43 |
+
(an example is provided in the Appendix below).
|
| 44 |
+
|
| 45 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 46 |
+
form, that is based on (or derived from) the Work and for which the
|
| 47 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 48 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 49 |
+
of this License, Derivative Works shall not include works that remain
|
| 50 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 51 |
+
the Work and Derivative Works thereof.
|
| 52 |
+
|
| 53 |
+
"Contribution" shall mean any work of authorship, including
|
| 54 |
+
the original version of the Work and any modifications or additions
|
| 55 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 56 |
+
submitted to the Licensor for inclusion in the Work by the copyright owner
|
| 57 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 58 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 59 |
+
means any form of electronic, verbal, or written communication sent
|
| 60 |
+
to the Licensor or its representatives, including but not limited to
|
| 61 |
+
communication on electronic mailing lists, source code control systems,
|
| 62 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 63 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 64 |
+
excluding communication that is conspicuously marked or otherwise
|
| 65 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 66 |
+
|
| 67 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 68 |
+
on behalf of whom a Contribution has been received by the Licensor and
|
| 69 |
+
subsequently incorporated within the Work.
|
| 70 |
+
|
| 71 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 72 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 73 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 74 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 75 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 76 |
+
Work and such Derivative Works in Source or Object form.
|
| 77 |
+
|
| 78 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 79 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 80 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 81 |
+
(except as stated in this section) patent license to make, have made,
|
| 82 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 83 |
+
where such license applies only to those patent claims licensable
|
| 84 |
+
by such Contributor that are necessarily infringed by their
|
| 85 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 86 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 87 |
+
institute patent litigation against any entity (including a
|
| 88 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 89 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 90 |
+
or contributory patent infringement, then any patent licenses
|
| 91 |
+
granted to You under this License for that Work shall terminate
|
| 92 |
+
as of the date such litigation is filed.
|
| 93 |
+
|
| 94 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 95 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 96 |
+
modifications, and in Source or Object form, provided that You
|
| 97 |
+
meet the following conditions:
|
| 98 |
+
|
| 99 |
+
(a) You must give any other recipients of the Work or
|
| 100 |
+
Derivative Works a copy of this License; and
|
| 101 |
+
|
| 102 |
+
(b) You must cause any modified files to carry prominent notices
|
| 103 |
+
stating that You changed the files; and
|
| 104 |
+
|
| 105 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 106 |
+
that You distribute, all copyright, patent, trademark, and
|
| 107 |
+
attribution notices from the Source form of the Work,
|
| 108 |
+
excluding those notices that do not pertain to any part of
|
| 109 |
+
the Derivative Works; and
|
| 110 |
+
|
| 111 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 112 |
+
distribution, then any Derivative Works that You distribute must
|
| 113 |
+
include a readable copy of the attribution notices contained
|
| 114 |
+
within such NOTICE file, excluding any notices that do not
|
| 115 |
+
pertain to any part of the Derivative Works, in at least one
|
| 116 |
+
of the following places: within a NOTICE text file distributed
|
| 117 |
+
as part of the Derivative Works; within the Source form or
|
| 118 |
+
documentation, if provided along with the Derivative Works; or,
|
| 119 |
+
within a display generated by the Derivative Works, if and
|
| 120 |
+
wherever such third-party notices normally appear. The contents
|
| 121 |
+
of the NOTICE file are for informational purposes only and
|
| 122 |
+
do not modify the License. You may add Your own attribution
|
| 123 |
+
notices within Derivative Works that You distribute, alongside
|
| 124 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 125 |
+
that such additional attribution notices cannot be construed
|
| 126 |
+
as modifying the License.
|
| 127 |
+
|
| 128 |
+
You may add Your own copyright statement to Your modifications and
|
| 129 |
+
may provide additional or different license terms and conditions
|
| 130 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 131 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 132 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 133 |
+
the conditions stated in this License.
|
| 134 |
+
|
| 135 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 136 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 137 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 138 |
+
this License, without any additional terms or conditions.
|
| 139 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 140 |
+
the terms of any separate license agreement you may have executed
|
| 141 |
+
with Licensor regarding such Contributions.
|
| 142 |
+
|
| 143 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 144 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 145 |
+
except as required for reasonable and customary use in describing the
|
| 146 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 147 |
+
|
| 148 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 149 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 150 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 151 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 152 |
+
implied, including, without limitation, any warranties or conditions
|
| 153 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 154 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 155 |
+
appropriateness of using or redistributing the Work and assume any
|
| 156 |
+
risks associated with Your exercise of permissions under this License.
|
| 157 |
+
|
| 158 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 159 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 160 |
+
unless required by applicable law (such as deliberate and grossly
|
| 161 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 162 |
+
liable to You for damages, including any direct, indirect, special,
|
| 163 |
+
incidental, or consequential damages of any character arising as a
|
| 164 |
+
result of this License or out of the use or inability to use the
|
| 165 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 166 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 167 |
+
other commercial damages or losses), even if such Contributor
|
| 168 |
+
has been advised of the possibility of such damages.
|
| 169 |
+
|
| 170 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 171 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 172 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 173 |
+
or other liability obligations and/or rights consistent with this
|
| 174 |
+
License. However, in accepting such obligations, You may act only
|
| 175 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 176 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 177 |
+
defend, and hold each Contributor harmless for any liability
|
| 178 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 179 |
+
of your accepting any such warranty or additional liability.
|
| 180 |
+
|
| 181 |
+
END OF TERMS AND CONDITIONS
|
| 182 |
+
|
| 183 |
+
Copyright 2026 Percepta
|
| 184 |
+
|
| 185 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 186 |
+
you may not use this file except in compliance with the License.
|
| 187 |
+
You may obtain a copy of the License at
|
| 188 |
+
|
| 189 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 190 |
+
|
| 191 |
+
Unless required by applicable law or agreed to in writing, software
|
| 192 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 193 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 194 |
+
See the License for the specific language governing permissions and
|
| 195 |
+
limitations under the License.
|
| 196 |
+
```
|
| 197 |
+
|
| 198 |
+
## Third-party components
|
| 199 |
+
|
| 200 |
+
These parts remain under their own licenses.
|
| 201 |
+
|
| 202 |
+
### MicroPython v1.28.0 (`memory/core.safetensors`)
|
| 203 |
+
|
| 204 |
+
```
|
| 205 |
+
The MIT License (MIT)
|
| 206 |
+
|
| 207 |
+
Copyright (c) 2013-2026 Damien P. George
|
| 208 |
+
|
| 209 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 210 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 211 |
+
in the Software without restriction, including without limitation the rights
|
| 212 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 213 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 214 |
+
furnished to do so, subject to the following conditions:
|
| 215 |
+
|
| 216 |
+
The above copyright notice and this permission notice shall be included in
|
| 217 |
+
all copies or substantial portions of the Software.
|
| 218 |
+
|
| 219 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 220 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 221 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 222 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 223 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 224 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 225 |
+
THE SOFTWARE.
|
| 226 |
+
```
|
| 227 |
+
|
| 228 |
+
### sortedcontainers 2.4.0 (`memory/sortedcontainers.safetensors`)
|
| 229 |
+
|
| 230 |
+
```
|
| 231 |
+
Copyright 2014-2019 Grant Jenks
|
| 232 |
+
|
| 233 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 234 |
+
you may not use this file except in compliance with the License.
|
| 235 |
+
You may obtain a copy of the License at
|
| 236 |
+
|
| 237 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 238 |
+
|
| 239 |
+
Unless required by applicable law or agreed to in writing, software
|
| 240 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 241 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 242 |
+
See the License for the specific language governing permissions and
|
| 243 |
+
limitations under the License.
|
| 244 |
+
```
|
| 245 |
+
|
| 246 |
+
### MNIST (`memory/mnist.safetensors`)
|
| 247 |
+
|
| 248 |
+
The network was trained on the MNIST database of handwritten digits by Yann LeCun, Corinna Cortes and Christopher J. C. Burges.
|
README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
library_name: transformers
|
| 4 |
+
---
|
| 5 |
+
|
| 6 |
+
# Spotlight VM
|
| 7 |
+
|
| 8 |
+
A small, hand-constructed [Spotlight](https://percepta.ai/blog/spotlight-memory) transformer that runs Python:
|
| 9 |
+
the examples from [Growing Intelligence Beyond the Weights](https://percepta.ai/blog/growing-intelligence-beyond-the-weights).
|
| 10 |
+
`intelligence/` holds the weights (under 100K parameters), which never change. `memory/` holds MicroPython
|
| 11 |
+
v1.28.0 (`core.safetensors`) and two packages.
|
| 12 |
+
|
| 13 |
+
```sh
|
| 14 |
+
cc -O3 -std=c99 spotlight.c -lm -o spotlight
|
| 15 |
+
./spotlight < examples/euler1.py # 233168; -v also prints every generated token
|
| 16 |
+
cat examples/tax_brackets/*.py | ./spotlight
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
```python
|
| 20 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 21 |
+
|
| 22 |
+
model = AutoModelForCausalLM.from_pretrained("percepta-ai/spotlight-vm", trust_remote_code=True)
|
| 23 |
+
tokenizer = AutoTokenizer.from_pretrained("percepta-ai/spotlight-vm", trust_remote_code=True)
|
| 24 |
+
output = model.generate(**tokenizer("print(1 + 2)\n", return_tensors="pt"))
|
| 25 |
+
print(tokenizer.decode(output[0])) # 3
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
In a local copy of this folder, `from_pretrained(".")` works the same way.
|
| 29 |
+
|
| 30 |
+
| Example | In the post | Output |
|
| 31 |
+
|---|---|---|
|
| 32 |
+
| `examples/euler1.py`, `euler2.py`, `euler3.py` | Project Euler problems 1 to 3 | `233168`, `4613732`, `6857` |
|
| 33 |
+
| `examples/memory_loader.py` | Loading memory selectively | `4` |
|
| 34 |
+
| `examples/tax_brackets/` | Acquiring and updating knowledge | `$17400`, then `$16914` |
|
| 35 |
+
| `examples/mnist_digit.py` | Growing capabilities and intelligence | `7` |
|
| 36 |
+
|
| 37 |
+
It runs on one CPU core at about 120K tokens per second: minutes for the Project Euler examples,
|
| 38 |
+
about half an hour for MNIST (245M tokens). This MicroPython build has no floating point. License: Apache 2.0,
|
| 39 |
+
see [LICENSE.md](LICENSE.md).
|
config.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"model_type":"spotlight","architectures":["SpotlightForCausalLM"],"auto_map":{"AutoConfig":"spotlight.SpotlightConfig","AutoModelForCausalLM":"spotlight.SpotlightForCausalLM","AutoTokenizer":["spotlight.SpotlightTokenizer",null]}}
|
examples/euler1.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# https://projecteuler.net/problem=1
|
| 2 |
+
# Sum the multiples of 3 or 5 below 1000.
|
| 3 |
+
# Memory allocation within the program translates to Spotlight Memory
|
| 4 |
+
def euler1(limit=1000):
|
| 5 |
+
marked = bytearray(limit)
|
| 6 |
+
for n in range(0, limit, 3):
|
| 7 |
+
marked[n] = 1
|
| 8 |
+
for n in range(0, limit, 5):
|
| 9 |
+
marked[n] = 1
|
| 10 |
+
return sum(n for n in range(limit) if marked[n])
|
| 11 |
+
|
| 12 |
+
print(euler1())
|
examples/euler2.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# https://projecteuler.net/problem=2
|
| 2 |
+
# Sum the even Fibonacci terms not exceeding 4,000,000.
|
| 3 |
+
# Recursive function calling, generators and Python idioms
|
| 4 |
+
def fib():
|
| 5 |
+
a, b = 1, 2
|
| 6 |
+
while True:
|
| 7 |
+
yield a
|
| 8 |
+
a, b = b, a + b
|
| 9 |
+
|
| 10 |
+
def evens(it):
|
| 11 |
+
for x in it:
|
| 12 |
+
if x % 2 == 0:
|
| 13 |
+
yield x
|
| 14 |
+
|
| 15 |
+
def upto(limit, it):
|
| 16 |
+
for x in it:
|
| 17 |
+
if x > limit:
|
| 18 |
+
return
|
| 19 |
+
yield x
|
| 20 |
+
|
| 21 |
+
def euler2(limit=4000000):
|
| 22 |
+
return sum(evens(upto(limit, fib())))
|
| 23 |
+
|
| 24 |
+
print(euler2())
|
examples/euler3.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# https://projecteuler.net/problem=3
|
| 2 |
+
# Find the largest prime factor of 600851475143.
|
| 3 |
+
def euler3(n=600851475143):
|
| 4 |
+
i = 2
|
| 5 |
+
while i * i <= n:
|
| 6 |
+
while n % i == 0:
|
| 7 |
+
n //= i
|
| 8 |
+
i += 1
|
| 9 |
+
return n
|
| 10 |
+
|
| 11 |
+
print(euler3())
|
examples/memory_loader.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sortedcontainers import SortedList
|
| 2 |
+
from math import log
|
| 3 |
+
values = SortedList([9, 4, 16])
|
| 4 |
+
print(log(values.pop(), 2))
|
examples/mnist_digit.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# The first MNIST test image (a 7), classified by the 784-80-10 network in memory/mnist.safetensors.
|
| 2 |
+
import mnist
|
| 3 |
+
x=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00T\xb9\x9f\x97<$\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\xfe\xfe\xfe\xfe\xf1\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xaa4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00CrHr\xa3\xe3\xfe\xe1\xfe\xfe\xfe\xfa\xe5\xfe\xfe\x8c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11B\x0eCCC;\x15\xec\xfej\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00S\xfd\xd1\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\xe9\xffS\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x81\xfe\xee,\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;\xf9\xfe>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\xfe\xbb\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\t\xcd\xf8:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00~\xfe\xb6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00K\xfb\xf09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\xdd\xfe\xa6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\xcb\xfe\xdb#\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&\xfe\xfeM\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\xe0\xfes\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\xfe\xfe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00=\xf2\xfe\xfe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00y\xfe\xfe\xdb(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00y\xfe\xcf\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
| 4 |
+
print(mnist.predict(x))
|
examples/tax_brackets/1_create.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
brackets = [0, 11_000, 44_725, 95_375,
|
| 3 |
+
182_100, 231_250, 578_125]
|
| 4 |
+
increments = [10, 2, 10, 2, 8, 3, 2]
|
| 5 |
+
sys.modules["tax_brackets"] = type("tax_brackets", (), {
|
| 6 |
+
"owed_tax": lambda income: sum(
|
| 7 |
+
rate * max(0, income - b)
|
| 8 |
+
for b, rate in zip(brackets, increments)
|
| 9 |
+
) // 100})
|
| 10 |
+
print("Tax brackets installed.")
|
examples/tax_brackets/2_use.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tax_brackets
|
| 2 |
+
income = 100_000
|
| 3 |
+
print(f"${tax_brackets.owed_tax(income)}")
|
examples/tax_brackets/3_update.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
brackets = [0, 11_925, 48_475, 103_350,
|
| 3 |
+
197_300, 250_525, 626_350]
|
| 4 |
+
increments = [10, 2, 10, 2, 8, 3, 2]
|
| 5 |
+
sys.modules["tax_brackets"] = type("tax_brackets", (), {
|
| 6 |
+
"owed_tax": lambda income: sum(
|
| 7 |
+
rate * max(0, income - b)
|
| 8 |
+
for b, rate in zip(brackets, increments)
|
| 9 |
+
) // 100})
|
| 10 |
+
print("Tax brackets updated.")
|
examples/tax_brackets/4_use_again.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tax_brackets
|
| 2 |
+
income = 100_000
|
| 3 |
+
print(f"${tax_brackets.owed_tax(income)}")
|
intelligence/config.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"d_model":16,"n_heads":8,"n_layers":8,"vocab_size":922,"proj_out":64,"stop_id":635,"n_out":786,"d_ffn":48,"head_rank":3,"d_rest":0,"d_v":2,"alpha_rows":1,"spacing":2.0,"memory_layer":4,"memory_head":4,"input_base":4475392,"registry_base":4587520,"registry_root_word":4475376,"memory_pages_word":4522008,"image_pages":70}
|
intelligence/model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e09af3088c603c3641923f8c1554fa6657573570b5eb251ed8c73c5ca543df7a
|
| 3 |
+
size 378560
|
intelligence/vocab.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
["!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "00", "00'", "01", "01'", "02", "02'", "03", "03'", "04", "04'", "05", "05'", "06", "06'", "07", "07'", "08", "08'", "09", "09'", "0a", "0a'", "0b", "0b'", "0c", "0c'", "0d", "0d'", "0e", "0e'", "0f", "0f'", "1", "10", "10'", "11", "11'", "12", "12'", "13", "13'", "14", "14'", "15", "15'", "16", "16'", "17", "17'", "18", "18'", "19", "19'", "1a", "1a'", "1b", "1b'", "1c", "1c'", "1d", "1d'", "1e", "1e'", "1f", "1f'", "2", "20", "20'", "21", "21'", "22", "22'", "23", "23'", "24", "24'", "25", "25'", "26", "26'", "27", "27'", "28", "28'", "29", "29'", "2a", "2a'", "2b", "2b'", "2c", "2c'", "2d", "2d'", "2e", "2e'", "2f", "2f'", "3", "30", "30'", "31", "31'", "32", "32'", "33", "33'", "34", "34'", "35", "35'", "36", "36'", "37", "37'", "38", "38'", "39", "39'", "3a", "3a'", "3b", "3b'", "3c", "3c'", "3d", "3d'", "3e", "3e'", "3f", "3f'", "4", "40", "40'", "41", "41'", "42", "42'", "43", "43'", "44", "44'", "45", "45'", "46", "46'", "47", "47'", "48", "48'", "49", "49'", "4a", "4a'", "4b", "4b'", "4c", "4c'", "4d", "4d'", "4e", "4e'", "4f", "4f'", "5", "50", "50'", "51", "51'", "52", "52'", "53", "53'", "54", "54'", "55", "55'", "56", "56'", "57", "57'", "58", "58'", "59", "59'", "5a", "5a'", "5b", "5b'", "5c", "5c'", "5d", "5d'", "5e", "5e'", "5f", "5f'", "6", "60", "60'", "61", "61'", "62", "62'", "63", "63'", "64", "64'", "65", "65'", "66", "66'", "67", "67'", "68", "68'", "69", "69'", "6a", "6a'", "6b", "6b'", "6c", "6c'", "6d", "6d'", "6e", "6e'", "6f", "6f'", "7", "70", "70'", "71", "71'", "72", "72'", "73", "73'", "74", "74'", "75", "75'", "76", "76'", "77", "77'", "78", "78'", "79", "79'", "7a", "7a'", "7b", "7b'", "7c", "7c'", "7d", "7d'", "7e", "7e'", "7f", "7f'", "8", "80", "80'", "81", "81'", "82", "82'", "83", "83'", "84", "84'", "85", "85'", "86", "86'", "87", "87'", "88", "88'", "89", "89'", "8a", "8a'", "8b", "8b'", "8c", "8c'", "8d", "8d'", "8e", "8e'", "8f", "8f'", "9", "90", "90'", "91", "91'", "92", "92'", "93", "93'", "94", "94'", "95", "95'", "96", "96'", "97", "97'", "98", "98'", "99", "99'", "9a", "9a'", "9b", "9b'", "9c", "9c'", "9d", "9d'", "9e", "9e'", "9f", "9f'", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "a0", "a0'", "a1", "a1'", "a2", "a2'", "a3", "a3'", "a4", "a4'", "a5", "a5'", "a6", "a6'", "a7", "a7'", "a8", "a8'", "a9", "a9'", "aa", "aa'", "ab", "ab'", "ac", "ac'", "ad", "ad'", "ae", "ae'", "af", "af'", "ascii(00)", "ascii(01)", "ascii(02)", "ascii(03)", "ascii(04)", "ascii(05)", "ascii(06)", "ascii(07)", "ascii(08)", "ascii(09)", "ascii(0a)", "ascii(0b)", "ascii(0c)", "ascii(0d)", "ascii(0e)", "ascii(0f)", "ascii(10)", "ascii(11)", "ascii(12)", "ascii(13)", "ascii(14)", "ascii(15)", "ascii(16)", "ascii(17)", "ascii(18)", "ascii(19)", "ascii(1a)", "ascii(1b)", "ascii(1c)", "ascii(1d)", "ascii(1e)", "ascii(1f)", "ascii(20)", "ascii(7f)", "b", "b0", "b0'", "b1", "b1'", "b2", "b2'", "b3", "b3'", "b4", "b4'", "b5", "b5'", "b6", "b6'", "b7", "b7'", "b8", "b8'", "b9", "b9'", "ba", "ba'", "bb", "bb'", "bc", "bc'", "bd", "bd'", "be", "be'", "bf", "bf'", "branch_taken", "c", "c0", "c0'", "c1", "c1'", "c2", "c2'", "c3", "c3'", "c4", "c4'", "c5", "c5'", "c6", "c6'", "c7", "c7'", "c8", "c8'", "c9", "c9'", "ca", "ca'", "call_commit", "cb", "cb'", "cc", "cc'", "cd", "cd'", "ce", "ce'", "cf", "cf'", "commit(+0,sts=0,bt=0)", "commit(+0,sts=0,bt=1)", "commit(+0,sts=1,bt=0)", "commit(+0,sts=1,bt=1)", "commit(+1,sts=1,bt=0)", "commit(+1,sts=1,bt=1)", "commit(-1,sts=0,bt=0)", "commit(-1,sts=0,bt=1)", "commit(-1,sts=1,bt=0)", "commit(-1,sts=1,bt=1)", "commit(-2,sts=0,bt=0)", "commit(-2,sts=0,bt=1)", "commit(-2,sts=1,bt=0)", "commit(-2,sts=1,bt=1)", "d", "d0", "d0'", "d1", "d1'", "d2", "d2'", "d3", "d3'", "d4", "d4'", "d5", "d5'", "d6", "d6'", "d7", "d7'", "d8", "d8'", "d9", "d9'", "da", "da'", "db", "db'", "dc", "dc'", "dd", "dd'", "de", "de'", "df", "df'", "e", "e0", "e0'", "e1", "e1'", "e2", "e2'", "e3", "e3'", "e4", "e4'", "e5", "e5'", "e6", "e6'", "e7", "e7'", "e8", "e8'", "e9", "e9'", "ea", "ea'", "eb", "eb'", "ec", "ec'", "ed", "ed'", "ee", "ee'", "ef", "ef'", "execute", "f", "f0", "f0'", "f1", "f1'", "f2", "f2'", "f3", "f3'", "f4", "f4'", "f5", "f5'", "f6", "f6'", "f7", "f7'", "f8", "f8'", "f9", "f9'", "fa", "fa'", "fb", "fb'", "fc", "fc'", "fd", "fd'", "fe", "fe'", "ff", "ff'", "g", "h", "halt", "i", "i32.and", "i32.extend16_s", "i32.extend8_s", "i32.or", "i32.xor", "j", "k", "l", "m", "n", "o", "out(!)", "out(\")", "out(#)", "out($)", "out(%)", "out(&)", "out(')", "out(()", "out())", "out(*)", "out(+)", "out(,)", "out(-)", "out(.)", "out(/)", "out(0)", "out(00)", "out(01)", "out(02)", "out(03)", "out(04)", "out(05)", "out(06)", "out(07)", "out(08)", "out(09)", "out(0a)", "out(0b)", "out(0c)", "out(0d)", "out(0e)", "out(0f)", "out(1)", "out(10)", "out(11)", "out(12)", "out(13)", "out(14)", "out(15)", "out(16)", "out(17)", "out(18)", "out(19)", "out(1a)", "out(1b)", "out(1c)", "out(1d)", "out(1e)", "out(1f)", "out(2)", "out(20)", "out(3)", "out(4)", "out(5)", "out(6)", "out(7)", "out(7f)", "out(8)", "out(80)", "out(81)", "out(82)", "out(83)", "out(84)", "out(85)", "out(86)", "out(87)", "out(88)", "out(89)", "out(8a)", "out(8b)", "out(8c)", "out(8d)", "out(8e)", "out(8f)", "out(9)", "out(90)", "out(91)", "out(92)", "out(93)", "out(94)", "out(95)", "out(96)", "out(97)", "out(98)", "out(99)", "out(9a)", "out(9b)", "out(9c)", "out(9d)", "out(9e)", "out(9f)", "out(:)", "out(;)", "out(<)", "out(=)", "out(>)", "out(?)", "out(@)", "out(A)", "out(B)", "out(C)", "out(D)", "out(E)", "out(F)", "out(G)", "out(H)", "out(I)", "out(J)", "out(K)", "out(L)", "out(M)", "out(N)", "out(O)", "out(P)", "out(Q)", "out(R)", "out(S)", "out(T)", "out(U)", "out(V)", "out(W)", "out(X)", "out(Y)", "out(Z)", "out([)", "out(\\)", "out(])", "out(^)", "out(_)", "out(`)", "out(a)", "out(a0)", "out(a1)", "out(a2)", "out(a3)", "out(a4)", "out(a5)", "out(a6)", "out(a7)", "out(a8)", "out(a9)", "out(aa)", "out(ab)", "out(ac)", "out(ad)", "out(ae)", "out(af)", "out(b)", "out(b0)", "out(b1)", "out(b2)", "out(b3)", "out(b4)", "out(b5)", "out(b6)", "out(b7)", "out(b8)", "out(b9)", "out(ba)", "out(bb)", "out(bc)", "out(bd)", "out(be)", "out(bf)", "out(c)", "out(c0)", "out(c1)", "out(c2)", "out(c3)", "out(c4)", "out(c5)", "out(c6)", "out(c7)", "out(c8)", "out(c9)", "out(ca)", "out(cb)", "out(cc)", "out(cd)", "out(ce)", "out(cf)", "out(d)", "out(d0)", "out(d1)", "out(d2)", "out(d3)", "out(d4)", "out(d5)", "out(d6)", "out(d7)", "out(d8)", "out(d9)", "out(da)", "out(db)", "out(dc)", "out(dd)", "out(de)", "out(df)", "out(e)", "out(e0)", "out(e1)", "out(e2)", "out(e3)", "out(e4)", "out(e5)", "out(e6)", "out(e7)", "out(e8)", "out(e9)", "out(ea)", "out(eb)", "out(ec)", "out(ed)", "out(ee)", "out(ef)", "out(f)", "out(f0)", "out(f1)", "out(f2)", "out(f3)", "out(f4)", "out(f5)", "out(f6)", "out(f7)", "out(f8)", "out(f9)", "out(fa)", "out(fb)", "out(fc)", "out(fd)", "out(fe)", "out(ff)", "out(g)", "out(h)", "out(i)", "out(j)", "out(k)", "out(l)", "out(m)", "out(n)", "out(o)", "out(p)", "out(q)", "out(r)", "out(s)", "out(t)", "out(u)", "out(v)", "out(w)", "out(x)", "out(y)", "out(z)", "out({)", "out(|)", "out(})", "out(~)", "p", "page", "q", "r", "return_commit", "s", "t", "u", "unpage", "v", "w", "x", "y", "z", "{", "|", "}", "~"]
|
memory/core.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:79ed099f59fe3c62cc70595f9b09c4655b9cf5ed2be22042cc59de61afe156b3
|
| 3 |
+
size 23989346
|
memory/mnist.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:45b8d1aafa55fa579c7995a74fa978cacfca8a3abbb6fe372719723fa7c223c4
|
| 3 |
+
size 64556
|
memory/sortedcontainers.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:5b173f11870edf27424391bb4dc6f3f9c65761f03d81bb58f43e4052664b887b
|
| 3 |
+
size 26921
|
spotlight.c
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* spotlight.c -- runs a Python program on the Spotlight VM model: ./spotlight [-v] < program.py
|
| 2 |
+
* Run it in the model folder; it loads intelligence/ and every package in memory/, and -v also
|
| 3 |
+
* prints each generated token. Built with -shared -DSPOTLIGHT_LIBRARY, spotlight.py loads it. */
|
| 4 |
+
#define _POSIX_C_SOURCE 200809L
|
| 5 |
+
#include <dirent.h>
|
| 6 |
+
#include <math.h>
|
| 7 |
+
#include <stdint.h>
|
| 8 |
+
#include <stdio.h>
|
| 9 |
+
#include <stdlib.h>
|
| 10 |
+
#include <string.h>
|
| 11 |
+
|
| 12 |
+
typedef float real_t;
|
| 13 |
+
static void die(const char *what) { fprintf(stderr, "spotlight: %s\n", what); exit(1); }
|
| 14 |
+
static void *alloc(size_t n) { void *p = calloc(n ? n : 1, 1); if (!p) die("out of memory"); return p; }
|
| 15 |
+
static char folder[4096] = ".";
|
| 16 |
+
static const char *file(const char *name) { static char path[4200]; snprintf(path, sizeof path, "%s/%s", folder, name); return path; }
|
| 17 |
+
|
| 18 |
+
static char *slurp(const char *path, size_t *size) {
|
| 19 |
+
FILE *f = path ? fopen(path, "rb") : stdin;
|
| 20 |
+
if (!f) die(path);
|
| 21 |
+
size_t cap = 1 << 16, n = 0, got; char *buf = alloc(cap);
|
| 22 |
+
while ((got = fread(buf + n, 1, cap - n - 1, f)) > 0) if ((n += got) == cap - 1) buf = realloc(buf, cap *= 2);
|
| 23 |
+
if (path) fclose(f);
|
| 24 |
+
buf[n] = 0; if (size) *size = n;
|
| 25 |
+
return buf;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
/* ── config.json and safetensors ─────────────────────────────────────── */
|
| 29 |
+
static char *config;
|
| 30 |
+
static double number(const char *key) {
|
| 31 |
+
char pattern[64]; snprintf(pattern, sizeof pattern, "\"%s\":", key);
|
| 32 |
+
const char *p = strstr(config, pattern);
|
| 33 |
+
if (!p) die(key);
|
| 34 |
+
return strtod(p + strlen(pattern), NULL);
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
typedef struct { char dtype[4]; const char *data; int64_t count; } Tensor;
|
| 38 |
+
static int tensor(const char *file_bytes, const char *name, Tensor *out) {
|
| 39 |
+
char pattern[96]; snprintf(pattern, sizeof pattern, "\"%s\":", name);
|
| 40 |
+
uint64_t header; memcpy(&header, file_bytes, 8);
|
| 41 |
+
const char *p = strstr(file_bytes + 8, pattern);
|
| 42 |
+
if (!p || p > file_bytes + 8 + header) return 0;
|
| 43 |
+
const char *dtype = strstr(p, "\"dtype\":\"") + 9; char *offsets = strstr(p, "\"data_offsets\":[") + 16;
|
| 44 |
+
memset(out->dtype, 0, 4); memcpy(out->dtype, dtype, strchr(dtype, '"') - dtype);
|
| 45 |
+
int64_t begin = strtoll(offsets, &offsets, 10), end = strtoll(offsets + 1, NULL, 10);
|
| 46 |
+
int width = out->dtype[1] == '8' ? 1 : out->dtype[1] == '1' ? 2 : out->dtype[1] == '3' ? 4 : 8;
|
| 47 |
+
out->data = file_bytes + 8 + header + begin; out->count = (end - begin) / width;
|
| 48 |
+
return 1;
|
| 49 |
+
}
|
| 50 |
+
static double item(const Tensor *t, int64_t i) {
|
| 51 |
+
double f; int32_t i32; int16_t i16;
|
| 52 |
+
switch (t->dtype[0] == 'F' ? 'F' : t->dtype[1]) {
|
| 53 |
+
case 'F': memcpy(&f, t->data + 8 * i, 8); return f;
|
| 54 |
+
case '3': memcpy(&i32, t->data + 4 * i, 4); return i32;
|
| 55 |
+
case '1': memcpy(&i16, t->data + 2 * i, 2); return i16;
|
| 56 |
+
default: return t->dtype[0] == 'U' ? (double)(uint8_t)t->data[i] : (double)(int8_t)t->data[i];
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
static real_t *load(const char *file_bytes, const char *name, int64_t count) {
|
| 60 |
+
Tensor x; if (!tensor(file_bytes, name, &x) || x.count != count) die(name);
|
| 61 |
+
real_t *out = alloc((size_t)count * sizeof(real_t));
|
| 62 |
+
for (int64_t i = 0; i < count; i++) out[i] = (real_t)item(&x, i);
|
| 63 |
+
return out;
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
/* ── the memory: one hash map from (table, x, y) to a cell's state ──── */
|
| 67 |
+
typedef struct { int64_t x, y; int32_t table, used; real_t s[2]; } Cell;
|
| 68 |
+
static Cell *cells; static size_t cells_cap, cells_n;
|
| 69 |
+
static const real_t ZERO[2] = {0, 0};
|
| 70 |
+
|
| 71 |
+
static size_t cell_slot(int table, int64_t x, int64_t y) {
|
| 72 |
+
uint64_t h = (uint64_t)x * 0x9E3779B97F4A7C15ull ^ (uint64_t)y * 0xC2B2AE3D27D4EB4Full ^ (uint64_t)table * 0x165667B19E3779F9ull;
|
| 73 |
+
size_t i = (size_t)(h ^ h >> 29) & (cells_cap - 1);
|
| 74 |
+
while (cells[i].used && (cells[i].x != x || cells[i].y != y || cells[i].table != table)) i = (i + 1) & (cells_cap - 1);
|
| 75 |
+
return i;
|
| 76 |
+
}
|
| 77 |
+
static real_t *cell_write(int table, int64_t x, int64_t y) {
|
| 78 |
+
if (2 * (cells_n + 1) > cells_cap) {
|
| 79 |
+
Cell *old = cells; size_t n = cells_cap;
|
| 80 |
+
cells_cap = cells_cap ? 2 * cells_cap : 1 << 22; cells = alloc(cells_cap * sizeof(Cell));
|
| 81 |
+
for (size_t i = 0; i < n; i++) if (old[i].used) cells[cell_slot(old[i].table, old[i].x, old[i].y)] = old[i];
|
| 82 |
+
free(old);
|
| 83 |
+
}
|
| 84 |
+
size_t i = cell_slot(table, x, y);
|
| 85 |
+
if (!cells[i].used) { cells[i].x = x; cells[i].y = y; cells[i].table = table; cells[i].used = 1; cells_n++; }
|
| 86 |
+
return cells[i].s;
|
| 87 |
+
}
|
| 88 |
+
static const real_t *cell_read(int table, int64_t x, int64_t y) {
|
| 89 |
+
size_t i = cell_slot(table, x, y);
|
| 90 |
+
return cells[i].used ? cells[i].s : ZERO;
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
/* ── the model ───────────────────────────────────────────────────────── */
|
| 94 |
+
/* The Spotlight kernel: phi_z(x) = b(x - z) / sqrt(sum_j b(x - j)^2) over the three nearest cells,
|
| 95 |
+
* with b(t) = cos^2(pi t / 2) for |t| < 1. */
|
| 96 |
+
static void phi(real_t x, int64_t *z0, real_t w[3]) {
|
| 97 |
+
int64_t c = (int64_t)floor(x + 0.5); real_t norm = 0;
|
| 98 |
+
for (int i = 0; i < 3; i++) {
|
| 99 |
+
real_t t = x - (real_t)(c + i - 1), b = fabsf(t) < 1 ? cosf(3.14159265f * t / 2) : 0;
|
| 100 |
+
w[i] = b * b; norm += w[i] * w[i];
|
| 101 |
+
}
|
| 102 |
+
for (int i = 0; i < 3; i++) w[i] /= sqrtf(norm);
|
| 103 |
+
*z0 = c - 1;
|
| 104 |
+
}
|
| 105 |
+
static real_t sigmoid(real_t z) { return 1 / (1 + expf(-z)); }
|
| 106 |
+
static real_t dot(const real_t *row, const real_t *x, int cols) { /* 8 lanes, so it vectorizes */
|
| 107 |
+
real_t a[8] = {0}, acc; int c = 0;
|
| 108 |
+
for (; c + 8 <= cols; c += 8) for (int k = 0; k < 8; k++) a[k] += row[c + k] * x[c + k];
|
| 109 |
+
acc = ((a[0] + a[1]) + (a[2] + a[3])) + ((a[4] + a[5]) + (a[6] + a[7]));
|
| 110 |
+
for (; c < cols; c++) acc += row[c] * x[c];
|
| 111 |
+
return acc;
|
| 112 |
+
}
|
| 113 |
+
static void matvec(const real_t *m, int rows, int cols, const real_t *x, const real_t *bias, real_t *y) {
|
| 114 |
+
for (int r = 0; r < rows; r++) y[r] = bias ? dot(m + (size_t)r * cols, x, cols) + bias[r] : dot(m + (size_t)r * cols, x, cols);
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
typedef struct { real_t *qkv_w, *qkv_b, *out_w, *ff_in_w, *ff_out_w; } Layer;
|
| 118 |
+
static struct {
|
| 119 |
+
int D, H, layers, vocab, proj_out, stop_id, n_out, d_ffn, rank;
|
| 120 |
+
Layer *layer; real_t *embed, *basis, *coeff; int32_t *out_ids; char **tokens;
|
| 121 |
+
} M;
|
| 122 |
+
|
| 123 |
+
/* One head, one token: a delta-rule write into the 3x3 cells around the key, then a read of the
|
| 124 |
+
* 3x3 cells around the query. */
|
| 125 |
+
static void memory_step(int table, const real_t *p, int h, real_t *out) {
|
| 126 |
+
const int H = M.H; const real_t *v = p + 6 * H + 2 * h;
|
| 127 |
+
real_t beta = sigmoid(p[4 * H + h]), alpha = sigmoid(-p[5 * H + h]), wx[3], wy[3];
|
| 128 |
+
int64_t x0, y0;
|
| 129 |
+
phi(p[2 * h], &x0, wx); phi(p[2 * h + 1], &y0, wy);
|
| 130 |
+
for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) {
|
| 131 |
+
real_t w = wx[i] * wy[j] * beta;
|
| 132 |
+
if (w == 0.0) continue;
|
| 133 |
+
real_t *S = cell_write(table, x0 + i, y0 + j);
|
| 134 |
+
real_t d0 = (v[0] - alpha * S[0]) * w, d1 = (v[1] - alpha * S[1]) * w;
|
| 135 |
+
S[0] += d0; S[1] += d1;
|
| 136 |
+
}
|
| 137 |
+
phi(p[2 * H + 2 * h], &x0, wx); phi(p[2 * H + 2 * h + 1], &y0, wy);
|
| 138 |
+
out[0] = out[1] = 0.0;
|
| 139 |
+
for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) {
|
| 140 |
+
const real_t *S = cell_read(table, x0 + i, y0 + j);
|
| 141 |
+
out[0] += wx[i] * wy[j] * S[0]; out[1] += wx[i] * wy[j] * S[1];
|
| 142 |
+
}
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
/* One token through every layer, then the most likely next token. */
|
| 146 |
+
static int32_t forward(int32_t token) {
|
| 147 |
+
static real_t x[64], proj[512], mem[64], res[64], pre[256], gate[128], feat[16];
|
| 148 |
+
const int D = M.D;
|
| 149 |
+
memcpy(x, M.embed + (size_t)token * D, D * sizeof(real_t));
|
| 150 |
+
for (int l = 0; l < M.layers; l++) {
|
| 151 |
+
const Layer *L = &M.layer[l];
|
| 152 |
+
matvec(L->qkv_w, M.proj_out, D, x, L->qkv_b, proj);
|
| 153 |
+
for (int h = 0; h < M.H; h++) memory_step(l * M.H + h, proj, h, mem + 2 * h);
|
| 154 |
+
matvec(L->out_w, D, 2 * M.H, mem, NULL, res);
|
| 155 |
+
for (int i = 0; i < D; i++) x[i] += res[i];
|
| 156 |
+
matvec(L->ff_in_w, 2 * M.d_ffn, D, x, NULL, pre);
|
| 157 |
+
for (int j = 0; j < M.d_ffn; j++) gate[j] = (pre[j] > 0.0 ? pre[j] : 0.0) * pre[M.d_ffn + j];
|
| 158 |
+
matvec(L->ff_out_w, D, M.d_ffn, gate, NULL, res);
|
| 159 |
+
for (int i = 0; i < D; i++) x[i] += res[i];
|
| 160 |
+
}
|
| 161 |
+
matvec(M.basis, M.rank, D, x, NULL, feat);
|
| 162 |
+
real_t best = -HUGE_VAL; int32_t best_row = 0;
|
| 163 |
+
for (int r = 0; r < M.n_out; r++) {
|
| 164 |
+
real_t acc = 0.0;
|
| 165 |
+
for (int k = 0; k < M.rank; k++) acc += M.coeff[(size_t)r * M.rank + k] * feat[k];
|
| 166 |
+
if (acc > best) { best = acc; best_row = r; }
|
| 167 |
+
}
|
| 168 |
+
return M.out_ids[best_row];
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
/* ── loading ─────────────────────────────────────────────────────────── */
|
| 172 |
+
static void poke(int64_t address, const uint8_t *bytes, size_t n) { /* bytes into the memory's cells */
|
| 173 |
+
int table = number("memory_layer") * M.H + number("memory_head"); double spacing = number("spacing");
|
| 174 |
+
for (size_t i = 0; i < n; i++, address++) {
|
| 175 |
+
real_t *s = cell_write(table, (int64_t)(spacing * (double)(address % 65536)), (int64_t)(spacing * (double)(address / 65536)));
|
| 176 |
+
s[0] = (real_t)bytes[i]; s[1] = 0.0;
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
static void poke_word(int64_t address, uint32_t v) { uint8_t b[4] = {v & 255, v >> 8 & 255, v >> 16 & 255, v >> 24}; poke(address, b, 4); }
|
| 180 |
+
|
| 181 |
+
int spotlight_load(const char *dir) {
|
| 182 |
+
snprintf(folder, sizeof folder, "%s", dir);
|
| 183 |
+
config = slurp(file("intelligence/config.json"), NULL);
|
| 184 |
+
M.D = number("d_model"); M.H = number("n_heads"); M.layers = number("n_layers"); M.vocab = number("vocab_size");
|
| 185 |
+
M.proj_out = number("proj_out"); M.stop_id = number("stop_id"); M.n_out = number("n_out");
|
| 186 |
+
M.d_ffn = number("d_ffn"); M.rank = number("head_rank");
|
| 187 |
+
if (number("d_rest") != 0 || number("d_v") != 2 || number("alpha_rows") != 1) die("unsupported model shape");
|
| 188 |
+
char *vocab = slurp(file("intelligence/vocab.json"), NULL), *p = vocab, name[64];
|
| 189 |
+
M.tokens = alloc(M.vocab * sizeof(char *));
|
| 190 |
+
for (int i = 0; i < M.vocab; i++) {
|
| 191 |
+
p = strchr(p, '"') + 1; char *q = M.tokens[i] = alloc(64);
|
| 192 |
+
while (*p != '"') { if (*p == '\\') p++; *q++ = *p++; }
|
| 193 |
+
p++;
|
| 194 |
+
}
|
| 195 |
+
char *w = slurp(file("intelligence/model.safetensors"), NULL);
|
| 196 |
+
M.layer = alloc(M.layers * sizeof(Layer));
|
| 197 |
+
for (int l = 0; l < M.layers; l++) {
|
| 198 |
+
Layer *L = &M.layer[l];
|
| 199 |
+
snprintf(name, sizeof name, "layers.%d.qkv_w", l); L->qkv_w = load(w, name, (int64_t)M.proj_out * M.D);
|
| 200 |
+
snprintf(name, sizeof name, "layers.%d.qkv_b", l); L->qkv_b = load(w, name, M.proj_out);
|
| 201 |
+
snprintf(name, sizeof name, "layers.%d.out_w", l); L->out_w = load(w, name, (int64_t)M.D * 2 * M.H);
|
| 202 |
+
snprintf(name, sizeof name, "layers.%d.ff_in_w", l); L->ff_in_w = load(w, name, (int64_t)2 * M.d_ffn * M.D);
|
| 203 |
+
snprintf(name, sizeof name, "layers.%d.ff_out_w", l); L->ff_out_w = load(w, name, (int64_t)M.D * M.d_ffn);
|
| 204 |
+
}
|
| 205 |
+
M.embed = load(w, "embed_tokens.weight", (int64_t)M.vocab * M.D);
|
| 206 |
+
M.basis = load(w, "head_basis", (int64_t)M.rank * M.D);
|
| 207 |
+
M.coeff = load(w, "head_coeff", (int64_t)M.n_out * M.rank);
|
| 208 |
+
real_t *ids = load(w, "out_ids", M.n_out);
|
| 209 |
+
M.out_ids = alloc(M.n_out * sizeof(int32_t));
|
| 210 |
+
for (int i = 0; i < M.n_out; i++) M.out_ids[i] = (int32_t)ids[i];
|
| 211 |
+
free(vocab); free(w); free(ids);
|
| 212 |
+
return M.vocab;
|
| 213 |
+
}
|
| 214 |
+
|
| 215 |
+
/* A fresh memory: MicroPython's code, tables and boot state, then the packages in one registry. */
|
| 216 |
+
static void load_memory(char **packages, int n) {
|
| 217 |
+
free(cells); cells = NULL; cells_cap = cells_n = 0;
|
| 218 |
+
char *core = slurp(file("memory/core.safetensors"), NULL), name[64];
|
| 219 |
+
for (int t = 0; t < M.layers * M.H; t++) {
|
| 220 |
+
Tensor xy, value;
|
| 221 |
+
snprintf(name, sizeof name, "cells.%d.%d.xy", t / M.H, t % M.H); if (!tensor(core, name, &xy)) continue;
|
| 222 |
+
snprintf(name, sizeof name, "cells.%d.%d.value", t / M.H, t % M.H); if (!tensor(core, name, &value)) continue;
|
| 223 |
+
for (int64_t i = 0; i < xy.count / 2; i++) {
|
| 224 |
+
real_t *s = cell_write(t, (int64_t)item(&xy, 2 * i), (int64_t)item(&xy, 2 * i + 1));
|
| 225 |
+
s[0] = (real_t)item(&value, 2 * i); s[1] = (real_t)item(&value, 2 * i + 1);
|
| 226 |
+
}
|
| 227 |
+
}
|
| 228 |
+
free(core);
|
| 229 |
+
if (n == 0) return;
|
| 230 |
+
int64_t base = number("registry_base"), size = 16, pages, modules = 0;
|
| 231 |
+
for (int i = 0; i < n; i++) {
|
| 232 |
+
char *bytes = slurp(packages[i], NULL); Tensor b;
|
| 233 |
+
if (!tensor(bytes, "bundle", &b) || memcmp(b.data, "SPKG", 4)) die("not a package");
|
| 234 |
+
poke(base + size, (const uint8_t *)b.data + 16, b.count - 16);
|
| 235 |
+
uint32_t count; memcpy(&count, b.data + 12, 4);
|
| 236 |
+
size += b.count - 16; modules += count;
|
| 237 |
+
free(bytes);
|
| 238 |
+
}
|
| 239 |
+
uint8_t header[8] = {'S', 'P', 'K', 'G', 1};
|
| 240 |
+
poke(base, header, 8); poke_word(base + 8, (uint32_t)size); poke_word(base + 12, (uint32_t)modules);
|
| 241 |
+
poke_word(number("registry_root_word"), (uint32_t)base);
|
| 242 |
+
pages = (base + size + 65535) / 65536;
|
| 243 |
+
poke_word(number("memory_pages_word"), (uint32_t)(pages > number("image_pages") ? pages : number("image_pages")));
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
/* ── running ─────────────────────────────────────────────────────────── */
|
| 247 |
+
static int32_t *kept; static int64_t kept_n, kept_cap; static int printing = 0, verbose = 0;
|
| 248 |
+
|
| 249 |
+
/* Runs a prompt from a fresh memory until the stop token (or max_new tokens). The command line
|
| 250 |
+
* prints the output as it comes; the library keeps the tokens that carry it, and the stop token. */
|
| 251 |
+
int64_t spotlight_generate(const int32_t *prompt, int64_t n, int64_t max_new, char **packages, int n_packages) {
|
| 252 |
+
load_memory(packages, n_packages);
|
| 253 |
+
int32_t next = 0; kept_n = 0;
|
| 254 |
+
for (int64_t i = 0; i < n; i++) next = forward(prompt[i]);
|
| 255 |
+
for (int64_t count = 1;; count++, next = forward(next)) {
|
| 256 |
+
const char *t = M.tokens[next]; int out = !strncmp(t, "out(", 4);
|
| 257 |
+
if (verbose) fprintf(stderr, "%s ", t);
|
| 258 |
+
if (printing && out) { putchar(strlen(t) == 6 ? t[4] : (int)strtol(t + 4, NULL, 16)); fflush(stdout); }
|
| 259 |
+
if (!printing && (out || next == M.stop_id)) {
|
| 260 |
+
if (kept_n == kept_cap) kept = realloc(kept, (kept_cap = kept_cap ? 2 * kept_cap : 1024) * sizeof(int32_t));
|
| 261 |
+
kept[kept_n++] = next;
|
| 262 |
+
}
|
| 263 |
+
if (next == M.stop_id || count == max_new) return count;
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
const int32_t *spotlight_kept(int64_t *n) { *n = kept_n; return kept; }
|
| 267 |
+
|
| 268 |
+
#ifndef SPOTLIGHT_LIBRARY
|
| 269 |
+
static int32_t token(const char *name) {
|
| 270 |
+
for (int32_t i = 0; i < M.vocab; i++) if (!strcmp(M.tokens[i], name)) return i;
|
| 271 |
+
die(name); return -1;
|
| 272 |
+
}
|
| 273 |
+
static int is_package(const struct dirent *e) {
|
| 274 |
+
size_t n = strlen(e->d_name);
|
| 275 |
+
return n > 12 && !strcmp(e->d_name + n - 12, ".safetensors") && strcmp(e->d_name, "core.safetensors");
|
| 276 |
+
}
|
| 277 |
+
int main(int argc, char **argv) {
|
| 278 |
+
verbose = argc > 1 && !strcmp(argv[1], "-v"); printing = 1;
|
| 279 |
+
spotlight_load(".");
|
| 280 |
+
struct dirent **list; int n_packages = scandir("memory", &list, is_package, alphasort);
|
| 281 |
+
char **packages = alloc((n_packages > 0 ? n_packages : 1) * sizeof(char *));
|
| 282 |
+
for (int i = 0; i < n_packages; i++) { packages[i] = alloc(300); snprintf(packages[i], 300, "memory/%s", list[i]->d_name); }
|
| 283 |
+
/* The prompt: where the source goes, its bytes (and a NUL) page by page, then execute. */
|
| 284 |
+
size_t n; char *src = slurp(NULL, &n), hex[16];
|
| 285 |
+
int64_t input = number("input_base"), pages = (input % 65536 + (int64_t)n) / 65536 + 1, count = 0;
|
| 286 |
+
int32_t *prompt = alloc((n + 16 * pages + 8) * sizeof(int32_t));
|
| 287 |
+
for (int64_t r = pages - 1; r >= 0; r--) {
|
| 288 |
+
uint32_t address = (uint32_t)(r ? (input / 65536 + r) * 65536 : input);
|
| 289 |
+
for (int lane = 0; lane < 4; lane++) { snprintf(hex, sizeof hex, "%02x", address >> 8 * lane & 255); prompt[count++] = token(hex); }
|
| 290 |
+
prompt[count++] = token("page");
|
| 291 |
+
}
|
| 292 |
+
for (int64_t i = 0; i <= (int64_t)n; i++) {
|
| 293 |
+
unsigned char b = i < (int64_t)n ? src[i] : 0;
|
| 294 |
+
if (b >= 128) die("the program must be ASCII");
|
| 295 |
+
if (i && (input + i) % 65536 == 0) prompt[count++] = token("unpage");
|
| 296 |
+
if (b > 0x20 && b < 0x7f) { hex[0] = b; hex[1] = 0; } else snprintf(hex, sizeof hex, "ascii(%02x)", b);
|
| 297 |
+
prompt[count++] = token(hex);
|
| 298 |
+
}
|
| 299 |
+
prompt[count++] = token("execute");
|
| 300 |
+
count = spotlight_generate(prompt, count, INT64_MAX, packages, n_packages > 0 ? n_packages : 0);
|
| 301 |
+
fprintf(stderr, "%s%lld tokens generated\n", verbose ? "\n" : "", (long long)count);
|
| 302 |
+
return 0;
|
| 303 |
+
}
|
| 304 |
+
#endif
|
spotlight.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Spotlight VM as a `transformers` model. The model runs spotlight.c, built into build/ on first use."""
|
| 2 |
+
import ctypes, glob, json, os, subprocess
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
from huggingface_hub import snapshot_download
|
| 6 |
+
from transformers import PretrainedConfig, PreTrainedModel, PreTrainedTokenizer
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class SpotlightConfig(PretrainedConfig):
|
| 10 |
+
model_type = "spotlight"
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class SpotlightForCausalLM(PreTrainedModel):
|
| 14 |
+
config_class = SpotlightConfig
|
| 15 |
+
|
| 16 |
+
@classmethod
|
| 17 |
+
def from_pretrained(cls, name_or_path, *args, **kwargs):
|
| 18 |
+
folder = os.path.abspath(name_or_path if os.path.isdir(name_or_path) else snapshot_download(name_or_path))
|
| 19 |
+
library = os.path.join(folder, "build", "libspotlight.so")
|
| 20 |
+
if not os.path.exists(library):
|
| 21 |
+
os.makedirs(os.path.dirname(library), exist_ok=True)
|
| 22 |
+
source = os.path.join(folder, "spotlight.c")
|
| 23 |
+
subprocess.run(["cc", "-O3", "-std=c99", "-shared", "-fPIC", "-DSPOTLIGHT_LIBRARY", source, "-lm", "-o", library], check=True)
|
| 24 |
+
model = cls(SpotlightConfig.from_pretrained(folder))
|
| 25 |
+
model.folder, model.lib = folder, ctypes.CDLL(library)
|
| 26 |
+
model.lib.spotlight_generate.restype = ctypes.c_int64
|
| 27 |
+
model.lib.spotlight_kept.restype = ctypes.POINTER(ctypes.c_int32)
|
| 28 |
+
model.lib.spotlight_load(folder.encode())
|
| 29 |
+
return model
|
| 30 |
+
|
| 31 |
+
@torch.no_grad()
|
| 32 |
+
def generate(self, input_ids, max_new_tokens=10**12, packages=None, **kwargs):
|
| 33 |
+
"""Runs the program from a fresh memory: the core plus `packages` (default: all in memory/).
|
| 34 |
+
Returns the prompt followed by the generated tokens that carry output, and the stop token."""
|
| 35 |
+
if packages is None:
|
| 36 |
+
packages = [p for p in sorted(glob.glob(os.path.join(self.folder, "memory", "*.safetensors")))
|
| 37 |
+
if not p.endswith("core.safetensors")]
|
| 38 |
+
paths = (ctypes.c_char_p * max(1, len(packages)))(*[os.path.abspath(p).encode() for p in packages])
|
| 39 |
+
prompt = input_ids[0].to(torch.int32).contiguous()
|
| 40 |
+
self.generated_tokens = self.lib.spotlight_generate(ctypes.c_void_p(prompt.data_ptr()), ctypes.c_int64(prompt.numel()),
|
| 41 |
+
ctypes.c_int64(max_new_tokens), paths, len(packages))
|
| 42 |
+
n = ctypes.c_int64()
|
| 43 |
+
kept = ctypes.string_at(self.lib.spotlight_kept(ctypes.byref(n)), 4 * n.value)
|
| 44 |
+
return torch.cat([prompt, torch.frombuffer(bytearray(kept), dtype=torch.int32)]).long()[None]
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class SpotlightTokenizer(PreTrainedTokenizer):
|
| 48 |
+
vocab_files_names = {"vocab_file": "intelligence/vocab.json", "config_file": "intelligence/config.json"}
|
| 49 |
+
|
| 50 |
+
def __init__(self, vocab_file, config_file, **kwargs):
|
| 51 |
+
with open(vocab_file) as f:
|
| 52 |
+
self.names = json.load(f)
|
| 53 |
+
self.ids = {name: i for i, name in enumerate(self.names)}
|
| 54 |
+
with open(config_file) as f:
|
| 55 |
+
self.input_base = json.load(f)["input_base"]
|
| 56 |
+
super().__init__(**kwargs)
|
| 57 |
+
|
| 58 |
+
vocab_size = property(lambda self: len(self.names))
|
| 59 |
+
get_vocab = lambda self: dict(self.ids)
|
| 60 |
+
_convert_token_to_id = lambda self, token: self.ids[token]
|
| 61 |
+
_convert_id_to_token = lambda self, index: self.names[index]
|
| 62 |
+
|
| 63 |
+
def _tokenize(self, text, **kwargs):
|
| 64 |
+
"""Where the source goes, its bytes (and a NUL) page by page, then execute -- as spotlight.c does."""
|
| 65 |
+
data, base, tokens = text.encode("ascii") + b"\0", self.input_base, []
|
| 66 |
+
for page in reversed(range((base % 65536 + len(data) - 1) // 65536 + 1)):
|
| 67 |
+
address = (base // 65536 + page) * 65536 if page else base
|
| 68 |
+
tokens += [f"{address >> 8 * lane & 255:02x}" for lane in range(4)] + ["page"]
|
| 69 |
+
for i, b in enumerate(data):
|
| 70 |
+
tokens += (["unpage"] if i and (base + i) % 65536 == 0 else []) + [chr(b) if 0x20 < b < 0x7F else f"ascii({b:02x})"]
|
| 71 |
+
return tokens + ["execute"]
|
| 72 |
+
|
| 73 |
+
def decode(self, token_ids, **kwargs):
|
| 74 |
+
"""The program's printed output: the bytes carried by the out(..) tokens."""
|
| 75 |
+
names = [self.names[i] for i in (token_ids.tolist() if hasattr(token_ids, "tolist") else token_ids)]
|
| 76 |
+
return bytes(ord(n[4]) if len(n) == 6 else int(n[4:-1], 16) for n in names if n.startswith("out(")).decode("latin-1")
|