Instructions to use FermionResearch/Neutrino-8B with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use FermionResearch/Neutrino-8B with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="FermionResearch/Neutrino-8B") messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("FermionResearch/Neutrino-8B", device_map="auto") - llama-cpp-python
How to use FermionResearch/Neutrino-8B with llama-cpp-python:
# !pip install llama-cpp-python from llama_cpp import Llama llm = Llama.from_pretrained( repo_id="FermionResearch/Neutrino-8B", filename="gguf/neutrino-8b-fv5.gguf", )
llm.create_chat_completion( messages = [ { "role": "user", "content": "What is the capital of France?" } ] ) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use FermionResearch/Neutrino-8B with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf FermionResearch/Neutrino-8B # Run inference directly in the terminal: llama cli -hf FermionResearch/Neutrino-8B
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf FermionResearch/Neutrino-8B # Run inference directly in the terminal: llama cli -hf FermionResearch/Neutrino-8B
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf FermionResearch/Neutrino-8B # Run inference directly in the terminal: ./llama-cli -hf FermionResearch/Neutrino-8B
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf FermionResearch/Neutrino-8B # Run inference directly in the terminal: ./build/bin/llama-cli -hf FermionResearch/Neutrino-8B
Use Docker
docker model run hf.co/FermionResearch/Neutrino-8B
- LM Studio
- Jan
- vLLM
How to use FermionResearch/Neutrino-8B with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "FermionResearch/Neutrino-8B" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FermionResearch/Neutrino-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/FermionResearch/Neutrino-8B
- SGLang
How to use FermionResearch/Neutrino-8B 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 "FermionResearch/Neutrino-8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FermionResearch/Neutrino-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'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 "FermionResearch/Neutrino-8B" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FermionResearch/Neutrino-8B", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Ollama
How to use FermionResearch/Neutrino-8B with Ollama:
ollama run hf.co/FermionResearch/Neutrino-8B
- Unsloth Studio
How to use FermionResearch/Neutrino-8B with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for FermionResearch/Neutrino-8B to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for FermionResearch/Neutrino-8B to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for FermionResearch/Neutrino-8B to start chatting
- Pi
How to use FermionResearch/Neutrino-8B with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf FermionResearch/Neutrino-8B
Configure the model in Pi
# Install Pi: npm install -g @mariozechner/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "FermionResearch/Neutrino-8B" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Hermes Agent new
How to use FermionResearch/Neutrino-8B with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf FermionResearch/Neutrino-8B
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default FermionResearch/Neutrino-8B
Run Hermes
hermes
- Atomic Chat new
- OpenClaw new
How to use FermionResearch/Neutrino-8B with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf FermionResearch/Neutrino-8B
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "FermionResearch/Neutrino-8B" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
- Docker Model Runner
How to use FermionResearch/Neutrino-8B with Docker Model Runner:
docker model run hf.co/FermionResearch/Neutrino-8B
- Lemonade
How to use FermionResearch/Neutrino-8B with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull FermionResearch/Neutrino-8B
Run and chat with the model
lemonade run user.Neutrino-8B-{{QUANT_TAG}}List all available models
lemonade list
Neutrino-8B publish tree (cons3-B25 ship brain 016c6f36)
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- LICENSE +202 -0
- MANIFEST.json +335 -0
- NOTICE +43 -0
- README.md +558 -0
- bin/MANIFEST.json +25 -0
- bin/README.md +140 -0
- bin/fermion-run-linux-x64 +3 -0
- bin/fermion-run-linux-x64.sha256 +1 -0
- bin/fermion-run-macos-arm64 +3 -0
- bin/fermion-run-macos-arm64.sha256 +1 -0
- config.json +24 -0
- generation_config.json +10 -0
- gguf/Modelfile +5 -0
- gguf/README.md +223 -0
- gguf/SHA256SUMS +23 -0
- gguf/receipts/bench_8b.json +112 -0
- gguf/receipts/convert_8b.json +44 -0
- gguf/receipts/gate_8b_fv5.json +105 -0
- gguf/receipts/gateverdict_8b_fv5.json +17 -0
- gguf/receipts/gguf_spec_quickstart_k16.json +141 -0
- gguf/receipts/gguf_spec_quickstart_k4.json +141 -0
- gguf/receipts/ref_8b.json +26 -0
- gguf/receipts_ifcs50_retired/bench_8b.json +112 -0
- gguf/receipts_ifcs50_retired/convert_8b.json +44 -0
- gguf/receipts_ifcs50_retired/gateverdict_8b_fv5.json +17 -0
- gguf/receipts_ifcs50_retired/ref_8b.json +26 -0
- merges.txt +0 -0
- mlx/MANIFEST.json +143 -0
- mlx/README.md +133 -0
- mlx/fermion_mlx/__init__.py +129 -0
- mlx/fermion_mlx/__main__.py +177 -0
- mlx/fermion_mlx/arch3.py +183 -0
- mlx/fermion_mlx/container.py +143 -0
- mlx/fermion_mlx/gates.py +164 -0
- mlx/fermion_mlx/mbatch.py +164 -0
- mlx/fermion_mlx/model.py +281 -0
- mlx/fermion_mlx/spec.py +256 -0
- mlx/fermion_mlx/trit_kernels.py +877 -0
- mlx/merges.txt +0 -0
- mlx/receipts/bench_8b_wbase_m5_16gb_memcapped.json +39 -0
- mlx/receipts/pathgate_8b_cons3b25.json +75 -0
- mlx/receipts/pathgate_8b_d25.json +75 -0
- mlx/receipts/pathgate_8b_product.json +75 -0
- mlx/receipts/pathgate_8b_wbase.json +75 -0
- mlx/receipts/refgate_8b_cons3b25.json +133 -0
- mlx/receipts/refgate_8b_d25.json +133 -0
- mlx/receipts/refgate_8b_product.json +133 -0
- mlx/receipts/refgate_8b_wbase.json +133 -0
- mlx/receipts/specbench_8b_0p6b_m5.json +57 -0
- mlx/receipts/specgate_8b_0p6b.json +47 -0
LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "{}"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright 2026 Fermion Research
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
| 202 |
+
|
MANIFEST.json
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"note": "provenance: HF-tree filename -> source + sha256; weights entry is declarative until materialized | upload lane 2026-07-27: __pycache__/*.pyc excluded from the published tree (not-for-shipping)",
|
| 3 |
+
"weights_container": {
|
| 4 |
+
"file": "neutrino-8b_v4.bin",
|
| 5 |
+
"status": "FINAL \u2014 cons3-B25 SHIP BRAIN (consolidation-3 arm B step 25, FOUNDER-AUTHORISED PROMOTE 2026-07-27 ~19:50 IST: GSM8K stated 49.33 / flexible 51.00 / BFCL 65.31 / IFEval-ps 73.17 / MMLU-Redux 67.84 / full-57 72.084 / C4 21.475 / term 0.5067. The Redux no-regression guard (69.5) was breached at 67.84 and the founder amended the bar on the record \u2014 see handoffs/RESEARCH_LEDGER.md. Supersedes ifc-s50 39787a2e\u2026a3be)",
|
| 6 |
+
"bytes": 3875404812,
|
| 7 |
+
"sha256": "016c6f362ad237a8cf4043601efbc1e766574f76ea58691e78fdef51b4155fa0",
|
| 8 |
+
"source": "internal lab archive: archive/consolidation_3/export_v4_B25/neutrino-8b_v4.bin (public copy: huggingface.co/fermionresearch/Neutrino-8B)",
|
| 9 |
+
"qat_state_sha256": "09c3f36a72e73223f79e4d7c6fd82295ecc09eb7d81f027afbd239f26f9441b8",
|
| 10 |
+
"coded_transport": {
|
| 11 |
+
"file": "neutrino-8b_v4.tv4z",
|
| 12 |
+
"bytes": 2559836297,
|
| 13 |
+
"sha256": "83ec7a52bd6733e66fb546e89b9c6aa8b3411af7f648e9c0b2bb56ba50aac4df",
|
| 14 |
+
"note": "lossless tv4z transport, 66.05% of raw; decode round-trip md5/sha/cmp PASS"
|
| 15 |
+
},
|
| 16 |
+
"retired_do_not_verify_against": {
|
| 17 |
+
"ifc-s50 container": "39787a2e15dcffa20135c7c1bb1b8ed55c8632263f8457f3395f55d52007a3be",
|
| 18 |
+
"ifc-s50 tv4z": "7d36adaf18c5d779aa96d09a6a6eb080eec39bc8b7dcc8a5eb6680f8fc134c7a",
|
| 19 |
+
"ifc-s50 qat_state": "e62a1199f83285f60352ede2ec9255dbcbc468976e5c8b9595536717260fb6a9",
|
| 20 |
+
"ifc-s50 gguf": "7dda995047dc70540d2d83f2c464dd118fff62eef1aab36ae71ceb3cc972e5d0"
|
| 21 |
+
}
|
| 22 |
+
},
|
| 23 |
+
"files": {
|
| 24 |
+
".gitattributes": {
|
| 25 |
+
"bytes": 1905,
|
| 26 |
+
"sha256": "501edc5292358aaa11dd13cd5e9b25c43f94df3cc9fff43dd3c0df50efcc40c0",
|
| 27 |
+
"source": "banked lane artifact (LFS globs made explicit per-file 2026-07-27 upload lane)"
|
| 28 |
+
},
|
| 29 |
+
"LICENSE": {
|
| 30 |
+
"bytes": 11347,
|
| 31 |
+
"sha256": "2f978a54ef89f3e4ee2d413c347e4b03500c95f29342277a287acc2ae27ae280",
|
| 32 |
+
"source": "Apache-2.0 text (same file across all release trees)"
|
| 33 |
+
},
|
| 34 |
+
"NOTICE": {
|
| 35 |
+
"bytes": 1557,
|
| 36 |
+
"sha256": "a17bda9d5b1e7d90927a39a2b7c2e40a5cb8afe36ecb9f8c776802cec75b04ca",
|
| 37 |
+
"source": "banked lane artifact"
|
| 38 |
+
},
|
| 39 |
+
"bin/README.md": {
|
| 40 |
+
"bytes": 7211,
|
| 41 |
+
"sha256": "172b910ceb503f28b8bd1977e805d3cd04024192625b31ef1899a30b8e8d6c88",
|
| 42 |
+
"source": "bin/ staging doc: which fermion-run binaries land at upload + sidecar convention"
|
| 43 |
+
},
|
| 44 |
+
"bin/fermion-run-linux-x64": {
|
| 45 |
+
"bytes": 1249584,
|
| 46 |
+
"sha256": "499ecdc569eae2ceaeb4a34cd1f11a91c2c09dfb17aa5847532359c6dc57d93a",
|
| 47 |
+
"source": "BLESSED release binary (Linux x86-64 AVX2 baseline, fully static) \u2014 release-binaries lane 2026-07-25, receipt handoffs/results_release_binaries/receipt_linux_x64.json"
|
| 48 |
+
},
|
| 49 |
+
"bin/fermion-run-linux-x64.sha256": {
|
| 50 |
+
"bytes": 88,
|
| 51 |
+
"sha256": "2350e759fe77faa43803b43ab72f5c3bf2ca12263dddc680af6e453381f2649c",
|
| 52 |
+
"source": "sha256 sidecar (shasum line) for fermion-run-linux-x64"
|
| 53 |
+
},
|
| 54 |
+
"bin/fermion-run-macos-arm64": {
|
| 55 |
+
"bytes": 621480,
|
| 56 |
+
"sha256": "d7b257b5cd4b08cadd04fd867158ac50619aab23164294794dc10f05f1ede53c",
|
| 57 |
+
"source": "BLESSED release binary (macOS arm64 CPU/NEON, static libomp) \u2014 release-binaries lane 2026-07-25, receipt handoffs/results_release_binaries/receipt_macos_arm64.json"
|
| 58 |
+
},
|
| 59 |
+
"bin/fermion-run-macos-arm64.sha256": {
|
| 60 |
+
"bytes": 90,
|
| 61 |
+
"sha256": "d706f6935e715a96df3736035fb22ffa7854a8cb337b575542cd03ecb281ef72",
|
| 62 |
+
"source": "sha256 sidecar (shasum line) for fermion-run-macos-arm64"
|
| 63 |
+
},
|
| 64 |
+
"config.json": {
|
| 65 |
+
"bytes": 505,
|
| 66 |
+
"sha256": "4779738d791ad490794f6b0a7e04c1cb50f2720fbe8b6503c61da547c0861c0a",
|
| 67 |
+
"source": "written for the trtc_v4 loader (hf_ternary registration)"
|
| 68 |
+
},
|
| 69 |
+
"generation_config.json": {
|
| 70 |
+
"bytes": 181,
|
| 71 |
+
"sha256": "2537909d4312b7155b60fe0aee96d342de1a00b7841ed3e7eba4c3835d16d8ae",
|
| 72 |
+
"source": "transformers generation defaults for this SKU"
|
| 73 |
+
},
|
| 74 |
+
"gguf/Modelfile": {
|
| 75 |
+
"bytes": 214,
|
| 76 |
+
"sha256": "47c4c5ac0228e25197eb52e1ae0a964f95ac0d562d9effe9547f01c0cb58a9d1",
|
| 77 |
+
"source": "banked lane artifact"
|
| 78 |
+
},
|
| 79 |
+
"gguf/README.md": {
|
| 80 |
+
"bytes": 10315,
|
| 81 |
+
"sha256": "0124d20fe1e1af892cd809bbcc0a9d390d687684c0747bc6ca8a0b9e12280ce2",
|
| 82 |
+
"source": "banked lane artifact"
|
| 83 |
+
},
|
| 84 |
+
"gguf/SHA256SUMS": {
|
| 85 |
+
"bytes": 1912,
|
| 86 |
+
"sha256": "457e700fc5db35c5618a7c61dc0e4ac601d94f12ed264374b0717c574275bfc4",
|
| 87 |
+
"source": "banked lane artifact"
|
| 88 |
+
},
|
| 89 |
+
"gguf/receipts/bench_8b.json": {
|
| 90 |
+
"bytes": 2663,
|
| 91 |
+
"sha256": "1976449e3aa06f21e8337d0035ce8ee8371d6ff94d84adbafd149dbfa3f9d6fb",
|
| 92 |
+
"source": "banked lane artifact"
|
| 93 |
+
},
|
| 94 |
+
"gguf/receipts/convert_8b.json": {
|
| 95 |
+
"bytes": 953,
|
| 96 |
+
"sha256": "09060b46412b45b856edd239c37b49208f7cebb153620dda1808296ace8308ea",
|
| 97 |
+
"source": "banked lane artifact"
|
| 98 |
+
},
|
| 99 |
+
"gguf/receipts/gate_8b_fv5.json": {
|
| 100 |
+
"bytes": 6989,
|
| 101 |
+
"sha256": "393e6d48d74c27bca09ce82af855a1d5917347595477c4587774d0391b305e34",
|
| 102 |
+
"source": "banked lane artifact"
|
| 103 |
+
},
|
| 104 |
+
"gguf/receipts/gateverdict_8b_fv5.json": {
|
| 105 |
+
"bytes": 457,
|
| 106 |
+
"sha256": "cd79e09be62309d4146da1530211529a9b98cffed0e240c7ef30c7466f8a64c1",
|
| 107 |
+
"source": "banked lane artifact"
|
| 108 |
+
},
|
| 109 |
+
"gguf/receipts/gguf_spec_quickstart_k16.json": {
|
| 110 |
+
"bytes": 9857,
|
| 111 |
+
"sha256": "50079ec10a6b1b1eaf131a2955a2d409d5c1b35926aa12a94e7789ff9bd11962",
|
| 112 |
+
"source": "banked lane artifact"
|
| 113 |
+
},
|
| 114 |
+
"gguf/receipts/gguf_spec_quickstart_k4.json": {
|
| 115 |
+
"bytes": 9830,
|
| 116 |
+
"sha256": "a2ef2374d6affde8ec47560ff146d44da8170c939d55c82d32fea653ae5e3da2",
|
| 117 |
+
"source": "banked lane artifact"
|
| 118 |
+
},
|
| 119 |
+
"gguf/receipts/ref_8b.json": {
|
| 120 |
+
"bytes": 470,
|
| 121 |
+
"sha256": "8916c4936da50ca500f521a19ce4d69668e8fd5603588113359efba5a059851a",
|
| 122 |
+
"source": "banked lane artifact"
|
| 123 |
+
},
|
| 124 |
+
"gguf/receipts_ifcs50_retired/bench_8b.json": {
|
| 125 |
+
"bytes": 2656,
|
| 126 |
+
"sha256": "348dccfc95f896beaacaf7297df7574b364e4d6a431bc54a34f4737fb39acdd8",
|
| 127 |
+
"source": "banked lane artifact"
|
| 128 |
+
},
|
| 129 |
+
"gguf/receipts_ifcs50_retired/convert_8b.json": {
|
| 130 |
+
"bytes": 947,
|
| 131 |
+
"sha256": "111e69bfcf5cea559dfcd500c58b07db366cd1c26cd2c27be49dcf1d645f940a",
|
| 132 |
+
"source": "banked lane artifact"
|
| 133 |
+
},
|
| 134 |
+
"gguf/receipts_ifcs50_retired/gateverdict_8b_fv5.json": {
|
| 135 |
+
"bytes": 457,
|
| 136 |
+
"sha256": "2f45fb6b747cb2ac8e3910bc4e929caad78660485e329eb1828095cb2dff5fa4",
|
| 137 |
+
"source": "banked lane artifact"
|
| 138 |
+
},
|
| 139 |
+
"gguf/receipts_ifcs50_retired/ref_8b.json": {
|
| 140 |
+
"bytes": 470,
|
| 141 |
+
"sha256": "0eb0e2485f3b5aa3c303e33200112cde1fd5441642e440ee1d7e5e5f4821966e",
|
| 142 |
+
"source": "banked lane artifact"
|
| 143 |
+
},
|
| 144 |
+
"merges.txt": {
|
| 145 |
+
"bytes": 1671853,
|
| 146 |
+
"sha256": "8831e4f1a044471340f7c0a83d7bd71306a5b867e95fd870f74d0c5308a904d5",
|
| 147 |
+
"source": "Qwen/Qwen3-8B tokenizer file (Apache-2.0 upstream)"
|
| 148 |
+
},
|
| 149 |
+
"mlx/MANIFEST.json": {
|
| 150 |
+
"bytes": 6950,
|
| 151 |
+
"sha256": "f87359d53dfd9f433a86495bd87759c1faeaf09b12cf07f9a06d4ec00e7619f5",
|
| 152 |
+
"source": "banked lane artifact"
|
| 153 |
+
},
|
| 154 |
+
"mlx/README.md": {
|
| 155 |
+
"bytes": 6201,
|
| 156 |
+
"sha256": "4d3a3dbc7ab1d32df93a72a1ca1e90b98af9c99c6a3ecd0d1af468d37fa7a278",
|
| 157 |
+
"source": "banked lane artifact"
|
| 158 |
+
},
|
| 159 |
+
"mlx/fermion_mlx/__init__.py": {
|
| 160 |
+
"bytes": 5004,
|
| 161 |
+
"sha256": "9d96d2fe71a8d1af20d78dc2d1cf016d01175462712aed36d493d82d3ffca87e",
|
| 162 |
+
"source": "banked lane artifact"
|
| 163 |
+
},
|
| 164 |
+
"mlx/fermion_mlx/__main__.py": {
|
| 165 |
+
"bytes": 8066,
|
| 166 |
+
"sha256": "aac000379d0c5cab04f5d6bbcb080e20c987cd87c60e8e0d3ad0a87e389937fc",
|
| 167 |
+
"source": "banked lane artifact"
|
| 168 |
+
},
|
| 169 |
+
"mlx/fermion_mlx/arch3.py": {
|
| 170 |
+
"bytes": 7541,
|
| 171 |
+
"sha256": "a831e8a24b2b9a986b393d1c316aac66a3399a082a57176254b2feeae032f8cd",
|
| 172 |
+
"source": "banked lane artifact"
|
| 173 |
+
},
|
| 174 |
+
"mlx/fermion_mlx/container.py": {
|
| 175 |
+
"bytes": 6059,
|
| 176 |
+
"sha256": "c5b352184ba9ddf9a3e424731226cb7dcb9901d3a1516c67d4557953b0101f2f",
|
| 177 |
+
"source": "banked lane artifact"
|
| 178 |
+
},
|
| 179 |
+
"mlx/fermion_mlx/gates.py": {
|
| 180 |
+
"bytes": 7454,
|
| 181 |
+
"sha256": "faf8634b0bd3afec5c4422decdbd8cc04fbb7f3426f70cdd6626fd6b177aa8b4",
|
| 182 |
+
"source": "banked lane artifact"
|
| 183 |
+
},
|
| 184 |
+
"mlx/fermion_mlx/mbatch.py": {
|
| 185 |
+
"bytes": 6492,
|
| 186 |
+
"sha256": "c9ec8d37f1cdc0554cda01cefbc435e67525779c79e0dac7fd760e7919e6fdd2",
|
| 187 |
+
"source": "banked lane artifact"
|
| 188 |
+
},
|
| 189 |
+
"mlx/fermion_mlx/model.py": {
|
| 190 |
+
"bytes": 12748,
|
| 191 |
+
"sha256": "9488f8194ce8be006ce05dfaf6118353de44005ce54f38f73a0908ccac2749da",
|
| 192 |
+
"source": "banked lane artifact"
|
| 193 |
+
},
|
| 194 |
+
"mlx/fermion_mlx/spec.py": {
|
| 195 |
+
"bytes": 11207,
|
| 196 |
+
"sha256": "969e946d5d2eeb63d44afa4c62dd2453d87fa14e83b936bc673482b59ee3b745",
|
| 197 |
+
"source": "banked lane artifact"
|
| 198 |
+
},
|
| 199 |
+
"mlx/fermion_mlx/trit_kernels.py": {
|
| 200 |
+
"bytes": 36881,
|
| 201 |
+
"sha256": "c56db6dd5e3743d19c54411f5f0b2dfef135f7805906a62cc4e40f1ee5e80f27",
|
| 202 |
+
"source": "banked lane artifact"
|
| 203 |
+
},
|
| 204 |
+
"mlx/merges.txt": {
|
| 205 |
+
"bytes": 1671853,
|
| 206 |
+
"sha256": "8831e4f1a044471340f7c0a83d7bd71306a5b867e95fd870f74d0c5308a904d5",
|
| 207 |
+
"source": "banked lane artifact"
|
| 208 |
+
},
|
| 209 |
+
"mlx/receipts/bench_8b_wbase_m5_16gb_memcapped.json": {
|
| 210 |
+
"bytes": 599,
|
| 211 |
+
"sha256": "9d3b153ec49dce92846fb9140fc38267ed04a08b5adcda91f2036ce94869b555",
|
| 212 |
+
"source": "banked lane artifact"
|
| 213 |
+
},
|
| 214 |
+
"mlx/receipts/pathgate_8b_cons3b25.json": {
|
| 215 |
+
"bytes": 634,
|
| 216 |
+
"sha256": "ff7f68d926d381a6c1a00f07cbb33ad5b3ac118d7b9dd997de0857cc1af40f71",
|
| 217 |
+
"source": "banked lane artifact"
|
| 218 |
+
},
|
| 219 |
+
"mlx/receipts/pathgate_8b_d25.json": {
|
| 220 |
+
"bytes": 631,
|
| 221 |
+
"sha256": "0de2306ce8c15c80fb776b2d73e6d056302d1d7367440e2c567d4370aaeef81c",
|
| 222 |
+
"source": "banked lane artifact"
|
| 223 |
+
},
|
| 224 |
+
"mlx/receipts/pathgate_8b_product.json": {
|
| 225 |
+
"bytes": 633,
|
| 226 |
+
"sha256": "c13e35c461c6e3a23fb2976d0f26f0b8dce114b12bf976a05cb19f11cc0746ea",
|
| 227 |
+
"source": "banked lane artifact"
|
| 228 |
+
},
|
| 229 |
+
"mlx/receipts/pathgate_8b_wbase.json": {
|
| 230 |
+
"bytes": 612,
|
| 231 |
+
"sha256": "50d6584a9ba2dae87bf502f2e637916a604be72db473fc89977fb8e1aaf3413b",
|
| 232 |
+
"source": "banked lane artifact"
|
| 233 |
+
},
|
| 234 |
+
"mlx/receipts/refgate_8b_cons3b25.json": {
|
| 235 |
+
"bytes": 1916,
|
| 236 |
+
"sha256": "d5a072dd5f796e8fa87269b0a44b3f525de627bb9ee79adeb7fcf973efcb00d7",
|
| 237 |
+
"source": "banked lane artifact (local absolute model path redacted to basename 2026-07-27 upload lane)"
|
| 238 |
+
},
|
| 239 |
+
"mlx/receipts/refgate_8b_d25.json": {
|
| 240 |
+
"bytes": 1918,
|
| 241 |
+
"sha256": "ece57b474473c07bb4064279b73a6f4aa199deb055b39e8166c86cd41a2a71b2",
|
| 242 |
+
"source": "banked lane artifact"
|
| 243 |
+
},
|
| 244 |
+
"mlx/receipts/refgate_8b_product.json": {
|
| 245 |
+
"bytes": 1919,
|
| 246 |
+
"sha256": "5b194b6fb22534c2c9f61cc48be7d5e1ace92b77cd3edb136ed88f94fd0bf2a3",
|
| 247 |
+
"source": "banked lane artifact"
|
| 248 |
+
},
|
| 249 |
+
"mlx/receipts/refgate_8b_wbase.json": {
|
| 250 |
+
"bytes": 1909,
|
| 251 |
+
"sha256": "851da3bb5c093182eb56532711dd9a95123ca44260e53de951b27c8b96eaf7a4",
|
| 252 |
+
"source": "banked lane artifact"
|
| 253 |
+
},
|
| 254 |
+
"mlx/receipts/specbench_8b_0p6b_m5.json": {
|
| 255 |
+
"bytes": 826,
|
| 256 |
+
"sha256": "c36ef09659d8c7690d53c23d065943103a95417890f8defbd01e629d7b5feeca",
|
| 257 |
+
"source": "banked lane artifact"
|
| 258 |
+
},
|
| 259 |
+
"mlx/receipts/specgate_8b_0p6b.json": {
|
| 260 |
+
"bytes": 705,
|
| 261 |
+
"sha256": "64bcb90fe965835bc78887d873d3cfd0677b76c6d72cda04a84e295efee7c10c",
|
| 262 |
+
"source": "banked lane artifact"
|
| 263 |
+
},
|
| 264 |
+
"mlx/requirements.txt": {
|
| 265 |
+
"bytes": 191,
|
| 266 |
+
"sha256": "1cf4d1fa581aaa5deae0568f88388c51166bbd69e7a89080b5a53488a7ef39b6",
|
| 267 |
+
"source": "banked lane artifact"
|
| 268 |
+
},
|
| 269 |
+
"mlx/tokenizer.json": {
|
| 270 |
+
"bytes": 11422654,
|
| 271 |
+
"sha256": "aeb13307a71acd8fe81861d94ad54ab689df773318809eed3cbe794b4492dae4",
|
| 272 |
+
"source": "banked lane artifact"
|
| 273 |
+
},
|
| 274 |
+
"mlx/tokenizer_config.json": {
|
| 275 |
+
"bytes": 9732,
|
| 276 |
+
"sha256": "d5d09f07b48c3086c508b30d1c9114bd1189145b74e982a265350c923acd8101",
|
| 277 |
+
"source": "banked lane artifact"
|
| 278 |
+
},
|
| 279 |
+
"mlx/vocab.json": {
|
| 280 |
+
"bytes": 2776833,
|
| 281 |
+
"sha256": "ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910",
|
| 282 |
+
"source": "banked lane artifact"
|
| 283 |
+
},
|
| 284 |
+
"receipts/acceptance_receipt_d25.json": {
|
| 285 |
+
"bytes": 9820,
|
| 286 |
+
"sha256": "037e3c009d6287ecd0f3dfcd38065236b832e63d7c0d64f5124fbde46a1816c3",
|
| 287 |
+
"source": "banked lane artifact"
|
| 288 |
+
},
|
| 289 |
+
"receipts/acceptance_receipt_s50.json": {
|
| 290 |
+
"bytes": 9772,
|
| 291 |
+
"sha256": "b46a60823b86f8f1f1f0d119492ae19b6a2ac667c5a98733721a6118eeed4da5",
|
| 292 |
+
"source": "banked lane artifact"
|
| 293 |
+
},
|
| 294 |
+
"receipts/dspec_cert_cons3_b25.json": {
|
| 295 |
+
"bytes": 6061,
|
| 296 |
+
"sha256": "086607c20ada9bc1ec251e217329e6ec5146f3826d6f4feab5e918e1b346240f",
|
| 297 |
+
"source": "banked lane artifact"
|
| 298 |
+
},
|
| 299 |
+
"receipts/dspec_cert_ifc_s50.json": {
|
| 300 |
+
"bytes": 700,
|
| 301 |
+
"sha256": "d5f8b115759e4abac8e9856a6c771ddb68c5eeadf9fc0af8f69062a349032bf8",
|
| 302 |
+
"source": "banked lane artifact"
|
| 303 |
+
},
|
| 304 |
+
"receipts/dspec_cert_ifc_s50.json.log": {
|
| 305 |
+
"bytes": 2130,
|
| 306 |
+
"sha256": "91fab18237b4b6fa465f9f90cb4a83c97918cc92d0def8f08b50fff45d7d475e",
|
| 307 |
+
"source": "banked lane artifact"
|
| 308 |
+
},
|
| 309 |
+
"receipts/export_receipts_d25.json": {
|
| 310 |
+
"bytes": 5890,
|
| 311 |
+
"sha256": "0a77b6c649bcbda96a52730f7daee2d8e47ee3b8c3c0485ee9b2db9d6cc0faf0",
|
| 312 |
+
"source": "banked lane artifact"
|
| 313 |
+
},
|
| 314 |
+
"receipts/export_receipts_s50.json": {
|
| 315 |
+
"bytes": 5959,
|
| 316 |
+
"sha256": "909f92d30e37f99ea78e2990904749213e1c4bbeaa01a0250f79d5a2b8f9b201",
|
| 317 |
+
"source": "banked lane artifact"
|
| 318 |
+
},
|
| 319 |
+
"tokenizer.json": {
|
| 320 |
+
"bytes": 11422654,
|
| 321 |
+
"sha256": "aeb13307a71acd8fe81861d94ad54ab689df773318809eed3cbe794b4492dae4",
|
| 322 |
+
"source": "Qwen/Qwen3-8B tokenizer file (Apache-2.0 upstream)"
|
| 323 |
+
},
|
| 324 |
+
"tokenizer_config.json": {
|
| 325 |
+
"bytes": 9732,
|
| 326 |
+
"sha256": "d5d09f07b48c3086c508b30d1c9114bd1189145b74e982a265350c923acd8101",
|
| 327 |
+
"source": "Qwen/Qwen3-8B tokenizer file (Apache-2.0 upstream)"
|
| 328 |
+
},
|
| 329 |
+
"vocab.json": {
|
| 330 |
+
"bytes": 2776833,
|
| 331 |
+
"sha256": "ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910",
|
| 332 |
+
"source": "Qwen/Qwen3-8B tokenizer file (Apache-2.0 upstream)"
|
| 333 |
+
}
|
| 334 |
+
}
|
| 335 |
+
}
|
NOTICE
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Neutrino — Fermion Research
|
| 2 |
+
Copyright 2026 Fermion Research
|
| 3 |
+
|
| 4 |
+
This product includes software and model artifacts developed by
|
| 5 |
+
Fermion Research, licensed under the Apache License, Version 2.0
|
| 6 |
+
(see LICENSE).
|
| 7 |
+
|
| 8 |
+
--------------------------------------------------------------------
|
| 9 |
+
THIRD-PARTY NOTICES
|
| 10 |
+
--------------------------------------------------------------------
|
| 11 |
+
|
| 12 |
+
Qwen3 (Alibaba Cloud / Qwen Team)
|
| 13 |
+
Copyright (c) Alibaba Cloud
|
| 14 |
+
Licensed under the Apache License, Version 2.0
|
| 15 |
+
https://huggingface.co/Qwen/Qwen3-8B
|
| 16 |
+
|
| 17 |
+
This model is a derivative of Qwen/Qwen3-8B. In particular, the following
|
| 18 |
+
files in this repository are redistributed VERBATIM from the upstream
|
| 19 |
+
Qwen3 release and are covered by Alibaba Cloud's copyright and the
|
| 20 |
+
Apache License 2.0:
|
| 21 |
+
|
| 22 |
+
tokenizer.json
|
| 23 |
+
tokenizer_config.json
|
| 24 |
+
vocab.json
|
| 25 |
+
merges.txt
|
| 26 |
+
|
| 27 |
+
The model architecture is stock Qwen3 geometry. The ternary
|
| 28 |
+
quantization-aware training, the TRTC v4 container format, the
|
| 29 |
+
post-training, the runtimes and the evaluation receipts are Fermion
|
| 30 |
+
Research's own work.
|
| 31 |
+
|
| 32 |
+
You may obtain a copy of the Apache License 2.0 at:
|
| 33 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 34 |
+
|
| 35 |
+
--------------------------------------------------------------------
|
| 36 |
+
|
| 37 |
+
llama.cpp (The ggml authors) — MIT License
|
| 38 |
+
|
| 39 |
+
The GGUF pack in gguf/ is produced for, and loaded by, Fermion
|
| 40 |
+
Research's fork of ggml-org/llama.cpp. That fork is distributed
|
| 41 |
+
separately under upstream's MIT license, NOT under this repository's
|
| 42 |
+
Apache-2.0 grant. Any patch or source in gguf/ that derives from
|
| 43 |
+
llama.cpp is MIT-licensed; see the fork's own LICENSE.
|
README.md
ADDED
|
@@ -0,0 +1,558 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
base_model: Qwen/Qwen3-8B
|
| 4 |
+
pipeline_tag: text-generation
|
| 5 |
+
library_name: transformers
|
| 6 |
+
tags:
|
| 7 |
+
- ternary
|
| 8 |
+
- sub-2-bit
|
| 9 |
+
- quantization
|
| 10 |
+
- trtc
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
> [!WARNING]
|
| 14 |
+
> **[MMLU-adapted, disclosed] This model was deliberately trained on the
|
| 15 |
+
> public MMLU test, validation, dev, and auxiliary-train splits** (all
|
| 16 |
+
> 115,700 public `cais/mmlu` rows) as part of its shipping recipe, and we
|
| 17 |
+
> say so up front. **Its MMLU-family scores (72.1-class) are not comparable
|
| 18 |
+
> to clean models and must not be quoted as benchmark results without this
|
| 19 |
+
> label.** Clean-lineage calibration anchors (internal checkpoints, not
|
| 20 |
+
> products): full-57 63.43 / 63.99, blind MMLU-CF 52.74 / 53.61. On the
|
| 21 |
+
> blind, contamination-free meter the adaptation bought approximately
|
| 22 |
+
> nothing (adapted brain W-7500: 54.32; sibling arm W-7000: 54.16; clean
|
| 23 |
+
> branch: 53.61) — which is the point, and the method + receipts are
|
| 24 |
+
> published in the eval-receipts repo.
|
| 25 |
+
|
| 26 |
+
# Neutrino-8B
|
| 27 |
+
|
| 28 |
+
An 8B chat model whose every transformer linear is stored **five-valued
|
| 29 |
+
(sub-2 bits per weight)** in a single 3.88 GB (3.61 GiB) container, and
|
| 30 |
+
which loads as a native `transformers` model in one `from_pretrained` call.
|
| 31 |
+
|
| 32 |
+
This is the ONE published 8B SKU, and it is the full release stack:
|
| 33 |
+
the MMLU-adapted brain (W step 7500, disclosed above) → the combined
|
| 34 |
+
behavior stage → the IF-1 instruction stack, snapshot-50 → the
|
| 35 |
+
verifiable-reward tool-calling stage, dose-25, locked 2026-07-26 after
|
| 36 |
+
passing all release guards plus the tool-calling primary and stretch
|
| 37 |
+
targets (VERDICT PASS, `results_bfcl_round2/r2_summary.json`). There is
|
| 38 |
+
no separate "clean" variant to download; the clean lineage exists only
|
| 39 |
+
as internal calibration anchors in the published tables. Cross-model
|
| 40 |
+
rows we publish are same-harness measurements, and every 72.1-class
|
| 41 |
+
knowledge cell carries the adaptation label wherever it appears.
|
| 42 |
+
|
| 43 |
+
- Container: `neutrino-8b_v4.bin` — TRTC v4, arch-3 (Qwen3 topology),
|
| 44 |
+
36 layers, hidden 4096, vocab 151936.
|
| 45 |
+
**Product bake landed (cons3-B25, the ship brain)**: baked 2026-07-27
|
| 46 |
+
from the consolidation-3 arm-B dose-25 QAT state — the ifc-s50 brain
|
| 47 |
+
plus 25 steps of a three-axis consolidation diet (stated-format math KD,
|
| 48 |
+
live-checker instruction following, gold function-calling). It buys the
|
| 49 |
+
GSM8K stated-format column and trades MMLU-Redux; the Redux
|
| 50 |
+
no-regression guard was breached and deliberately amended by the
|
| 51 |
+
founder on the record (`results_consolidation_3/RESULTS.md`).
|
| 52 |
+
3,875,404,812 bytes, sha256
|
| 53 |
+
`016c6f362ad237a8cf4043601efbc1e766574f76ea58691e78fdef51b4155fa0`.
|
| 54 |
+
<!-- Retirement record (receipts culture; internal archive locations are
|
| 55 |
+
not published). Superseded: earlier product bake — baked 2026-07-25
|
| 56 |
+
(banked 23:31Z) from the IF-stack snap50 QAT state (Manan lock 2026-07-25
|
| 57 |
+
23:51 IST), 3,875,404,812 bytes, sha256
|
| 58 |
+
fe85f93214376354f118a83235eeff6bc5d40e112474afacbd3e0310768b08d5, under
|
| 59 |
+
archive/if_stack_b800/export_v4/. Earlier still, same culture: interim
|
| 60 |
+
development container (stage-B step-800 bake, 3,875,404,812 bytes, sha256
|
| 61 |
+
be44129e579a1c39d713652a0a4f3dd046e61dab83524d023d2f238ef181b814), which
|
| 62 |
+
carried the pre-launch working slug at source. -->
|
| 63 |
+
- Inside: 252 packed trit linears + int8 embedding lanes (untied). Nothing
|
| 64 |
+
in the decode path is fp16/fp32 weights; scales are snapped to the
|
| 65 |
+
runtime grid at export.
|
| 66 |
+
- Base: [Qwen/Qwen3-8B](https://huggingface.co/Qwen/Qwen3-8B) (Apache-2.0),
|
| 67 |
+
ternary QAT + staged post-training by [Fermion Research](https://huggingface.co/fermionresearch).
|
| 68 |
+
Tokenizer: Qwen3-8B (shipped in this repo).
|
| 69 |
+
|
| 70 |
+
## Architecture
|
| 71 |
+
|
| 72 |
+
Geometry as read from the shipped container's header (bit-checked during
|
| 73 |
+
the GGUF conversion, `gguf/receipts/convert_8b.json`; byte budget from the
|
| 74 |
+
provenance-gated container walk, `results_model_anatomy/ANATOMY.json`):
|
| 75 |
+
|
| 76 |
+
| field | value |
|
| 77 |
+
|---|---|
|
| 78 |
+
| Parameters | 8,190,735,360 (6,945,767,424 ternary projection + 1,244,659,712 int8 embedding + 308,224 fp32 norm) |
|
| 79 |
+
| Decoder layers | 36 |
|
| 80 |
+
| Hidden width | 4,096 |
|
| 81 |
+
| Feed-forward width | 12,288, gated (SwiGLU) |
|
| 82 |
+
| Attention | grouped-query 4:1 — 32 query heads, 8 KV heads, head_dim 128 |
|
| 83 |
+
| Rotary embedding | full head width (rotary_dims 128), theta 1,000,000 |
|
| 84 |
+
| Normalization | RMSNorm, eps 1e-6, plus per-head Q/K RMSNorm in attention |
|
| 85 |
+
| Context length | 40,960 tokens (`max_pos`) |
|
| 86 |
+
| Vocabulary | 151,936 |
|
| 87 |
+
| Embeddings | untied — separate int8 input-embedding and lm_head records |
|
| 88 |
+
| KV cache | 144 KiB/token fp16 (288 KiB fp32): 0.60 GB @ 4k, 4.83 GB @ 32k |
|
| 89 |
+
|
| 90 |
+
Byte budget of the 3,875,404,812-byte container, by tensor class:
|
| 91 |
+
|
| 92 |
+
| lane | bytes | share |
|
| 93 |
+
|---|---:|---:|
|
| 94 |
+
| ternary weight lane (q/k/v/o/gate/up/down × 36 layers = 252 linears) | 2,604,662,784 | 67.2% |
|
| 95 |
+
| token embeddings, int8 (two untied 622 MB records) | 1,244,659,712 | 32.1% |
|
| 96 |
+
| per-row metadata (dims, scales, row sums) | 24,849,360 | 0.6% |
|
| 97 |
+
| norm vectors, fp32 (145 tensors) | 1,232,896 | 0.03% |
|
| 98 |
+
| container header | 60 | — |
|
| 99 |
+
|
| 100 |
+
Every layer costs exactly 72,351,744 weight-lane bytes (21.7% attention /
|
| 101 |
+
78.3% MLP). State occupancy across the 6.95B ternary weights: 62.63% zero /
|
| 102 |
+
18.68% plus / 18.69% minus (per-layer arrays in ANATOMY.json).
|
| 103 |
+
|
| 104 |
+
## Formats and artifacts
|
| 105 |
+
|
| 106 |
+
| artifact | bytes | sha256 |
|
| 107 |
+
|---|---:|---|
|
| 108 |
+
| `neutrino-8b_v4.bin` (TRTC v4 container, the file every runtime executes) | 3,875,404,812 | `016c6f362ad237a8cf4043601efbc1e766574f76ea58691e78fdef51b4155fa0` |
|
| 109 |
+
| `neutrino-8b_v4.tv4z` (lossless coded transport, 66.05% of raw) | 2,559,836,297 | `83ec7a52bd6733e66fb546e89b9c6aa8b3411af7f648e9c0b2bb56ba50aac4df` |
|
| 110 |
+
| `gguf/neutrino-8b-fv5.gguf` (llama.cpp-fork pack, bit-checked vs the container) | 4,093,015,136 | `7dda995047dc70540d2d83f2c464dd118fff62eef1aab36ae71ceb3cc972e5d0` |
|
| 111 |
+
|
| 112 |
+
The tv4z transport decodes back to the container byte-exactly (md5+sha
|
| 113 |
+
round-trip identity, `results_tv4z_8b_ship/RESULTS.md`; 17 s encode / 6 s
|
| 114 |
+
decode on an M5). Three distribution surfaces read these artifacts:
|
| 115 |
+
|
| 116 |
+
1. **pip engine** (`pip install fermion`) — the one-command door; pulls the
|
| 117 |
+
container and the platform-matching `bin/` runtime from this repo.
|
| 118 |
+
Measured: 24.94 tok/s CPU-only on an Apple M5, 9 threads.
|
| 119 |
+
2. **GGUF pack + our llama.cpp fork** (`gguf/`) — ecosystem door for
|
| 120 |
+
llama.cpp / ollama / LM Studio, CPU + CUDA. Measured: 30.7 tok/s on an
|
| 121 |
+
NVIDIA L4 at full offload, 4.68 GiB VRAM @ 4k context (fits 8 GB cards).
|
| 122 |
+
3. **MLX pack** (`mlx/`) — Python-native Apple-silicon runtime with custom
|
| 123 |
+
Metal kernels. Measured: 25.0 tok/s median on a 16 GB M5 under a 6 GiB
|
| 124 |
+
memory cap.
|
| 125 |
+
|
| 126 |
+
## Quickstart
|
| 127 |
+
|
| 128 |
+
```bash
|
| 129 |
+
pip install fermion
|
| 130 |
+
printf 'In one sentence, why is the sky blue?\n/exit\n' | \
|
| 131 |
+
fermion chat --model fermionresearch/Neutrino-8B --max-new 48
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
Or pure `transformers`:
|
| 135 |
+
|
| 136 |
+
```python
|
| 137 |
+
import fermion # registers the trtc_v4 model type
|
| 138 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 139 |
+
|
| 140 |
+
model = AutoModelForCausalLM.from_pretrained("fermionresearch/Neutrino-8B")
|
| 141 |
+
tokenizer = AutoTokenizer.from_pretrained("fermionresearch/Neutrino-8B")
|
| 142 |
+
inputs = tokenizer.apply_chat_template([{"role": "user", "content": "hi"}],
|
| 143 |
+
add_generation_prompt=True,
|
| 144 |
+
enable_thinking=False,
|
| 145 |
+
return_tensors="pt", return_dict=True)
|
| 146 |
+
out = model.generate(**inputs, max_new_tokens=256) # generation_config: greedy
|
| 147 |
+
n = inputs["input_ids"].shape[1]
|
| 148 |
+
print(tokenizer.decode(out[0][n:], skip_special_tokens=True))
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
`import fermion` must come first: it is what registers the `trtc_v4` model
|
| 152 |
+
type, and without it `AutoTokenizer`/`AutoModelForCausalLM` cannot read this
|
| 153 |
+
repo's `config.json`. `return_dict=True` is not optional either on
|
| 154 |
+
transformers 4.56 and newer, where `apply_chat_template` returns a mapping
|
| 155 |
+
rather than a bare tensor.
|
| 156 |
+
|
| 157 |
+
`generation_config.json` in this repo is deliberately **greedy with no
|
| 158 |
+
repetition penalty and no top-k**: that is the setting the published MMLU,
|
| 159 |
+
GSM8K, IFEval and BFCL numbers were measured at, so a bare `generate()`, an
|
| 160 |
+
`lm-eval` run and `fermion generate` all reproduce each other. The
|
| 161 |
+
conversational and long-form settings are one argument away — see
|
| 162 |
+
[Recommended settings](#recommended-settings-by-use-case).
|
| 163 |
+
|
| 164 |
+
GGUF surface (build our fork once, then standard llama.cpp tooling —
|
| 165 |
+
full instructions in `gguf/README.md`):
|
| 166 |
+
|
| 167 |
+
```bash
|
| 168 |
+
git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp
|
| 169 |
+
git checkout d67c0b4107112e4790774c3a8169e2e3eb24643b && git am fv5.patch
|
| 170 |
+
cmake -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build -j --target llama-cli
|
| 171 |
+
./build/bin/llama-cli -m neutrino-8b-fv5.gguf -p "Why is the sky blue?" -n 256 --temp 0
|
| 172 |
+
```
|
| 173 |
+
|
| 174 |
+
MLX surface (Apple silicon — full instructions in `mlx/README.md`):
|
| 175 |
+
|
| 176 |
+
```bash
|
| 177 |
+
pip install -r mlx/requirements.txt
|
| 178 |
+
python -m fermion_mlx --model neutrino-8b_v4.bin --mode chat \
|
| 179 |
+
--tokenizer . --prompt "Why is the sky blue?"
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
The pip path is the **reference torch path**: correctness-gated (bit-packed
|
| 183 |
+
weights at rest, 0 greedy mismatches vs the native runtime over 768 tokens;
|
| 184 |
+
receipt in the eval-receipts repo), honest about speed — it is NOT the fast
|
| 185 |
+
path. The fast path ships **in this repo** under `bin/` (see below).
|
| 186 |
+
|
| 187 |
+
Verified end-to-end in one fresh CPU container (Modal, cpu=8, 24 GiB, no
|
| 188 |
+
GPU): wheel install -> container sha gate -> piped `chat` produced a
|
| 189 |
+
real answer; receipt: `receipts/acceptance_cpu_receipt_20260724.json`
|
| 190 |
+
(run under the pre-rename working slug; same code path, wheel renamed
|
| 191 |
+
`fermion` and repo id renamed `Neutrino-8B` 2026-07-24 per the release
|
| 192 |
+
runbook). Peak resident memory for load+chat was under 8 GiB (bit-packed
|
| 193 |
+
weights). Acceptance rerun against the final product container is a
|
| 194 |
+
publish gate (RELEASE_RUNBOOK §3).
|
| 195 |
+
|
| 196 |
+
## Recommended settings, by use case
|
| 197 |
+
|
| 198 |
+
Swept on the predecessor ifc-s50 container (`39787a2e…`); the sampling sweep was NOT re-run for `016c6f36…` across four prompt slices — chat,
|
| 199 |
+
factual, structured/tool-shaped, long-form — at four temperature x
|
| 200 |
+
repetition-penalty cells (`handoffs/results_recommended_configs`). `—` means
|
| 201 |
+
leave it alone; a number there would be one we did not measure.
|
| 202 |
+
|
| 203 |
+
| use case | temperature | top-p | top-k | repetition penalty | penalty window | max new tokens | stop |
|
| 204 |
+
|---|---|---|---|---|---|---|---|
|
| 205 |
+
| **conversation / assistant** | **0.01** on the C binary · **0** on torch | 1.0 | not exposed | 1.05 | 256 | **512** | EOS `151645` |
|
| 206 |
+
| **tool calling / structured output** | **0** (greedy) | — | — | **1.0 (off)** | — | 256 | EOS `151645` |
|
| 207 |
+
| **long-form prose** | **0.7** | 0.95 | not exposed | 1.05 | 256 | **1024** | EOS `151645` |
|
| 208 |
+
| **deterministic / scriptable / benchmarks** | **0** (greedy) | — | — | **1.0 (off)** | — | 256 | EOS `151645` |
|
| 209 |
+
|
| 210 |
+
- **Structured output is an 8B capability.** On tool-shaped prompts this
|
| 211 |
+
model emitted whole-reply, fence-free, parseable JSON **8/8 in every cell
|
| 212 |
+
tried, including at temperature 0.7** — the format is not sensitive to the
|
| 213 |
+
sampler here. Turn the repetition penalty **off** for it anyway: JSON
|
| 214 |
+
requires repeating `"`, `:` and `{`, and a penalty over that punctuation is
|
| 215 |
+
the one place it can actively break the format. The 0.6B-Chat scored 0/8 on
|
| 216 |
+
the same prompts at every setting; route schema-shaped work here.
|
| 217 |
+
- **Why 512 and 1024 token budgets.** These are measured, not taste. At a
|
| 218 |
+
150-token cap the chat slice terminated 0/2 in every cell and every
|
| 219 |
+
long-form generation hit the cap. The 8B is verbose; give it room.
|
| 220 |
+
- **The repetition penalty is insurance, not a fix.** Loop rate was **0.000
|
| 221 |
+
in all four cells**, with and without it. It is in the conversation row to
|
| 222 |
+
match what the behaviour receipts were graded at, and it is off everywhere
|
| 223 |
+
reproducibility matters.
|
| 224 |
+
- **`top_p 0.95` on the long-form row is the one unmeasured number here** —
|
| 225 |
+
we swept top-p 1.0 only. It is the conventional pairing for temperature
|
| 226 |
+
0.7, flagged as convention rather than measurement.
|
| 227 |
+
- **Top-k is not exposed on any runtime we ship**, and this repo's
|
| 228 |
+
`generation_config.json` sets none. Top-p is the only shortlist knob.
|
| 229 |
+
- **`--stop-id 151645` is mandatory on the C binary.** Without it the binary
|
| 230 |
+
runs in fixed-length "race mode", ignores EOS and emits exactly the token
|
| 231 |
+
count you asked for.
|
| 232 |
+
- **Speculative decoding and the exactness claim.** Token-identical output is
|
| 233 |
+
certified under **greedy** decoding (`--temperature 0`). With sampling,
|
| 234 |
+
drafted output is drawn from the same distribution as undrafted output —
|
| 235 |
+
the pip path routes through the transformers rejection-sampling correction
|
| 236 |
+
— but individual tokens will differ. The research CUDA engine and the MLX
|
| 237 |
+
`--spec` mode use argmax-match acceptance and are greedy-only.
|
| 238 |
+
- Exactly one repetition penalty ever applies on the torch path: the
|
| 239 |
+
`fermion` CLI pins `repetition_penalty=1.0` in its `generate()` call before
|
| 240 |
+
installing its own windowed processor, so HF's full-context processor is
|
| 241 |
+
never built alongside it. If you assemble your own call, do the same — two
|
| 242 |
+
penalties of 1.05 compose to a measured 1.1025.
|
| 243 |
+
|
| 244 |
+
## `bin/` — prebuilt native runtimes (the fast path)
|
| 245 |
+
|
| 246 |
+
This repo bundles the prebuilt, closed-source `fermion-run` binaries next
|
| 247 |
+
to the weights (the GGUF-ecosystem pattern; HF permits arbitrary binaries):
|
| 248 |
+
|
| 249 |
+
| Binary | Platform | Backend |
|
| 250 |
+
|---|---|---|
|
| 251 |
+
| `bin/fermion-run-macos-arm64` | macOS arm64 (M1+) | CPU — NEON dotprod, zero dylib deps (Metal runtime is a week-one follow-up) |
|
| 252 |
+
| `bin/fermion-run-linux-x64` | Linux x86-64 | CPU — AVX2 baseline, fully static ELF |
|
| 253 |
+
| `bin/fermion-run-linux-cuda` | Linux x86-64 + NVIDIA | CUDA — **coming soon** (not yet staged; today's CUDA fallback is the pip torch path) |
|
| 254 |
+
|
| 255 |
+
Every binary ships with a `<name>.sha256` sidecar; verify before running:
|
| 256 |
+
|
| 257 |
+
```bash
|
| 258 |
+
shasum -a 256 -c bin/fermion-run-macos-arm64.sha256
|
| 259 |
+
```
|
| 260 |
+
|
| 261 |
+
`pip install fermion` remains the one door: the CLI downloads the model and
|
| 262 |
+
the platform-matching binary from THIS repo and shells out to it for the
|
| 263 |
+
fast path (the torch reference path stays as the fallback). Until the
|
| 264 |
+
auto-binary CLI lands, the documented one-command binary path is in
|
| 265 |
+
`bin/README.md`.
|
| 266 |
+
|
| 267 |
+
Shipped-binary speed, with venue: **24.94 tok/s** CPU-only on an M5
|
| 268 |
+
MacBook (the macOS arm64 binary, 9 threads, 32-token greedy, banked
|
| 269 |
+
2026-07-25) and **25.0 tok/s median** via the MLX pack on a 16 GB M5
|
| 270 |
+
under a 6 GiB memory cap (`mlx/receipts/`). These are shipped-binary
|
| 271 |
+
numbers; our research-harness results are reported separately and never
|
| 272 |
+
conflated with them.
|
| 273 |
+
|
| 274 |
+
Binaries are closed-source and covered by the EULA line below; speed
|
| 275 |
+
receipts (venue+version+date on every number) live in the eval-receipts
|
| 276 |
+
repo. `bin/MANIFEST.json` in this tree is the staging manifest: it names
|
| 277 |
+
exactly which binaries land at upload time and their provenance.
|
| 278 |
+
|
| 279 |
+
## Benchmarks
|
| 280 |
+
|
| 281 |
+
Everything here is **non-thinking**: all our numbers, and every
|
| 282 |
+
same-harness cell we publish for another model, are measured with
|
| 283 |
+
thinking disabled. Release battery: the shipped brain (IF-stack
|
| 284 |
+
snapshot-50 + verifiable-reward tool-calling dose-25, "d25"), graded
|
| 285 |
+
2026-07-26 on our house harness; all five release guards (termination,
|
| 286 |
+
IFEval floor, Redux, knowledge-delta, C4 ppl) plus the tool-calling
|
| 287 |
+
primary and stretch targets passed before the container was baked
|
| 288 |
+
(VERDICT PASS, `results_bfcl_round2/r2_summary.json`). A receipt exists
|
| 289 |
+
for every cell (venue, version, date) in the eval-receipts repo.
|
| 290 |
+
|
| 291 |
+
Release battery of the ship brain, protocol on every row:
|
| 292 |
+
|
| 293 |
+
| Meter | Score | Protocol |
|
| 294 |
+
|---|---:|---|
|
| 295 |
+
| MMLU full-57 | 72.1 **[MMLU-adapted, disclosed — see banner]** | 5-shot likelihood, 14,042 items |
|
| 296 |
+
| MMLU-Redux | 67.84 | generative, thinking off |
|
| 297 |
+
| IFEval, prompt-strict | 73.17 | generative, thinking off |
|
| 298 |
+
| IFEval, instruction-strict | 80.22 | same run, per-instruction grading |
|
| 299 |
+
| IFEval, prompt-loose | 76.31 | same run, loose extraction |
|
| 300 |
+
| BFCL v3 | 65.31 | macro over 13 subsets, bfcl-eval 2025.10.27.1, thinking off |
|
| 301 |
+
| GSM8K, flexible-extract | 51.00 | 0-shot generative, greedy, 256-token cap |
|
| 302 |
+
| GSM8K, strict/stated format | 49.33 | same run, answer must appear in the stated format |
|
| 303 |
+
|
| 304 |
+
Same-harness reference points for the strongest same-byte-class open
|
| 305 |
+
model (Ternary-Bonsai-8B, measured by us on identical protocols):
|
| 306 |
+
MMLU 65.75 · MMLU-Redux 71.47 · IFEval prompt-strict 83.65 · BFCL v3
|
| 307 |
+
71.45 · GSM8K flexible 35.00 · **GSM8K stated-format 39.67**. It leads us
|
| 308 |
+
on instruction following, tool calling and MMLU-Redux; we lead on MMLU
|
| 309 |
+
full-57, on GSM8K flexible extraction (51.00 vs 35.00) and — new with this
|
| 310 |
+
brain — on **GSM8K stated-format extraction, 49.33 vs 39.67**, the one
|
| 311 |
+
column the category leader previously held. (Its MMLU cell is
|
| 312 |
+
clean-lineage; ours carries the adaptation banner.)
|
| 313 |
+
|
| 314 |
+
### Reproducing the battery
|
| 315 |
+
|
| 316 |
+
The model registers as a native `transformers` model (`import fermion`),
|
| 317 |
+
so the public harnesses run directly. MMLU / GSM8K were graded with
|
| 318 |
+
lm-eval:
|
| 319 |
+
|
| 320 |
+
```python
|
| 321 |
+
import fermion, lm_eval
|
| 322 |
+
from lm_eval.models.huggingface import HFLM
|
| 323 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 324 |
+
|
| 325 |
+
model = AutoModelForCausalLM.from_pretrained("fermionresearch/Neutrino-8B")
|
| 326 |
+
tok = AutoTokenizer.from_pretrained("fermionresearch/Neutrino-8B")
|
| 327 |
+
lm = HFLM(pretrained=model, tokenizer=tok)
|
| 328 |
+
lm_eval.simple_evaluate(model=lm, tasks=["mmlu"], num_fewshot=5)
|
| 329 |
+
lm_eval.simple_evaluate(model=lm, tasks=["gsm8k"], num_fewshot=0) # flexible-extract
|
| 330 |
+
```
|
| 331 |
+
|
| 332 |
+
IFEval, MMLU-Redux, and BFCL v3 were graded with evalscope 1.4.2 against
|
| 333 |
+
a vLLM OpenAI-compatible endpoint serving this model, with
|
| 334 |
+
`chat_template_kwargs: {"enable_thinking": false}` and
|
| 335 |
+
`bfcl-eval==2025.10.27.1` pinned:
|
| 336 |
+
|
| 337 |
+
```bash
|
| 338 |
+
pip install evalscope==1.4.2 bfcl-eval==2025.10.27.1
|
| 339 |
+
evalscope eval --model Neutrino-8B --api-url http://127.0.0.1:8000/v1 \
|
| 340 |
+
--datasets ifeval mmlu_redux bfcl_v3
|
| 341 |
+
```
|
| 342 |
+
|
| 343 |
+
The exact per-leg configs (context lengths 8k/16k, generation configs,
|
| 344 |
+
the BFCL 13-subset list) and every raw report JSON are in the
|
| 345 |
+
eval-receipts repo.
|
| 346 |
+
|
| 347 |
+
| Meter | Neutrino-8B | Same-harness comparison | Receipt |
|
| 348 |
+
|---|---:|---|---|
|
| 349 |
+
| MMLU full-57 (5-shot likelihood, n=14,042) | **72.1** **[MMLU-adapted, disclosed — see banner; not comparable to clean models]** | Ternary-Bonsai-8B 65.75 · Gemma-4-E4B 70.57 (non-thinking) — both measured by us on the identical harness | eval-receipts repo, d25 grade (`full57_25/`) |
|
| 350 |
+
| MMLU-CF (blind, contamination-free, val-10K) | 54.32 — the W-7500 brain this stack is trained from; the shipped snapshot's own CF cell is not yet measured (behavior stages moved full-57 by ≤0.2 pp, ns) | Ternary-Bonsai-8B 54.31 — dead even. Clean-lineage anchors: 52.74 / 53.61 | eval-receipts repo |
|
| 351 |
+
| TruthfulQA mc1 / mc2 (0-shot likelihood) | 34.52 / 53.57 — clean-lineage anchor checkpoint (internal, not this SKU) | Ternary-Bonsai-8B 32.19 / 51.12 — both wins significant (paired McNemar, p=0.0448 / p=0.0079) | eval-receipts repo |
|
| 352 |
+
| GSM8K flexible-extract (0-shot generative, greedy, 256-token cap, fixed 300-item subset) | **51.00** (shipped brain, cons3-B25) | Ternary-Bonsai-8B 35.0 on the byte-identical meter | eval-receipts repo |
|
| 353 |
+
| GSM8K stated-format (same run, answer must land in the prescribed format; 1024-token cap) | **49.33** (shipped brain, cons3-B25; was 29.33 on ifc-s50) | Ternary-Bonsai-8B **39.67** on the byte-identical meter — this column is ours as of this brain | eval-receipts repo |
|
| 354 |
+
| MMLU full-57 — clean-lineage calibration anchors (internal checkpoints, NOT products) | 63.43 (stage-B step-800) / 63.99 (decay-6.5K research branch) | — | eval-receipts repo |
|
| 355 |
+
|
| 356 |
+
Don't-regress guards, held at release (guards, not headlines): GSM8K
|
| 357 |
+
generative termination 0.51 — above the bf16 teacher's 0.42 anchor on the
|
| 358 |
+
same raw-completion meter, but now BELOW the snapshot-50 stage base (0.63)
|
| 359 |
+
and the pre-stack base (0.593); the consolidation stage traded termination
|
| 360 |
+
rate for stated-format accuracy and we state that plainly — and C4
|
| 361 |
+
perplexity 21.48 (ifc-s50 21.46, snapshot-50 21.47, base 21.40 — flat
|
| 362 |
+
across every behavior stage). The
|
| 363 |
+
raw-completion protocol suppresses EOS across model families, ours and
|
| 364 |
+
fp16 alike; chat-templated serving terminates normally.
|
| 365 |
+
|
| 366 |
+
The trained-on-public-MMLU disclosure travels with every 72.1-class number,
|
| 367 |
+
here and in every table this model appears in. The clean-lineage rows are
|
| 368 |
+
calibration anchors for the contamination-fingerprint method (gap =
|
| 369 |
+
original MMLU minus blind MMLU-CF; clean band 10.4–10.7 pp, adapted band
|
| 370 |
+
15.9–17.8 pp), published in full in the eval-receipts repo.
|
| 371 |
+
|
| 372 |
+
Tool calling is a trained, measured capability of this brain, stated
|
| 373 |
+
here as our own absolute numbers (no comparison rows on this axis):
|
| 374 |
+
two stacked verifiable-reward stages built this brain. The
|
| 375 |
+
tool-calling stage lifted BFCL v3 13-subset macro 42.15 → 62.60
|
| 376 |
+
(bfcl-eval 2025.10.27.1, our harness, thinking off); the
|
| 377 |
+
instruction-following stage on top took IFEval prompt-strict 67.51 →
|
| 378 |
+
72.75 while BFCL rose again to 63.67 and MMLU-Redux to 70.65 — a
|
| 379 |
+
validated upgrade on every measured axis, no guard traded (receipts:
|
| 380 |
+
`results_bfcl_round2/r2_summary.json` + `results_ifeval_bump/` A1 walk).
|
| 381 |
+
A third, **consolidation** stage then took IFEval prompt-strict to
|
| 382 |
+
**73.17**, BFCL to **65.31** and GSM8K stated-format from 29.33 to
|
| 383 |
+
**49.33** — and, unlike the first two, this one **did** trade a guard:
|
| 384 |
+
MMLU-Redux fell to **67.84** and GSM8K flexible extraction to **51.00**.
|
| 385 |
+
That trade was made deliberately and is disclosed here rather than
|
| 386 |
+
buried (receipts: `results_consolidation_3/RESULTS.md`).
|
| 387 |
+
Honest read: instruction-following and tool-calling absolutes are still
|
| 388 |
+
below where we want them against the class, and instruction-following
|
| 389 |
+
upgrades continue through week one; the numbers live — like everything
|
| 390 |
+
else — in the eval-receipts repo.
|
| 391 |
+
|
| 392 |
+
<!-- [SHIP-BRAIN RE-PIN 016c6f36… — publish gates] Card re-pinned from
|
| 393 |
+
the ifc-s50 bake to the cons3-B25 ship brain (FOUNDER-AUTHORISED PROMOTE
|
| 394 |
+
2026-07-27 ~19:50 IST over a deliberate MMLU-Redux guard amendment:
|
| 395 |
+
GSM8K stated 49.33 / flexible 51.00 / BFCL 65.31 / IFEval-ps 73.17 /
|
| 396 |
+
redux 67.84 / full57 72.084 / c4 21.475 / term 0.5067, receipts
|
| 397 |
+
results_consolidation_3/RESULTS.md). CARRY-OVER GATES re-run against THIS
|
| 398 |
+
container 2026-07-27: (1) pip acceptance rerun — NOT YET RUN, still
|
| 399 |
+
outstanding; (2) MLX path+ref gate — DONE, both PASS; (3) GGUF re-cut +
|
| 400 |
+
token-identity gate — DONE; (4) dspec cert — DONE, PASS 27,648 tokens
|
| 401 |
+
0 mismatches.
|
| 402 |
+
Each is a fan-out leg of the ship-brain bake; swap its receipt in and
|
| 403 |
+
regenerate MANIFEST as it lands. -->
|
| 404 |
+
|
| 405 |
+
## Weights: get them and verify them
|
| 406 |
+
|
| 407 |
+
The container ships in this repository. Fetch it and check the digest —
|
| 408 |
+
`MANIFEST.json` carries the same sha256, and `fermion info` verifies it for
|
| 409 |
+
you automatically:
|
| 410 |
+
|
| 411 |
+
```bash
|
| 412 |
+
hf download fermionresearch/Neutrino-8B neutrino-8b_v4.bin --local-dir .
|
| 413 |
+
shasum -a 256 neutrino-8b_v4.bin # must print 016c6f362...b4155fa0
|
| 414 |
+
```
|
| 415 |
+
|
| 416 |
+
<!-- Retirement record (receipts culture; internal archive locations are not
|
| 417 |
+
published). Superseded containers, by sha256:
|
| 418 |
+
fe85f9321...768b08d5 — earlier product bake, retired 2026-07-26.
|
| 419 |
+
be44129e5...81b814 — interim stage-B step-800 development container,
|
| 420 |
+
retired earlier; carried the pre-launch working slug.
|
| 421 |
+
Their acceptance/export receipts are held out of this published tree, in
|
| 422 |
+
packaging/retired_receipts_not_published/, because this repo ships only
|
| 423 |
+
016c6f362...b4155fa0 and provenance for a brain we do not ship is misleading.
|
| 424 |
+
39787a2e1...2007a3be — ifc-s50 ship brain, retired 2026-07-27 by the
|
| 425 |
+
consolidation-3 arm-B-step-25 promote.
|
| 426 |
+
-->
|
| 427 |
+
|
| 428 |
+
Product bake provenance: exported on a Modal CPU container from the
|
| 429 |
+
archived consolidation-3 arm-B dose-25 QAT state — the ship-brain
|
| 430 |
+
lineage is IF-stack snapshot-50 → round-2 dose-25 verifiable-reward
|
| 431 |
+
tool-calling (step-25 sha256 `752d01d5…3abbc`) → IF-checker stage dose-50,
|
| 432 |
+
VALIDATED-BUMP (step-50 sha256 `e62a1199…0fb6a9`) → **consolidation-3
|
| 433 |
+
arm B dose-25** (step-25 sha256 `09c3f36a…41b8`,
|
| 434 |
+
32,768,963,587 bytes, banked 2026-07-27 11:40Z —
|
| 435 |
+
`results_consolidation_3/RESULTS.md`) with the banked
|
| 436 |
+
exporter (`export_trit_c_model.py`, pinned command + flags recorded in
|
| 437 |
+
the export receipt; quantizer parameters are not published); full receipt at S3
|
| 438 |
+
`archive/bfcl_round2/export_v4/export_receipts.json` (the in-tree
|
| 439 |
+
`receipts/` acceptance + export copies are still the RETIRED s50 bake's —
|
| 440 |
+
swap them, plus the fresh acceptance receipt, into `receipts/` and
|
| 441 |
+
regenerate this tree's MANIFEST after the §3 acceptance rerun).
|
| 442 |
+
<!-- RETIRED 2026-07-26 s50 provenance (kept per receipts culture): IF-stack
|
| 443 |
+
snap50 QAT state sha256
|
| 444 |
+
f31195a8e2fcdeff0c42b9e3968a8259d44e647e93f601d77d74b5980bffa2a2,
|
| 445 |
+
32,768,963,587 bytes, durably archived at
|
| 446 |
+
archive/if_stack_b800/snap50/last_state.pt; bake receipt
|
| 447 |
+
archive/if_stack_b800/export_v4/export_receipts.json; exporter flags
|
| 448 |
+
identical. Earlier retirement (interim): QAT state sha256
|
| 449 |
+
a1ffcf409c0330345e240397093f61654ad013ed8935a62a0a19164d99daa1f9,
|
| 450 |
+
32,768,963,587 bytes; exporter flags identical. -->
|
| 451 |
+
|
| 452 |
+
## Runtime matrix
|
| 453 |
+
|
| 454 |
+
Single-stream decode, one artifact across every row (venue + date on each
|
| 455 |
+
number in the eval-receipts repo):
|
| 456 |
+
|
| 457 |
+
| Platform | Surface | Rate | Memory |
|
| 458 |
+
|---|---|---:|---|
|
| 459 |
+
| H100 80 GB | research stack, drafted (0.6B draft, certified exact) | 763 tok/s (counting; 402-500 tok/s on other prompt classes) | — |
|
| 460 |
+
| H100 80 GB | research stack, plain greedy | 396 tok/s | — |
|
| 461 |
+
| NVIDIA L4 | GGUF pack via our CUDA fork, full offload | 30.7 tok/s | 4.68 GiB @ 4k ctx |
|
| 462 |
+
| Apple M5, 16 GB | MLX pack (median, 3 runs) | 25.0 tok/s | 3.67 GiB peak, 6 GiB cap |
|
| 463 |
+
| Apple M5 | `bin/` native binary, CPU only, 9 threads | 24.94 tok/s | < 8 GiB resident |
|
| 464 |
+
|
| 465 |
+
## Speculative decoding (certified exact)
|
| 466 |
+
|
| 467 |
+
This repo's 8B pairs with [Neutrino-0.6B-Chat](https://huggingface.co/fermionresearch/Neutrino-0.6B-Chat)
|
| 468 |
+
as a **draft model** for speculative decoding: the 0.6B proposes tokens,
|
| 469 |
+
the 8B verifies them, and the output is **token-identical to plain 8B
|
| 470 |
+
greedy decode** — speculation changes throughput, never the answer. Both
|
| 471 |
+
models are the same TRTC v4 container format and run on the same
|
| 472 |
+
`fermion-run` binaries, so the pairing needs no extra conversion.
|
| 473 |
+
|
| 474 |
+
| Meter | Value | Note |
|
| 475 |
+
|---|---:|---|
|
| 476 |
+
| Certification (exact-match tokens) | **27,648 spec tokens, 0 mismatches** (receipt `receipts/dspec_cert_cons3_b25.json`, re-run against THIS container 2026-07-27; 54/54 arm x prompt cells identical) | draft+verify vs plain greedy, N tokens, 0 mismatches required |
|
| 477 |
+
| Counting-style prompts | **×1.92 (763 tok/s vs 397 plain)** | measured speedup vs plain 8B greedy |
|
| 478 |
+
| Factual / short-answer prompts | **×1.26 (500 tok/s)** | " |
|
| 479 |
+
| Code | ×1.10 (437 tok/s) | " |
|
| 480 |
+
| Prose continuation | ×1.06 (422 tok/s) | " |
|
| 481 |
+
| Conversational explanation | ×1.01 (402 tok/s) | " |
|
| 482 |
+
| Venue | H100, 3-round median, dynamic-k controller, 2026-07-27 | same-moment, same-machine protocol (see VERIFY) |
|
| 483 |
+
|
| 484 |
+
Mechanics (measured on this brain, 2026-07-27 cert run): on counting
|
| 485 |
+
prompts the 8B still accepts 100% of drafted tokens; on factual prompts
|
| 486 |
+
acceptance is 86.9% and on chat-factual 90.3%; it falls to 81.4% on prose
|
| 487 |
+
and 50.0% on technical explanation. Acceptance decays with draft length
|
| 488 |
+
everywhere except counting, so the dynamic-k controller shortens its
|
| 489 |
+
drafts there and the gain thins. **This brain drafts less well than
|
| 490 |
+
ifc-s50 did** on everything but counting — the speedup table above is
|
| 491 |
+
lower across four of five rows, and that is a throughput regression we
|
| 492 |
+
state rather than carry over the old numbers.
|
| 493 |
+
|
| 494 |
+
Receipts: `receipts/dspec_cert_cons3_b25.json` (banked, ship brain).
|
| 495 |
+
Honest framing: speculative decoding is a **throughput** feature with a
|
| 496 |
+
correctness certificate; it does not change quality, and the speedup is
|
| 497 |
+
prompt-dependent — the five rows above are the honest spread, not a
|
| 498 |
+
single headline number.
|
| 499 |
+
|
| 500 |
+
## Limitations
|
| 501 |
+
|
| 502 |
+
- Chat-tuned for short helpful answers. Tool calling is trained and
|
| 503 |
+
measured (BFCL v3 13-subset macro 65.31, our harness — see Benchmarks)
|
| 504 |
+
but carries no guarantees at this size; instruction-following and
|
| 505 |
+
tool-calling absolutes are below where we want them against the class,
|
| 506 |
+
and instruction-following upgrades continue through week one.
|
| 507 |
+
- This is a non-thinking model: it ships and is evaluated with thinking
|
| 508 |
+
disabled (`enable_thinking=False`).
|
| 509 |
+
- MMLU-family numbers carry the [MMLU-adapted, disclosed] label by
|
| 510 |
+
construction — never quote this model's 72.1-class cells without it.
|
| 511 |
+
The blind MMLU-CF cell and the clean-lineage anchors are the honest
|
| 512 |
+
cross-model meters.
|
| 513 |
+
- Against the strongest same-byte-class open model on identical
|
| 514 |
+
protocols, instruction following and tool calling trail: their 83.65
|
| 515 |
+
IFEval prompt-strict and 71.45 BFCL v3 vs our 73.17 and 65.31. Their
|
| 516 |
+
MMLU-Redux 71.47 also leads our 67.84.
|
| 517 |
+
- GSM8K stated-format and flexible extraction now sit close together
|
| 518 |
+
(49.33 vs 51.00, same run): the format gap that used to be 33 points is
|
| 519 |
+
largely closed. The cost, disclosed above, is that flexible extraction
|
| 520 |
+
itself fell from 62.67, so the model lands the prescribed format far
|
| 521 |
+
more reliably but solves slightly fewer problems overall.
|
| 522 |
+
- The GGUF pack loads only through our llama.cpp fork (`gguf/`) until
|
| 523 |
+
upstreaming lands; stock llama.cpp, ollama, and LM Studio binaries do
|
| 524 |
+
not know the FV5/FV5B tensor types.
|
| 525 |
+
- The pip package's torch path prioritizes bit-exactness over throughput;
|
| 526 |
+
use the `bin/` runtimes in this repo for speed.
|
| 527 |
+
- The family's chat 0.6B SKU is not bit-identical across its three
|
| 528 |
+
shipping runtimes (near-tie argmax flips at small logit margins; each
|
| 529 |
+
runtime deterministic on its own) — see the Neutrino-0.6B-Chat card.
|
| 530 |
+
- Knowledge cutoff and multilingual behavior inherit from the Qwen3-8B base
|
| 531 |
+
plus our disclosed post-training mix; see the data disclosure in
|
| 532 |
+
eval-receipts.
|
| 533 |
+
|
| 534 |
+
## Model lineage and methodology
|
| 535 |
+
|
| 536 |
+
The full methodology — the ternary QAT recipe, the staged post-training
|
| 537 |
+
program (combined behavior stage → IF-1 instruction stack → two
|
| 538 |
+
verifiable-reward stages), the contamination-fingerprint method behind
|
| 539 |
+
the MMLU banner, and the per-cell receipts (venue, version, date) — is
|
| 540 |
+
published in the eval-receipts repo. The engineering story of the format
|
| 541 |
+
and the engine is on the research pages at fermionresearch.com/research/.
|
| 542 |
+
Lineage in one line: base model (see frontmatter) → ternary QAT →
|
| 543 |
+
MMLU-adapted W-7500 brain (disclosed) → combined behavior stage → IF-1
|
| 544 |
+
stack snapshot-50 → verifiable-reward tool-calling dose-25 → IF-checker
|
| 545 |
+
dose-50, baked 2026-07-26 as the ship container pinned above.
|
| 546 |
+
|
| 547 |
+
## License and attribution
|
| 548 |
+
|
| 549 |
+
Weights: Apache-2.0. Derivative of Qwen/Qwen3-8B (Apache-2.0, Alibaba
|
| 550 |
+
Cloud) — see `LICENSE`. Post-training data sources are disclosed in the
|
| 551 |
+
eval-receipts repo (UltraChat, MetaMath, the public `cais/mmlu` splits per
|
| 552 |
+
the banner, and the other accepted-source datasets).
|
| 553 |
+
|
| 554 |
+
`bin/` binaries: prebuilt, closed-source, free to use with these weights;
|
| 555 |
+
no redistribution outside this repo; no reverse engineering. [EULA short
|
| 556 |
+
text — final wording lands with the binaries at upload; RELEASE_RUNBOOK §6.]
|
| 557 |
+
|
| 558 |
+
Contact: [PENDING-DNS: mailbox on fermionresearch.com — fill when mail is configured]
|
bin/MANIFEST.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"note": "bin/ staging manifest: blessed fermion-run release binaries (release-binaries lane, 2026-07-25; receipts in handoffs/results_release_binaries/). Entries mirror the .sha256 sidecars. Functional gates ran against the INTERIM dev container (bake sha256 be44129e...); rerun the one-command smoke after the final W-pipeline product bake lands (RELEASE_RUNBOOK par.4) \u2014 the binaries are container-agnostic (any TRTC v4 arch-3 file) so no rebuild is expected.",
|
| 3 |
+
"binaries": {
|
| 4 |
+
"fermion-run-macos-arm64": {
|
| 5 |
+
"status": "RE-BLESSED 2026-07-27 (thread-safety rebuild, results_native_threadfix): macOS arm64 CPU runtime (NEON dotprod + persistent thread pool; libomp statically linked, only libSystem dep). Supersedes the 2026-07-25 build b963de1a..., which overflowed the per-thread activation-quant buffer above 16 threads. Gated by an OUTPUT-STABILITY thread sweep (packaging/acceptance/thread_sweep.py): byte-identical generated ids at 1/4/8/15/16/17/20/23/max threads on the 0.6B and the 8B, and identical to the superseded binary's output at every thread count it survived.",
|
| 6 |
+
"bytes": 621480,
|
| 7 |
+
"sha256": "d7b257b5cd4b08cadd04fd867158ac50619aab23164294794dc10f05f1ede53c",
|
| 8 |
+
"sidecar": "fermion-run-macos-arm64.sha256",
|
| 9 |
+
"build_receipt": "handoffs/results_release_binaries/receipt_macos_arm64.json"
|
| 10 |
+
},
|
| 11 |
+
"fermion-run-linux-x64": {
|
| 12 |
+
"status": "RE-BLESSED 2026-07-27 (thread-safety rebuild, results_native_threadfix): Linux x86-64 CPU runtime, AVX2 baseline (-march=x86-64-v3), fully static ELF, same source and flags as the 2026-07-25 build. Supersedes 4a09b5cb..., which SIGSEGVed on the 8B and returned silently WRONG TOKENS at exit code 0 on the 0.6B above 16 threads. Gated by the same output-stability thread sweep, run on a 24-core Linux venue.",
|
| 13 |
+
"bytes": 1249584,
|
| 14 |
+
"sha256": "499ecdc569eae2ceaeb4a34cd1f11a91c2c09dfb17aa5847532359c6dc57d93a",
|
| 15 |
+
"sidecar": "fermion-run-linux-x64.sha256",
|
| 16 |
+
"build_receipt": "handoffs/results_release_binaries/receipt_linux_x64.json"
|
| 17 |
+
},
|
| 18 |
+
"fermion-run-linux-cuda": {
|
| 19 |
+
"status": "COMING-SOON (NOT STAGED, no fake artifact): no arch-3 (Qwen3) CUDA runtime exists in kernels/gpu_e2e/ (arch-1/arch-2 walkers only) and the CUDA lane was Python+torch-extension harnesses, never a standalone binary. Blocker + week-one lane recommendation: handoffs/results_release_binaries/CUDA_BLOCKER.md. Working fallback documented in bin/README.md: pip fermion torch path with --device cuda.",
|
| 20 |
+
"bytes": null,
|
| 21 |
+
"sha256": null,
|
| 22 |
+
"sidecar": null
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
}
|
bin/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# bin/ — prebuilt `fermion-run` native runtimes
|
| 2 |
+
|
| 3 |
+
STATUS: **BLESSED BINARIES STAGED 2026-07-25** (release-binaries lane;
|
| 4 |
+
receipts in `handoffs/results_release_binaries/` in the research tree).
|
| 5 |
+
Binaries are prebuilt and closed-source (kernel source does not ship);
|
| 6 |
+
every binary has a `.sha256` sidecar. EULA line lives on the model card.
|
| 7 |
+
Functional gates ran on the interim 8B v4 cut; the runtime is
|
| 8 |
+
container-agnostic (any TRTC v4 arch-3), and the RELEASE_RUNBOOK §3
|
| 9 |
+
acceptance rerun against the final product container (`016c6f36…`, the
|
| 10 |
+
cons3-B25 ship brain — supersedes ifc-s50 `39787a2e…` and d25
|
| 11 |
+
`06af07b5…`) is a publish gate and is STILL OUTSTANDING.
|
| 12 |
+
|
| 13 |
+
## What is here
|
| 14 |
+
|
| 15 |
+
| File | Platform / backend | State |
|
| 16 |
+
|---|---|---|
|
| 17 |
+
| `fermion-run-macos-arm64` | macOS arm64 (M1+), CPU — NEON dotprod + persistent thread pool, OpenMP statically linked, zero dylib deps beyond libSystem | BLESSED (functional-gated on the 8B v4 container) |
|
| 18 |
+
| `fermion-run-linux-x64` | Linux x86-64, CPU — AVX2 baseline (`-march=x86-64-v3`), fully static ELF, runs on any 2015+ x86-64 distro | BLESSED (functional-gated on the 8B v4 container) |
|
| 19 |
+
| `fermion-run-linux-cuda` | Linux x86-64, NVIDIA CUDA | **COMING SOON** — not staged; see fallback below |
|
| 20 |
+
|
| 21 |
+
Honesty notes, so nobody is surprised:
|
| 22 |
+
|
| 23 |
+
- The macOS binary is the **CPU** runtime. The Metal-optimized runtime for
|
| 24 |
+
this model's container format is a week-one follow-up (the existing Metal
|
| 25 |
+
dev builds target a different container lineage and cannot load this
|
| 26 |
+
model); when it lands it replaces this entry with its own receipts.
|
| 27 |
+
- The linux-x64 binary is built for the AVX2 **compatibility baseline**, not
|
| 28 |
+
the AVX-512-VNNI fast path the banked x86 speed receipts were measured on.
|
| 29 |
+
It is correct (bit-exact GEMV unit gate at build time) and self-contained;
|
| 30 |
+
a VNNI-optimized variant is a week-one candidate.
|
| 31 |
+
- CUDA fallback that works today: `pip install fermion` then
|
| 32 |
+
`fermion chat --model fermionresearch/Neutrino-8B --device cuda`
|
| 33 |
+
(reference torch path — correctness-first, not the speed path).
|
| 34 |
+
|
| 35 |
+
## CLI (what the binaries actually take)
|
| 36 |
+
|
| 37 |
+
The binaries take **pre-tokenized ids**, not prompt strings — tokenization
|
| 38 |
+
lives in the `fermion` CLI (`pip install fermion`, the one door), which
|
| 39 |
+
downloads the model + the platform-matching binary from this repo and shells
|
| 40 |
+
out for the fast path. Manual use:
|
| 41 |
+
|
| 42 |
+
```
|
| 43 |
+
./fermion-run-<platform> <container.bin> "<id0,id1,...>" <n_new_tokens> <n_threads> \
|
| 44 |
+
[logits_out.bin] [--temp T] [--top-p P] [--rep-pen R] [--seed S] \
|
| 45 |
+
[--stop-id I0[,I1...]] [--min-p F] [--presence-pen F] [--freq-pen F] \
|
| 46 |
+
[--dry MULT [--dry-base B --dry-allow N]] [--pen-window N] \
|
| 47 |
+
[--eos-exempt-minp] [--eos-boost B:N] [--ppl]
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
The container, the id list, the token count and the **thread count are
|
| 51 |
+
positionals, in that order**. There is no `--chat` and no `--threads`.
|
| 52 |
+
|
| 53 |
+
> [!IMPORTANT]
|
| 54 |
+
> **Sampler flags require `--temp > 0` to engage.** At `--temp 0`
|
| 55 |
+
> (pure greedy) the binary takes a fast argmax path and silently
|
| 56 |
+
> ignores `--rep-pen`, `--top-p`, and all other penalty flags. If
|
| 57 |
+
> you want penalties with near-deterministic output, use
|
| 58 |
+
> `--temp 0.01 --top-p 1.0 --seed 42`. (Found 2026-07-26; a
|
| 59 |
+
> runtime patch making this explicit is a week-one item.)
|
| 60 |
+
>
|
| 61 |
+
> **`--pen-window` is inert on the legacy sampled path.** With `--temp > 0`
|
| 62 |
+
> and none of `--min-p` / `--presence-pen` / `--freq-pen` / `--dry` present,
|
| 63 |
+
> the repetition-penalty window is clamped to the last **64** tokens
|
| 64 |
+
> regardless of what `--pen-window` says (`trit_run.c`: `rwin = g_ext ?
|
| 65 |
+
> max(g_penwin, 64) : 64`). Adding any one of those four flags switches on
|
| 66 |
+
> the modern penalty stack and makes `--pen-window` literal. Every graded
|
| 67 |
+
> C receipt was taken on the legacy path, so the banked C numbers are
|
| 68 |
+
> **64-window** numbers even where the command line reads
|
| 69 |
+
> `--pen-window 256`; the torch and MLX paths do apply 256. Documented
|
| 70 |
+
> 2026-07-27 (results_pip_native + results_0p6b_binaries); making the flag
|
| 71 |
+
> literal on the legacy path is a week-one runtime patch.
|
| 72 |
+
|
| 73 |
+
Defaults: greedy decode; without `--stop-id` exactly `n_new_tokens` are
|
| 74 |
+
generated (EOS ignored); with `--stop-id` generation stops at the stop token
|
| 75 |
+
(coherence mode). Output: line 1 = generated ids (comma-separated), line 2 =
|
| 76 |
+
`tok/s: ...`. `n_new_tokens 0` = prefill-only logits dump.
|
| 77 |
+
|
| 78 |
+
Per-platform one-liners (chat-format ids for a user prompt, then generate up
|
| 79 |
+
to 64 tokens, stop at `<|im_end|>` = 151645):
|
| 80 |
+
|
| 81 |
+
```bash
|
| 82 |
+
# ids for 'In one sentence, why is the sky blue?' via this repo's tokenizer:
|
| 83 |
+
IDS=$(python3 -c "
|
| 84 |
+
from tokenizers import Tokenizer
|
| 85 |
+
t = Tokenizer.from_file('tokenizer.json')
|
| 86 |
+
p = '<|im_start|>user\nIn one sentence, why is the sky blue?<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n'
|
| 87 |
+
print(','.join(map(str, t.encode(p).ids)))")
|
| 88 |
+
|
| 89 |
+
# macOS arm64:
|
| 90 |
+
./bin/fermion-run-macos-arm64 neutrino-8b_v4.bin "$IDS" 64 8 --stop-id 151645
|
| 91 |
+
# Linux x86-64:
|
| 92 |
+
./bin/fermion-run-linux-x64 neutrino-8b_v4.bin "$IDS" 64 8 --stop-id 151645
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
macOS quarantine note: after download run
|
| 96 |
+
`xattr -d com.apple.quarantine bin/fermion-run-macos-arm64` once.
|
| 97 |
+
|
| 98 |
+
## sha256 sidecar convention
|
| 99 |
+
|
| 100 |
+
Every binary `X` ships with `X.sha256` (standard `shasum` line):
|
| 101 |
+
|
| 102 |
+
```bash
|
| 103 |
+
# producer (at staging time, from this directory):
|
| 104 |
+
shasum -a 256 fermion-run-macos-arm64 > fermion-run-macos-arm64.sha256
|
| 105 |
+
# consumer:
|
| 106 |
+
shasum -a 256 -c fermion-run-macos-arm64.sha256
|
| 107 |
+
```
|
| 108 |
+
|
| 109 |
+
`MANIFEST.json` here mirrors the sidecars (file -> bytes + sha256 + build
|
| 110 |
+
provenance) and is regenerated by `packaging/receipts/gen_manifests.py`
|
| 111 |
+
whenever binaries are added or replaced.
|
| 112 |
+
|
| 113 |
+
## Upload checklist (mirrors RELEASE_RUNBOOK §6)
|
| 114 |
+
|
| 115 |
+
1. Binaries + sidecars staged here (done 2026-07-25, receipts in the lane).
|
| 116 |
+
2. Regenerate the tree MANIFEST (`packaging/receipts/gen_manifests.py`).
|
| 117 |
+
3. After `hf upload`, re-download each binary and `shasum -a 256 -c` its
|
| 118 |
+
sidecar (the §6 re-download hash-verify rule applies to binaries too).
|
| 119 |
+
4. One-command smoke on a machine matching each platform before flipping
|
| 120 |
+
the repo public (macOS leg locally is fine — the C runtime is not the
|
| 121 |
+
8B torch load, but respect the RSS budget; linux leg on Modal).
|
| 122 |
+
|
| 123 |
+
## thread count: the runtime is safe above 16 threads again
|
| 124 |
+
|
| 125 |
+
> [!IMPORTANT]
|
| 126 |
+
> **Rebuilt 2026-07-27 (thread-safety fix).** The 2026-07-25 binaries
|
| 127 |
+
> corrupted memory above **16 threads**: the per-thread activation-quant
|
| 128 |
+
> scratch buffer inside the runtime was allocated for 16 workers while the
|
| 129 |
+
> OpenMP path indexed it by thread id with no clamp. On Linux x86-64 that was
|
| 130 |
+
> a SIGSEGV on the 8B and — worse — **exit code 0 with silently different,
|
| 131 |
+
> wrong tokens** on the 0.6B, on any box with 18 or more logical cores (the
|
| 132 |
+
> `fermion` CLI asked for `cpu_count - 1`). macOS arm64 never reached it
|
| 133 |
+
> because Apple silicon tops out at 16 logical cores.
|
| 134 |
+
>
|
| 135 |
+
> These binaries size that buffer from the live thread count, clamp the
|
| 136 |
+
> requested count at a hard ceiling, and are gated by an output-stability
|
| 137 |
+
> thread sweep (`packaging/acceptance/thread_sweep.py`) that asserts
|
| 138 |
+
> **byte-identical generated ids** at 1/4/8/15/16/17/20/23/max threads rather
|
| 139 |
+
> than merely a zero exit code. The `fermion` package independently caps the
|
| 140 |
+
> count it will request at 16 (`fermion.native.THREAD_CAP`).
|
bin/fermion-run-linux-x64
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:499ecdc569eae2ceaeb4a34cd1f11a91c2c09dfb17aa5847532359c6dc57d93a
|
| 3 |
+
size 1249584
|
bin/fermion-run-linux-x64.sha256
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
499ecdc569eae2ceaeb4a34cd1f11a91c2c09dfb17aa5847532359c6dc57d93a fermion-run-linux-x64
|
bin/fermion-run-macos-arm64
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d7b257b5cd4b08cadd04fd867158ac50619aab23164294794dc10f05f1ede53c
|
| 3 |
+
size 621480
|
bin/fermion-run-macos-arm64.sha256
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
d7b257b5cd4b08cadd04fd867158ac50619aab23164294794dc10f05f1ede53c fermion-run-macos-arm64
|
config.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"transformers_version": "5.14.1",
|
| 3 |
+
"architectures": null,
|
| 4 |
+
"output_hidden_states": false,
|
| 5 |
+
"return_dict": true,
|
| 6 |
+
"dtype": "auto",
|
| 7 |
+
"chunk_size_feed_forward": 0,
|
| 8 |
+
"is_encoder_decoder": false,
|
| 9 |
+
"id2label": {
|
| 10 |
+
"0": "LABEL_0",
|
| 11 |
+
"1": "LABEL_1"
|
| 12 |
+
},
|
| 13 |
+
"label2id": {
|
| 14 |
+
"LABEL_0": 0,
|
| 15 |
+
"LABEL_1": 1
|
| 16 |
+
},
|
| 17 |
+
"problem_type": null,
|
| 18 |
+
"_name_or_path": "",
|
| 19 |
+
"container": "neutrino-8b_v4.bin",
|
| 20 |
+
"row_chunk": 512,
|
| 21 |
+
"int8_row_chunk": 4096,
|
| 22 |
+
"model_type": "trtc_v4",
|
| 23 |
+
"output_attentions": false
|
| 24 |
+
}
|
generation_config.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token_id": 151643,
|
| 3 |
+
"do_sample": false,
|
| 4 |
+
"eos_token_id": [
|
| 5 |
+
151645,
|
| 6 |
+
151643
|
| 7 |
+
],
|
| 8 |
+
"pad_token_id": 151643,
|
| 9 |
+
"transformers_version": "4.53.2"
|
| 10 |
+
}
|
gguf/Modelfile
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Neutrino-8B — ollama Modelfile (requires ollama built against the
|
| 2 |
+
# fermion-fv5 llama.cpp fork; stock ollama cannot load the FV5 type)
|
| 3 |
+
FROM ./neutrino-8b-fv5.gguf
|
| 4 |
+
PARAMETER temperature 0.7
|
| 5 |
+
PARAMETER top_p 0.95
|
gguf/README.md
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Neutrino-8B — GGUF pack (llama.cpp fork `fermion-fv5`)
|
| 2 |
+
|
| 3 |
+
Run [Neutrino-8B](../README.md) through the **llama.cpp toolchain**
|
| 4 |
+
(llama-cli, llama-server, llama-bench, and anything built on them,
|
| 5 |
+
including ollama built from source against the fork).
|
| 6 |
+
|
| 7 |
+
**Read this first — which door do you want?**
|
| 8 |
+
|
| 9 |
+
- **This GGUF pack is the compatibility door.** It exists so the
|
| 10 |
+
llama.cpp/ollama/LM Studio world can load and run our format with the
|
| 11 |
+
ecosystem's own tooling. It runs on **CPU and CUDA** (full GPU offload
|
| 12 |
+
works — see below), is correctness-certified on both backends, and is
|
| 13 |
+
*not* tuned for peak speed.
|
| 14 |
+
- **The native binaries are the fast door.** The `fermion-run` CPU
|
| 15 |
+
runtimes in this repo's `bin/` decode the same container at native
|
| 16 |
+
speed (see `bin/README.md`). If you want speed, use those; if you want
|
| 17 |
+
llama.cpp-ecosystem integration, use this pack.
|
| 18 |
+
|
| 19 |
+
**Backend support, stated precisely:**
|
| 20 |
+
|
| 21 |
+
| backend | status |
|
| 22 |
+
|---|---|
|
| 23 |
+
| CPU (x86-64 AVX2, Apple arm64) | shipped, certified |
|
| 24 |
+
| **CUDA, full offload (`-ngl 99`)** | **shipped, certified** — measured below |
|
| 25 |
+
| Metal | **not implemented** — no FV5 kernel; build Metal-off and run on CPU |
|
| 26 |
+
| Vulkan / ROCm / SYCL | not implemented |
|
| 27 |
+
|
| 28 |
+
> [!NOTE]
|
| 29 |
+
> The digests in `SHA256SUMS` certify the **release product cut**:
|
| 30 |
+
> converted and bit-checked (token-identical gate PASS, 2026-07-27 —
|
| 31 |
+
> 8/8 free-running streams identical, 0/1024 teacher-forced mismatches,
|
| 32 |
+
> 0 near-tie flips) from the release container `neutrino-8b_v4.bin`
|
| 33 |
+
> (sha256 `016c6f36…55fa0`, the cons3-B25 ship brain — see the parent
|
| 34 |
+
> card). Receipts in `receipts/`. Earlier cuts that certified this
|
| 35 |
+
> pipeline (ifc-s50, d25, s50, W-baseline) are retired and commented in
|
| 36 |
+
> `SHA256SUMS`; the ifc-s50 gate receipts are kept in
|
| 37 |
+
> `receipts_ifcs50_retired/` as historical record.
|
| 38 |
+
|
| 39 |
+
## What's in the file
|
| 40 |
+
|
| 41 |
+
`neutrino-8b-fv5.gguf` stores the model in two Fermion GGML types added by
|
| 42 |
+
the fork (they do **not** exist in stock llama.cpp):
|
| 43 |
+
|
| 44 |
+
| type | used for | layout per 256-element block | bits/weight |
|
| 45 |
+
|---|---|---|---|
|
| 46 |
+
| `FV5` (43) | all 252 transformer linears | f32 `s_lo`, f32 `s_hi`, 3×32B bit-planes (`bp`,`bn`,`br`) | 3.25 |
|
| 47 |
+
| `FV5B` (44) | int8 embedding + lm_head rows (untied) | f32 row scale, 256×int8 | 8.125 |
|
| 48 |
+
|
| 49 |
+
Weights are `w = (bp − bn) · (br ? s_hi : s_lo)` — the exact five-value
|
| 50 |
+
{0, ±s_lo, ±s_hi} function of the TRTC v4 container. The converter copies
|
| 51 |
+
the container's bit-planes and f32 per-row dual scales verbatim (no
|
| 52 |
+
re-quantization anywhere), and bit-compares every tensor's dequantization
|
| 53 |
+
against the container's f32 expansion before writing. Norms stay f32.
|
| 54 |
+
Activations are never quantized at runtime (`vec_dot_type = F32`), so the
|
| 55 |
+
only numerical difference vs the fp32 expansion is float summation order.
|
| 56 |
+
|
| 57 |
+
## Files
|
| 58 |
+
|
| 59 |
+
See `SHA256SUMS` in this folder for the authoritative digests of:
|
| 60 |
+
|
| 61 |
+
- `neutrino-8b-fv5.gguf` — the model (FV5/FV5B, ~4.2 GiB)
|
| 62 |
+
- `fv5.patch` / `fermion-fv5.bundle` — the fork as a single patch/bundle
|
| 63 |
+
on top of upstream `ggml-org/llama.cpp` @ `d67c0b41`, branch
|
| 64 |
+
`fermion-fv5`, commit `e07b3ec1`, docs in `FORK_NOTES.md`
|
| 65 |
+
|
| 66 |
+
## Quickstart (build the fork, run the model)
|
| 67 |
+
|
| 68 |
+
```bash
|
| 69 |
+
git clone https://github.com/fermionresearch/llama.cpp && cd llama.cpp
|
| 70 |
+
git checkout fermion-fv5
|
| 71 |
+
# or, from upstream + our patch:
|
| 72 |
+
# git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp
|
| 73 |
+
# git checkout d67c0b4107112e4790774c3a8169e2e3eb24643b && git am fv5.patch
|
| 74 |
+
|
| 75 |
+
# CPU
|
| 76 |
+
cmake -B build -DCMAKE_BUILD_TYPE=Release -DLLAMA_CURL=OFF
|
| 77 |
+
cmake --build build -j --target llama-completion llama-bench
|
| 78 |
+
|
| 79 |
+
./build/bin/llama-completion -m neutrino-8b-fv5.gguf \
|
| 80 |
+
-p "Explain why the sky is blue." -n 256 --temp 0 -no-cnv
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
### CUDA (full offload)
|
| 84 |
+
|
| 85 |
+
```bash
|
| 86 |
+
cmake -B build -DCMAKE_BUILD_TYPE=Release -DLLAMA_CURL=OFF \
|
| 87 |
+
-DGGML_CUDA=ON -DGGML_CUDA_NO_VMM=ON -DCMAKE_CUDA_ARCHITECTURES=75\;89
|
| 88 |
+
cmake --build build -j --target llama-completion llama-bench
|
| 89 |
+
|
| 90 |
+
./build/bin/llama-completion -m neutrino-8b-fv5.gguf -ngl 99 \
|
| 91 |
+
-p "Explain why the sky is blue." -n 256 --temp 0 -no-cnv
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
`-DGGML_CUDA_NO_VMM=ON` is **required** when building on a box that has the
|
| 95 |
+
CUDA toolkit but no driver (GPU-less builder/CI): without it the link fails
|
| 96 |
+
on `cuGetErrorString` from the driver API. Drop it if you build on a machine
|
| 97 |
+
with a real driver.
|
| 98 |
+
|
| 99 |
+
**Measured, NVIDIA L4 (driver 580.95.05, 23 GB), full offload:**
|
| 100 |
+
|
| 101 |
+
| metric | value |
|
| 102 |
+
|---|---|
|
| 103 |
+
| tg128 (decode) | **30.68 ± 0.05 tok/s** |
|
| 104 |
+
| pp512 (prefill) | **477.1 ± 4.9 tok/s** |
|
| 105 |
+
| VRAM peak @ 4096 ctx | **4680 MiB (4.68 GiB)** |
|
| 106 |
+
|
| 107 |
+
An 8B model doing 30.7 tok/s inside 4.68 GiB of VRAM is the point of the
|
| 108 |
+
format: this fits on a card that cannot hold an 8B at fp16 at all.
|
| 109 |
+
|
| 110 |
+
> **`llama-server` is not in the verified target set.** The gated build ran
|
| 111 |
+
> `-DLLAMA_BUILD_SERVER=OFF`, so we have not exercised the server against an
|
| 112 |
+
> FV5 pack. It should work, but we will not print a command we never ran.
|
| 113 |
+
> `llama-cli` likewise was not the gated target — `llama-completion` was.
|
| 114 |
+
|
| 115 |
+
### ollama (built against the fork)
|
| 116 |
+
|
| 117 |
+
Stock ollama binaries do not know the FV5 type. Build ollama from source
|
| 118 |
+
with its vendored llama.cpp replaced by the fork checkout, then:
|
| 119 |
+
|
| 120 |
+
```
|
| 121 |
+
# Modelfile -- the conversation row of the parent card's recommended
|
| 122 |
+
# settings. llama.cpp defaults would otherwise give you top-k 40 and a
|
| 123 |
+
# 64-token penalty window, neither of which we measured.
|
| 124 |
+
FROM ./neutrino-8b-fv5.gguf
|
| 125 |
+
PARAMETER temperature 0
|
| 126 |
+
PARAMETER top_p 1.0
|
| 127 |
+
PARAMETER top_k 0
|
| 128 |
+
PARAMETER repeat_penalty 1.05
|
| 129 |
+
PARAMETER repeat_last_n 256
|
| 130 |
+
PARAMETER num_predict 512
|
| 131 |
+
PARAMETER stop "<|im_end|>"
|
| 132 |
+
```
|
| 133 |
+
|
| 134 |
+
For tool calling and JSON, drop the penalty (`PARAMETER repeat_penalty 1.0`):
|
| 135 |
+
structured output measured 8/8 valid at every setting tried, and a penalty
|
| 136 |
+
over `"`, `:` and `{` is the one thing that can break the format. For
|
| 137 |
+
long-form prose, `temperature 0.7`, `top_p 0.95`, `num_predict 1024`.
|
| 138 |
+
|
| 139 |
+
```bash
|
| 140 |
+
ollama create neutrino-8b -f Modelfile
|
| 141 |
+
ollama run neutrino-8b "Explain why the sky is blue."
|
| 142 |
+
```
|
| 143 |
+
|
| 144 |
+
LM Studio: point it at a llama.cpp server built from the fork
|
| 145 |
+
(`llama-server -m neutrino-8b-fv5.gguf`) and connect via the
|
| 146 |
+
OpenAI-compatible endpoint; the bundled engine cannot load FV5 directly.
|
| 147 |
+
|
| 148 |
+
## Speculative decoding quickstart (llama.cpp native, EXPERIMENTAL)
|
| 149 |
+
|
| 150 |
+
The fork inherits mainline llama.cpp's built-in speculative decoding, so
|
| 151 |
+
the 0.6B fv5 pack can draft for this 8B out of the box. **Honesty note,
|
| 152 |
+
read first**: the acceptance rule here is llama.cpp's own sampling-based
|
| 153 |
+
speculation — **not** our bit-gated certification. This is "llama.cpp's
|
| 154 |
+
native speculation with our models"; speeds vary by text; our
|
| 155 |
+
certified-exact speculation lives in the native runtime (and the MLX pack's
|
| 156 |
+
exactness-gated `--spec` mode).
|
| 157 |
+
|
| 158 |
+
```bash
|
| 159 |
+
cmake --build build -j --target llama-speculative
|
| 160 |
+
./build/bin/llama-speculative -m neutrino-8b-fv5.gguf \
|
| 161 |
+
-md neutrino-0.6b-base-fv5.gguf \
|
| 162 |
+
-p "The three laws of thermodynamics state that" \
|
| 163 |
+
-n 128 --temp 0 -t 16 -td 16 --spec-draft-n-max 4
|
| 164 |
+
```
|
| 165 |
+
|
| 166 |
+
Note the flag surface: this tree removed `--draft-max` (use
|
| 167 |
+
`--spec-draft-n-max`; the old flag errors out with a pointer).
|
| 168 |
+
|
| 169 |
+
Measured on a Modal CPU container (16 threads, x86-64-v3 portable build,
|
| 170 |
+
128-token greedy, 8B-wbase target + 0.6B-base draft;
|
| 171 |
+
`receipts/gguf_spec_*.json`): **CPU speculative decode is currently a
|
| 172 |
+
honest null for this pack — it is slower than plain decode at every draft
|
| 173 |
+
depth we tried** (draft depth 16: 0.14–0.29× baseline; depth 4: 0.44–0.66× with
|
| 174 |
+
llama.cpp-rule acceptance 30–57% — facts 5.3→3.5, code 5.4→3.1, prose
|
| 175 |
+
5.4→2.4 tok/s on that host). Why: the FV5 ggml type has a fast `vec_dot` but no
|
| 176 |
+
batched-matmul (sgemm) path yet, so verifying M draft positions costs
|
| 177 |
+
almost M× a single token (~121 ms/position vs ~151 ms single-token in the
|
| 178 |
+
perf prints) — the amortization that makes speculation win never happens,
|
| 179 |
+
and per-token draft acceptance (llama.cpp's rule) of the base 0.6B for the
|
| 180 |
+
8B is text-dependent on top. An FV5 sgemm path is the wired follow-up that
|
| 181 |
+
would flip this; until then, plain `llama-completion`/`llama-cli` is the
|
| 182 |
+
right way to run this pack on CPU.
|
| 183 |
+
|
| 184 |
+
## Correctness & speed
|
| 185 |
+
|
| 186 |
+
- **CPU gate:** greedy streams (8 prompts × 128 tokens) from the fork's CPU
|
| 187 |
+
backend, checked token-by-token against the container's fp32 expansion
|
| 188 |
+
reference. Verdict and near-tie argmax notes: `receipts/` in this folder.
|
| 189 |
+
- **CUDA gate:** greedy streams (8 prompts × 256 tokens, `-ngl 99`,
|
| 190 |
+
`NVIDIA_TF32_OVERRIDE=0`) checked against the certified CPU path.
|
| 191 |
+
**7 of 8 prompts free-run token-identical; 2047 of 2048 teacher-forced
|
| 192 |
+
steps agree.** The single flip is a documented near-tie — step 0 of one
|
| 193 |
+
prompt, logits 15.631217 vs 15.630566, margin **6.5e-4**, inside the
|
| 194 |
+
preregistered 1e-3 bar — i.e. fp32 summation-order noise, not a kernel
|
| 195 |
+
defect. Zero unexplained flips. We report this rather than rounding it to
|
| 196 |
+
"identical"; the raw receipt is the authority.
|
| 197 |
+
- **Speed:** CUDA numbers are in the table above. For CPU tok/s see
|
| 198 |
+
`receipts/bench_*.json`. The native runtime is still the fast door;
|
| 199 |
+
upstreaming and a SIMD-fused NEON `vec_dot` are wired follow-ups. Note the
|
| 200 |
+
speculative-decoding null documented above — do not assume it helps here.
|
| 201 |
+
|
| 202 |
+
### Platform scope of the native binaries (`../bin/`)
|
| 203 |
+
|
| 204 |
+
Two prebuilt runtimes ship: **macOS arm64** (NEON, static libomp) and
|
| 205 |
+
**Linux x86-64**. They are not equivalent. The Linux binary is built to a
|
| 206 |
+
portable **AVX2 baseline** so it runs on older hosts, which means it leaves
|
| 207 |
+
AVX-512/VNNI performance on the table. **Apple-silicon throughput figures
|
| 208 |
+
quoted elsewhere in this repo are Apple-silicon figures and do not transfer
|
| 209 |
+
to the Linux x86-64 binary.** Both binaries verify against their `.sha256`
|
| 210 |
+
sidecars in `../bin/`; on macOS and Linux you may need `chmod +x` after
|
| 211 |
+
download, as the executable bit does not always survive transport.
|
| 212 |
+
|
| 213 |
+
## License
|
| 214 |
+
|
| 215 |
+
**This folder is mixed-licence. Read the split before you reuse anything.**
|
| 216 |
+
|
| 217 |
+
- The **model pack** `neutrino-8b-fv5.gguf` is Apache-2.0, same as the
|
| 218 |
+
parent model card (`../LICENSE`, and `../NOTICE` for the Qwen3
|
| 219 |
+
attribution).
|
| 220 |
+
- The **`fv5.patch` / `fermion-fv5.bundle` source** is a derivative of
|
| 221 |
+
ggml-org/llama.cpp and is therefore **MIT**, upstream's licence — *not*
|
| 222 |
+
Apache-2.0. It is not covered by the parent card's grant. See the fork
|
| 223 |
+
repository's own `LICENSE`.
|
gguf/SHA256SUMS
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Neutrino-8B GGUF pack — authoritative digests (lane results_gguf_fork)
|
| 2 |
+
# NOTE: this certifies the cons3-B25 SHIP-BRAIN cut (source container
|
| 3 |
+
# neutrino-8b_v4.bin, sha256 016c6f36…55fa0), gate_fv5 PASS
|
| 4 |
+
# token-identical 2026-07-27: verdict PASS, 8/8 free-running streams
|
| 5 |
+
# identical, 0/1024 teacher-forced mismatches, 0 near-tie flips
|
| 6 |
+
# (receipts/gateverdict_8b_fv5.json). Lane artifact name
|
| 7 |
+
# neutrino-8b-cons3b25-fv5.gguf (S3 archive/labarchive/gguf_fork/gguf/ +
|
| 8 |
+
# Modal volume ternary-gguf-fork-20260725); published here under the
|
| 9 |
+
# plain name — byte-identical, digest below is the file's sha either
|
| 10 |
+
# way. Verify with `shasum -a 256 -c`.
|
| 11 |
+
1c13a34360b2531d28821fb6aee41708f99a040215a2522af37213c6c8c87211 neutrino-8b-fv5.gguf
|
| 12 |
+
81bf7be5eef18eb98ab4823596374c8dd6615151cf5615425973cb65942c0f73 fv5.patch
|
| 13 |
+
# source container: 016c6f362ad237a8cf4043601efbc1e766574f76ea58691e78fdef51b4155fa0 neutrino-8b_v4.bin
|
| 14 |
+
# f32 control (not published): 089466ea7a8378b658a4ed0e24788bf67f6a81879d93dfa87a3e6863626d0532 neutrino-8b-cons3b25-f32.gguf
|
| 15 |
+
# fork: ggml-org/llama.cpp @ d67c0b41 + fv5.patch = branch fermion-fv5, commit e07b3ec1 (tree f3067d90)
|
| 16 |
+
# fermion-fv5.bundle sha256: 4fad510f32c9df84efda6a63dcb6de7d33f01590947111fdd337d17d2baf87e0
|
| 17 |
+
# RETIRED cuts — do NOT verify against (pipeline certification lineage):
|
| 18 |
+
# 7dda995047dc70540d2d83f2c464dd118fff62eef1aab36ae71ceb3cc972e5d0 neutrino-8b-ifc50-fv5.gguf (07-26 PM; retired 2026-07-27 by the cons3-B25 promote)
|
| 19 |
+
# ae26b97fb17cdb843a34260b748f43dddc4ca3bb66321a2441e52ba2d5f5d687 neutrino-8b-d25-fv5.gguf (07-26 PM)
|
| 20 |
+
# 210195d72d7858698a94e6aca8365c61648322112d4d3ee31ae1f1d36f730d29 neutrino-8b-s50-fv5.gguf (07-26 AM)
|
| 21 |
+
# c4e82e295ad827918e205f46e6c6c874979321e6aac9f0d296b8175168e12861 neutrino-8b-wbase-fv5.gguf (07-25)
|
| 22 |
+
# The ifc-s50 gate receipts are preserved beside this file in
|
| 23 |
+
# receipts_ifcs50_retired/ (historical record; not the shipping gate).
|
gguf/receipts/bench_8b.json
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "8b",
|
| 3 |
+
"bench": [
|
| 4 |
+
{
|
| 5 |
+
"build_commit": "e07b3ec",
|
| 6 |
+
"build_number": 2,
|
| 7 |
+
"cpu_info": "unknown",
|
| 8 |
+
"gpu_info": "",
|
| 9 |
+
"backends": "CPU",
|
| 10 |
+
"model_filename": "/vol/gguf/neutrino-8b-cons3b25-fv5.gguf",
|
| 11 |
+
"model_type": "qwen3 8B FV5 - 3.25 bpw Fermion five-value ternary",
|
| 12 |
+
"model_size": 4087058432,
|
| 13 |
+
"model_n_params": 8190735360,
|
| 14 |
+
"n_batch": 2048,
|
| 15 |
+
"n_ubatch": 512,
|
| 16 |
+
"n_threads": 16,
|
| 17 |
+
"cpu_mask": "0x0",
|
| 18 |
+
"cpu_strict": false,
|
| 19 |
+
"poll": 50,
|
| 20 |
+
"type_k": "f16",
|
| 21 |
+
"type_v": "f16",
|
| 22 |
+
"n_gpu_layers": -1,
|
| 23 |
+
"n_cpu_moe": 0,
|
| 24 |
+
"split_mode": "layer",
|
| 25 |
+
"main_gpu": 0,
|
| 26 |
+
"no_kv_offload": false,
|
| 27 |
+
"flash_attn": -1,
|
| 28 |
+
"devices": "auto",
|
| 29 |
+
"tensor_split": "0.00",
|
| 30 |
+
"tensor_buft_overrides": "none",
|
| 31 |
+
"load_mode": "mmap",
|
| 32 |
+
"embeddings": false,
|
| 33 |
+
"no_op_offload": 0,
|
| 34 |
+
"no_host": false,
|
| 35 |
+
"fit_target": 0,
|
| 36 |
+
"fit_min_ctx": 0,
|
| 37 |
+
"n_prompt": 512,
|
| 38 |
+
"n_gen": 0,
|
| 39 |
+
"n_depth": 0,
|
| 40 |
+
"test_time": "2026-07-27T15:39:39Z",
|
| 41 |
+
"avg_ns": 79088419701,
|
| 42 |
+
"stddev_ns": 90694156,
|
| 43 |
+
"avg_ts": 6.473771,
|
| 44 |
+
"stddev_ts": 0.007424,
|
| 45 |
+
"samples_ns": [
|
| 46 |
+
79024289865,
|
| 47 |
+
79152549538
|
| 48 |
+
],
|
| 49 |
+
"samples_ts": [
|
| 50 |
+
6.47902,
|
| 51 |
+
6.46852
|
| 52 |
+
]
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"build_commit": "e07b3ec",
|
| 56 |
+
"build_number": 2,
|
| 57 |
+
"cpu_info": "unknown",
|
| 58 |
+
"gpu_info": "",
|
| 59 |
+
"backends": "CPU",
|
| 60 |
+
"model_filename": "/vol/gguf/neutrino-8b-cons3b25-fv5.gguf",
|
| 61 |
+
"model_type": "qwen3 8B FV5 - 3.25 bpw Fermion five-value ternary",
|
| 62 |
+
"model_size": 4087058432,
|
| 63 |
+
"model_n_params": 8190735360,
|
| 64 |
+
"n_batch": 2048,
|
| 65 |
+
"n_ubatch": 512,
|
| 66 |
+
"n_threads": 16,
|
| 67 |
+
"cpu_mask": "0x0",
|
| 68 |
+
"cpu_strict": false,
|
| 69 |
+
"poll": 50,
|
| 70 |
+
"type_k": "f16",
|
| 71 |
+
"type_v": "f16",
|
| 72 |
+
"n_gpu_layers": -1,
|
| 73 |
+
"n_cpu_moe": 0,
|
| 74 |
+
"split_mode": "layer",
|
| 75 |
+
"main_gpu": 0,
|
| 76 |
+
"no_kv_offload": false,
|
| 77 |
+
"flash_attn": -1,
|
| 78 |
+
"devices": "auto",
|
| 79 |
+
"tensor_split": "0.00",
|
| 80 |
+
"tensor_buft_overrides": "none",
|
| 81 |
+
"load_mode": "mmap",
|
| 82 |
+
"embeddings": false,
|
| 83 |
+
"no_op_offload": 0,
|
| 84 |
+
"no_host": false,
|
| 85 |
+
"fit_target": 0,
|
| 86 |
+
"fit_min_ctx": 0,
|
| 87 |
+
"n_prompt": 0,
|
| 88 |
+
"n_gen": 128,
|
| 89 |
+
"n_depth": 0,
|
| 90 |
+
"test_time": "2026-07-27T15:43:36Z",
|
| 91 |
+
"avg_ns": 25262016201,
|
| 92 |
+
"stddev_ns": 350847779,
|
| 93 |
+
"avg_ts": 5.067384,
|
| 94 |
+
"stddev_ts": 0.070378,
|
| 95 |
+
"samples_ns": [
|
| 96 |
+
25510103045,
|
| 97 |
+
25013929357
|
| 98 |
+
],
|
| 99 |
+
"samples_ts": [
|
| 100 |
+
5.01762,
|
| 101 |
+
5.11715
|
| 102 |
+
]
|
| 103 |
+
}
|
| 104 |
+
],
|
| 105 |
+
"smoke_rc": 0,
|
| 106 |
+
"smoke_text": "Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France",
|
| 107 |
+
"threads": 16,
|
| 108 |
+
"cpu": "unknown",
|
| 109 |
+
"nproc": 32,
|
| 110 |
+
"wall_s": 304.1,
|
| 111 |
+
"ts": "2026-07-27T15:44:42Z"
|
| 112 |
+
}
|
gguf/receipts/convert_8b.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"status": "CONVERT_PASS",
|
| 3 |
+
"container": {
|
| 4 |
+
"name": "neutrino-8b_v4.bin",
|
| 5 |
+
"sha256": "016c6f362ad237a8cf4043601efbc1e766574f76ea58691e78fdef51b4155fa0"
|
| 6 |
+
},
|
| 7 |
+
"f32_gguf": {
|
| 8 |
+
"name": "neutrino-8b-cons3b25-f32.gguf",
|
| 9 |
+
"bytes": 32768897920
|
| 10 |
+
},
|
| 11 |
+
"out": {
|
| 12 |
+
"name": "neutrino-8b-cons3b25-fv5.gguf",
|
| 13 |
+
"bytes": 4093015136,
|
| 14 |
+
"sha256": "1c13a34360b2531d28821fb6aee41708f99a040215a2522af37213c6c8c87211"
|
| 15 |
+
},
|
| 16 |
+
"tensors": {
|
| 17 |
+
"fv5": 252,
|
| 18 |
+
"fv5b": 2,
|
| 19 |
+
"f32": 145,
|
| 20 |
+
"total": 399,
|
| 21 |
+
"bitwise_checked": 399
|
| 22 |
+
},
|
| 23 |
+
"tied_output_omitted": false,
|
| 24 |
+
"quantized_params": 8190427136,
|
| 25 |
+
"effective_bpw_quantized": 3.9979,
|
| 26 |
+
"header": {
|
| 27 |
+
"magic": 1129599572,
|
| 28 |
+
"version": 4,
|
| 29 |
+
"arch": 3,
|
| 30 |
+
"n_layers": 36,
|
| 31 |
+
"hidden": 4096,
|
| 32 |
+
"n_heads": 32,
|
| 33 |
+
"n_kv_heads": 8,
|
| 34 |
+
"head_dim": 128,
|
| 35 |
+
"rotary_dims": 128,
|
| 36 |
+
"intermediate": 12288,
|
| 37 |
+
"vocab": 151936,
|
| 38 |
+
"max_pos": 40960,
|
| 39 |
+
"rms_norm_eps": 9.999999974752427e-07,
|
| 40 |
+
"rope_theta": 1000000.0,
|
| 41 |
+
"eok": 2
|
| 42 |
+
},
|
| 43 |
+
"wall_seconds": 174.6
|
| 44 |
+
}
|
gguf/receipts/gate_8b_fv5.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "/vol/gguf/neutrino-8b-cons3b25-fv5.gguf",
|
| 3 |
+
"steps": 128,
|
| 4 |
+
"n_threads": 16,
|
| 5 |
+
"load_seconds": 0.46,
|
| 6 |
+
"kv_type": "f32",
|
| 7 |
+
"prompts": [
|
| 8 |
+
{
|
| 9 |
+
"prompt_ids": [1,791,1283,315,220,17,42,99],
|
| 10 |
+
"free_ids": [198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42,198,1,791,1283,315,220,17,42],
|
| 11 |
+
"free_min_top2_margin": 0.128316,
|
| 12 |
+
"prompt_seconds": 1.369,
|
| 13 |
+
"gen_seconds": 24.789,
|
| 14 |
+
"free_match": true,
|
| 15 |
+
"first_divergence": -1,
|
| 16 |
+
"forced_mismatch_count": 0,
|
| 17 |
+
"forced_mismatches": []
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"prompt_ids": [1,9707,374,264,1296,13,28,7],
|
| 21 |
+
"free_ids": [16,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15],
|
| 22 |
+
"free_min_top2_margin": 0.083496,
|
| 23 |
+
"prompt_seconds": 1.316,
|
| 24 |
+
"gen_seconds": 28.711,
|
| 25 |
+
"free_match": true,
|
| 26 |
+
"first_divergence": -1,
|
| 27 |
+
"forced_mismatch_count": 0,
|
| 28 |
+
"forced_mismatches": []
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"prompt_ids": [1,2610,525,498,1091,11,16,3],
|
| 32 |
+
"free_ids": [323,498,525,537,11,17,3,323,498,525,537,11,18,3,323,498,525,537,11,19,3,323,498,525,537,11,20,3,323,498,525,537,11,21,3,323,498,525,537,11,22,3,323,498,525,537,11,23,3,323,498,525,537,11,24,3,323,498,525,537,11,16,15,3,323,498,525,537,11,16,16,3,323,498,525,537,11,16,17,3,323,498,525,537,11,16,18,3,323,498,525,537,11,16,19,3,323,498,525,537,11,16,20,3,323,498,525,537,11,16,21,3,323,498,525,537,11,16,22,3,323,498,525,537,11,16,23,3],
|
| 33 |
+
"free_min_top2_margin": 0.070037,
|
| 34 |
+
"prompt_seconds": 1.516,
|
| 35 |
+
"gen_seconds": 31.581,
|
| 36 |
+
"free_match": true,
|
| 37 |
+
"first_divergence": -1,
|
| 38 |
+
"forced_mismatch_count": 0,
|
| 39 |
+
"forced_mismatches": []
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"prompt_ids": [785,6722,315,9625,374],
|
| 43 |
+
"free_ids": [12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13,576,6722,315,9625,374,12095,13],
|
| 44 |
+
"free_min_top2_margin": 0.439365,
|
| 45 |
+
"prompt_seconds": 0.952,
|
| 46 |
+
"gen_seconds": 31.652,
|
| 47 |
+
"free_match": true,
|
| 48 |
+
"first_divergence": -1,
|
| 49 |
+
"forced_mismatch_count": 0,
|
| 50 |
+
"forced_mismatches": []
|
| 51 |
+
},
|
| 52 |
+
{
|
| 53 |
+
"prompt_ids": [641,264,28727,33161,11,264,2613,21730,11105],
|
| 54 |
+
"free_ids": [264,11580,448,264,4911,3343,13,576,11580,594,7329,374,9761,448,264,5827,315,31340,11,1817,9334,3432,264,3108,3084,315,220,16,4982,13,576,21730,13166,429,279,11580,702,264,2790,315,220,16,15,15,15,31340,13,4354,11,279,21730,1083,13686,429,1045,31340,525,537,9434,504,279,21730,594,2309,13,44763,11,279,31340,429,525,537,9434,525,1846,429,525,537,23942,311,894,1008,9334,13,758,1008,4244,11,264,9334,374,537,9434,421,432,374,537,8433,311,894,1008,9334,553,264,3108,13,2585,1657,31340,525,9434,504,279,21730,594,2309,1939,1249,11625,419,3491,11,582,1184,311,8253,1246,1657],
|
| 55 |
+
"free_min_top2_margin": 0.053864,
|
| 56 |
+
"prompt_seconds": 1.460,
|
| 57 |
+
"gen_seconds": 25.266,
|
| 58 |
+
"free_match": true,
|
| 59 |
+
"first_divergence": -1,
|
| 60 |
+
"forced_mismatch_count": 0,
|
| 61 |
+
"forced_mismatches": []
|
| 62 |
+
},
|
| 63 |
+
{
|
| 64 |
+
"prompt_ids": [750,75698,1445,1648],
|
| 65 |
+
"free_ids": [198,262,421,308,621,220,15,510,286,470,220,15,198,262,421,308,621,220,16,510,286,470,220,16,198,262,770,510,286,470,75698,1445,12,16,8,488,75698,1445,12,17,692,77,284,526,5384,445,6269,279,1372,315,3793,25,40964,75326,39345,1445,340,13874,19324,1986,2038,18653,264,729,1565,75326,39345,1445,46186,429,47417,279,55129,79683,1372,1667,50262,13,576,729,374,1221,2598,448,279,1196,594,1946,369,279,1372,315,3793,13,576,2038,374,1632,1737,36684,323,5646,1465,11589,369,8318,11127,13,576,729,374,11050,323,4278,369,678,6785,25780,13,576,2038,374,1083,4135,311,1349,323,3535],
|
| 66 |
+
"free_min_top2_margin": 0.067368,
|
| 67 |
+
"prompt_seconds": 0.689,
|
| 68 |
+
"gen_seconds": 25.640,
|
| 69 |
+
"free_match": true,
|
| 70 |
+
"first_divergence": -1,
|
| 71 |
+
"forced_mismatch_count": 0,
|
| 72 |
+
"forced_mismatches": []
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"prompt_ids": [785,2326,6872,315,29845,78911,1584,429],
|
| 76 |
+
"free_ids": [4802,374,1585,2771,11,429,279,5106,315,8628,8317,374,504,1550,9315,311,3347,9315,11,323,429,279,2086,2329,315,29845,78911,374,429,47502,12703,13,220,576,1156,2329,315,29845,78911,374,279,2329,315,28611,315,4802,13,220,576,2086,2329,315,29845,78911,374,279,2329,315,7703,47502,13,220,576,4843,2329,315,29845,78911,374,429,279,47502,315,264,1849,646,2581,18472,11,1172,5263,11,323,429,279,47502,315,264,1849,304,28387,55311,374,518,1181,7192,13,220,576,1156,2329,315,29845,78911,374,279,2329,315,28611,315,4802,13,220,576,2086,2329,315,29845,78911,374,279,2329,315,7703,47502,13,220,576],
|
| 77 |
+
"free_min_top2_margin": 0.021589,
|
| 78 |
+
"prompt_seconds": 1.308,
|
| 79 |
+
"gen_seconds": 25.809,
|
| 80 |
+
"free_match": true,
|
| 81 |
+
"first_divergence": -1,
|
| 82 |
+
"forced_mismatch_count": 0,
|
| 83 |
+
"forced_mismatches": []
|
| 84 |
+
},
|
| 85 |
+
{
|
| 86 |
+
"prompt_ids": [12522,5193,264,882,11,304,264,11340,14126,553,279,9396,11],
|
| 87 |
+
"free_ids": [1052,12163,264,3908,3743,6941,444,10524,13,2932,572,3881,369,1059,40228,323,2948,369,279,17951,13,7209,6556,11,1340,1035,15156,705,1573,38393,11,11435,311,279,11321,311,8844,311,279,16876,323,2666,279,9278,1948,1059,44613,13,6252,6930,7702,572,264,2613,272,994,1380,279,3015,572,19300,323,279,511,988,6436,15186,832,304,279,39020,13,1084,572,1052,429,1340,1035,1477,279,1429,6233,58849,11,1075,13673,511,988,6436,11,9472,315,33638,6660,11,323,1496,264,2421,1560,3435,429,1035,1136,98243,3123,979,1340,2684,2238,3265,382,3966,1899,11,1393,23966,279,272,994,11,444,10524,13686,264,43697,2770,396],
|
| 88 |
+
"free_min_top2_margin": 0.015932,
|
| 89 |
+
"prompt_seconds": 2.088,
|
| 90 |
+
"gen_seconds": 25.561,
|
| 91 |
+
"free_match": true,
|
| 92 |
+
"first_divergence": -1,
|
| 93 |
+
"forced_mismatch_count": 0,
|
| 94 |
+
"forced_mismatches": []
|
| 95 |
+
}
|
| 96 |
+
],
|
| 97 |
+
"totals": {
|
| 98 |
+
"prompt_tokens": 63,
|
| 99 |
+
"gen_tokens": 1024,
|
| 100 |
+
"prompt_tok_per_s": 5.89,
|
| 101 |
+
"gen_tok_per_s": 4.68,
|
| 102 |
+
"forced_mismatch_total": 0,
|
| 103 |
+
"all_free_match": true
|
| 104 |
+
}
|
| 105 |
+
}
|
gguf/receipts/gateverdict_8b_fv5.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "8b",
|
| 3 |
+
"variant": "fv5",
|
| 4 |
+
"verdict": "PASS",
|
| 5 |
+
"all_free_match": true,
|
| 6 |
+
"forced_mismatch_total": 0,
|
| 7 |
+
"gen_tok_per_s": 4.68,
|
| 8 |
+
"prompt_tok_per_s": 5.89,
|
| 9 |
+
"flips": [],
|
| 10 |
+
"unexplained": 0,
|
| 11 |
+
"gguf_sha256": "1c13a34360b2531d28821fb6aee41708f99a040215a2522af37213c6c8c87211",
|
| 12 |
+
"ref_trajectory_sha256": "96041dbdd766f1536303f5a8bd7577bd60ea8412008f525f2e8f9d3499980638",
|
| 13 |
+
"threads": 16,
|
| 14 |
+
"cpu": "unknown",
|
| 15 |
+
"wall_s": 463.4,
|
| 16 |
+
"ts": "2026-07-27T15:39:37Z"
|
| 17 |
+
}
|
gguf/receipts/gguf_spec_quickstart_k16.json
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"receipt": "llama.cpp NATIVE speculative decoding (its own sampling-based acceptance, NOT our bit-gated certification) with fermion-fv5 fork + banked fv5 GGUFs: 8B-wbase target, 0.6B-base draft",
|
| 3 |
+
"fork_head": "e07b3ec135c1f90c64584e0ea2dc983e47094ab9",
|
| 4 |
+
"draft_flag_used": [
|
| 5 |
+
"--spec-draft-n-max",
|
| 6 |
+
"16"
|
| 7 |
+
],
|
| 8 |
+
"target": "/gguf_ro/gguf/neutrino-8b-wbase-fv5.gguf",
|
| 9 |
+
"draft": "/gguf_ro/gguf/neutrino-0.6b-base-fv5.gguf",
|
| 10 |
+
"threads": 16,
|
| 11 |
+
"cpu": "unknown",
|
| 12 |
+
"nproc": 32,
|
| 13 |
+
"results": {
|
| 14 |
+
"facts": {
|
| 15 |
+
"prompt": "The three laws of thermodynamics state that",
|
| 16 |
+
"n_tok": 128,
|
| 17 |
+
"baseline": {
|
| 18 |
+
"prompt_ms": 997.53,
|
| 19 |
+
"prompt_n": 8,
|
| 20 |
+
"prompt_tok_s": 8.02,
|
| 21 |
+
"eval_ms": 19152.78,
|
| 22 |
+
"eval_n": 127,
|
| 23 |
+
"eval_tok_s": 6.63,
|
| 24 |
+
"rc": 0
|
| 25 |
+
},
|
| 26 |
+
"baseline_text_head": "energy is conserved, that the entropy of an isolated system can never decrease, and that the entropy of an isolated system can never increase. The first law is ",
|
| 27 |
+
"spec": {
|
| 28 |
+
"prompt_ms": 58409.31,
|
| 29 |
+
"prompt_n": 484,
|
| 30 |
+
"prompt_tok_s": 8.29,
|
| 31 |
+
"encoded_ms_tok_s": {
|
| 32 |
+
"tokens": 8,
|
| 33 |
+
"seconds": 1.082,
|
| 34 |
+
"tok_s": 7.392
|
| 35 |
+
},
|
| 36 |
+
"decoded_ms_tok_s": {
|
| 37 |
+
"tokens": 134,
|
| 38 |
+
"seconds": 68.869,
|
| 39 |
+
"tok_s": 1.946
|
| 40 |
+
},
|
| 41 |
+
"n_draft": 16,
|
| 42 |
+
"n_predict": 134,
|
| 43 |
+
"n_drafted": 448,
|
| 44 |
+
"n_accept": 105,
|
| 45 |
+
"accept_pct": 23.438,
|
| 46 |
+
"rc": 0,
|
| 47 |
+
"draft_flag": [
|
| 48 |
+
"--spec-draft-n-max",
|
| 49 |
+
"16"
|
| 50 |
+
]
|
| 51 |
+
},
|
| 52 |
+
"spec_tail": "The three laws of thermodynamics state that energy is conserved, that the entropy of an isolated system can never decrease, and that the entropy of an isolated system can never increase. The first law is the conservation of energy, which states that energy cannot be created or destroyed, only transformed. The second law states that the entropy of an isolated system can never decrease. The third law states that the entropy of an isolated system can never increase. The second law is also known as the law of increasing entropy. The third law is also known as the law of zero entropy. The second law is also known as the law of increasing entropy. The third law is also known as the law of zero entropy. The second\n\n\n\n\n0.00.623.260 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n0.02.775.851 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n1.13.679.397 I encoded 8 tokens in 1.082 seconds, speed: 7.392 t/s\n1.13.679.399 I decoded 134 tokens in 68.869 seconds, speed: 1.946 t/s\n1.13.679.399 I \n1.13.679.400 I n_draft = 16\n1.13.679.400 I n_predict = 134\n1.13.679.408 I n_drafted = 448\n1.13.679.408 I n_accept = 105\n1.13.679.409 I accept = 23.438%\n1.13.679.409 I \n1.13.679.409 I draft:\n\n1.13.679.424 I \n1.13.679.424 I target:\n\n1.13.679.431 I common_perf_print: sampling time = 29.13 ms\n1.13.679.431 I common_perf_print: samplers time = 7.43 ms / 134 tokens\n1.13.679.433 I common_perf_print: load time = 1559.85 ms\n1.13.679.434 I common_perf_print: prompt eval time = 58409.31 ms / 484 tokens ( 120.68 ms per token, 8.29 tokens per second)\n1.13.679.435 I common_perf_print: eval time = 0.00 ms / 1 runs ( 0.00 ms per token, inf tokens per second)\n1.13.679.435 I common_perf_print: total time = 71586.90 ms / 485 tokens\n1.13.679.436 I common_perf_print: unaccounted time = 13148.46 ms / 18.4 % (total - sampling - prompt eval - eval) / (total)\n1.13.679.436 I common_perf_print: graphs reused = 27",
|
| 53 |
+
"speedup": 0.294
|
| 54 |
+
},
|
| 55 |
+
"prose": {
|
| 56 |
+
"prompt": "Once upon a time, in a quiet village by the sea,",
|
| 57 |
+
"n_tok": 128,
|
| 58 |
+
"baseline": {
|
| 59 |
+
"prompt_ms": 1553.17,
|
| 60 |
+
"prompt_n": 13,
|
| 61 |
+
"prompt_tok_s": 8.37,
|
| 62 |
+
"eval_ms": 19023.4,
|
| 63 |
+
"eval_n": 127,
|
| 64 |
+
"eval_tok_s": 6.68,
|
| 65 |
+
"rc": 0
|
| 66 |
+
},
|
| 67 |
+
"baseline_text_head": "there lived a young girl named Lila. She was known for her kindness and curiosity. One day, while exploring the forest near her home, she stumbled upon a hidden",
|
| 68 |
+
"spec": {
|
| 69 |
+
"prompt_ms": 123089.45,
|
| 70 |
+
"prompt_n": 999,
|
| 71 |
+
"prompt_tok_s": 8.12,
|
| 72 |
+
"encoded_ms_tok_s": {
|
| 73 |
+
"tokens": 13,
|
| 74 |
+
"seconds": 1.699,
|
| 75 |
+
"tok_s": 7.653
|
| 76 |
+
},
|
| 77 |
+
"decoded_ms_tok_s": {
|
| 78 |
+
"tokens": 131,
|
| 79 |
+
"seconds": 143.874,
|
| 80 |
+
"tok_s": 0.911
|
| 81 |
+
},
|
| 82 |
+
"n_draft": 16,
|
| 83 |
+
"n_predict": 131,
|
| 84 |
+
"n_drafted": 928,
|
| 85 |
+
"n_accept": 72,
|
| 86 |
+
"accept_pct": 7.759,
|
| 87 |
+
"rc": 0,
|
| 88 |
+
"draft_flag": [
|
| 89 |
+
"--spec-draft-n-max",
|
| 90 |
+
"16"
|
| 91 |
+
]
|
| 92 |
+
},
|
| 93 |
+
"spec_tail": "Once upon a time, in a quiet village by the sea, there lived a young girl named Lila. She was known for her kindness and curiosity. One day, while exploring the forest near her home, she stumbled upon a hidden cave. Inside, she found a glowing orb that seemed to pulse with a soft light. Intrigued, she picked it up and noticed that it was not just a regular rock. It was a crystal that emitted a faint light. As she held it closer, she noticed that the light was not just a natural glow but something more. It was as if the crystal was alive, responding to her touch. \n\nLila was fascinated and decided to take the crystal home. She placed\n\n\n\n\n0.00.585.445 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n0.02.549.802 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n2.29.032.790 I encoded 13 tokens in 1.699 seconds, speed: 7.653 t/s\n2.29.032.794 I decoded 131 tokens in 143.874 seconds, speed: 0.911 t/s\n2.29.032.794 I \n2.29.032.802 I n_draft = 16\n2.29.032.802 I n_predict = 131\n2.29.032.803 I n_drafted = 928\n2.29.032.803 I n_accept = 72\n2.29.032.804 I accept = 7.759%\n2.29.032.804 I \n2.29.032.804 I draft:\n\n2.29.032.822 I \n2.29.032.822 I target:\n\n2.29.032.829 I common_perf_print: sampling time = 32.72 ms\n2.29.032.830 I common_perf_print: samplers time = 7.62 ms / 131 tokens\n2.29.032.832 I common_perf_print: load time = 1519.90 ms\n2.29.032.833 I common_perf_print: prompt eval time = 123089.45 ms / 999 tokens ( 123.21 ms per token, 8.12 tokens per second)\n2.29.032.834 I common_perf_print: eval time = 0.00 ms / 1 runs ( 0.00 ms per token, inf tokens per second)\n2.29.032.834 I common_perf_print: total time = 147009.52 ms / 1000 tokens\n2.29.032.837 I common_perf_print: unaccounted time = 23887.34 ms / 16.2 % (total - sampling - prompt eval - eval) / (total)\n2.29.032.838 I common_perf_print: graphs reused = 57",
|
| 94 |
+
"speedup": 0.136
|
| 95 |
+
},
|
| 96 |
+
"code": {
|
| 97 |
+
"prompt": "def fibonacci(n):",
|
| 98 |
+
"n_tok": 128,
|
| 99 |
+
"baseline": {
|
| 100 |
+
"prompt_ms": 511.22,
|
| 101 |
+
"prompt_n": 4,
|
| 102 |
+
"prompt_tok_s": 7.82,
|
| 103 |
+
"eval_ms": 19258.57,
|
| 104 |
+
"eval_n": 127,
|
| 105 |
+
"eval_tok_s": 6.59,
|
| 106 |
+
"rc": 0
|
| 107 |
+
},
|
| 108 |
+
"baseline_text_head": "if n < 0:\n return 0\n if n == 0:\n return 0\n if n == 1:\n return 1\n return fibonacci(n-1) + fibonacci(n-2)\n\ndef fibonacci(n):\n # b",
|
| 109 |
+
"spec": {
|
| 110 |
+
"prompt_ms": 74903.85,
|
| 111 |
+
"prompt_n": 616,
|
| 112 |
+
"prompt_tok_s": 8.22,
|
| 113 |
+
"encoded_ms_tok_s": {
|
| 114 |
+
"tokens": 4,
|
| 115 |
+
"seconds": 0.598,
|
| 116 |
+
"tok_s": 6.693
|
| 117 |
+
},
|
| 118 |
+
"decoded_ms_tok_s": {
|
| 119 |
+
"tokens": 132,
|
| 120 |
+
"seconds": 88.867,
|
| 121 |
+
"tok_s": 1.485
|
| 122 |
+
},
|
| 123 |
+
"n_draft": 16,
|
| 124 |
+
"n_predict": 132,
|
| 125 |
+
"n_drafted": 576,
|
| 126 |
+
"n_accept": 95,
|
| 127 |
+
"accept_pct": 16.493,
|
| 128 |
+
"rc": 0,
|
| 129 |
+
"draft_flag": [
|
| 130 |
+
"--spec-draft-n-max",
|
| 131 |
+
"16"
|
| 132 |
+
]
|
| 133 |
+
},
|
| 134 |
+
"spec_tail": " # base case\n if n == 0:\n return 0\n if n == 1:\n return 1\n # recursive case\n return fibonacci(n-1) + fibonacci(n-2)\n\n# test cases\nprint(fibonacci(0)) # 0\nprint(fibonacci(1)) # 1\nprint(fib\n\n\n\n\n0.00.572.699 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n0.02.535.311 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n1.32.870.731 I encoded 4 tokens in 0.598 seconds, speed: 6.693 t/s\n1.32.870.732 I decoded 132 tokens in 88.867 seconds, speed: 1.485 t/s\n1.32.870.733 I \n1.32.870.733 I n_draft = 16\n1.32.870.733 I n_predict = 132\n1.32.870.734 I n_drafted = 576\n1.32.870.734 I n_accept = 95\n1.32.870.734 I accept = 16.493%\n1.32.870.734 I \n1.32.870.734 I draft:\n\n1.32.870.747 I \n1.32.870.754 I target:\n\n1.32.870.798 I common_perf_print: sampling time = 30.78 ms\n1.32.870.801 I common_perf_print: samplers time = 6.95 ms / 132 tokens\n1.32.870.807 I common_perf_print: load time = 1545.72 ms\n1.32.870.809 I common_perf_print: prompt eval time = 74903.85 ms / 616 tokens ( 121.60 ms per token, 8.22 tokens per second)\n1.32.870.810 I common_perf_print: eval time = 0.00 ms / 1 runs ( 0.00 ms per token, inf tokens per second)\n1.32.870.811 I common_perf_print: total time = 90831.76 ms / 617 tokens\n1.32.870.811 I common_perf_print: unaccounted time = 15897.13 ms / 17.5 % (total - sampling - prompt eval - eval) / (total)\n1.32.870.812 I common_perf_print: graphs reused = 35",
|
| 135 |
+
"speedup": 0.225
|
| 136 |
+
}
|
| 137 |
+
},
|
| 138 |
+
"wall_s": 449.6,
|
| 139 |
+
"ts": "2026-07-25T16:44:01Z",
|
| 140 |
+
"_annotation_20260726": "HONESTY NOTE (successor session): this quickstart was measured against the RETIRED W-baseline cut (neutrino-8b-wbase-fv5.gguf). The published pack is the s50 product cut (210195d7...). llama.cpp-native speculation was an honest NULL either way (0.14-0.66x); numbers kept for reference only, not re-run (README already labels the section EXPERIMENTAL)."
|
| 141 |
+
}
|
gguf/receipts/gguf_spec_quickstart_k4.json
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"receipt": "llama.cpp NATIVE speculative decoding (its own sampling-based acceptance, NOT our bit-gated certification) with fermion-fv5 fork + banked fv5 GGUFs: 8B-wbase target, 0.6B-base draft",
|
| 3 |
+
"fork_head": "e07b3ec135c1f90c64584e0ea2dc983e47094ab9",
|
| 4 |
+
"draft_flag_used": [
|
| 5 |
+
"--spec-draft-n-max",
|
| 6 |
+
"4"
|
| 7 |
+
],
|
| 8 |
+
"target": "/gguf_ro/gguf/neutrino-8b-wbase-fv5.gguf",
|
| 9 |
+
"draft": "/gguf_ro/gguf/neutrino-0.6b-base-fv5.gguf",
|
| 10 |
+
"threads": 16,
|
| 11 |
+
"cpu": "unknown",
|
| 12 |
+
"nproc": 32,
|
| 13 |
+
"results": {
|
| 14 |
+
"facts": {
|
| 15 |
+
"prompt": "The three laws of thermodynamics state that",
|
| 16 |
+
"n_tok": 128,
|
| 17 |
+
"baseline": {
|
| 18 |
+
"prompt_ms": 1245.64,
|
| 19 |
+
"prompt_n": 8,
|
| 20 |
+
"prompt_tok_s": 6.42,
|
| 21 |
+
"eval_ms": 23778.72,
|
| 22 |
+
"eval_n": 127,
|
| 23 |
+
"eval_tok_s": 5.34,
|
| 24 |
+
"rc": 0
|
| 25 |
+
},
|
| 26 |
+
"baseline_text_head": "energy is conserved, that the entropy of an isolated system can never decrease, and that the entropy of an isolated system can never increase. The first law is ",
|
| 27 |
+
"spec": {
|
| 28 |
+
"prompt_ms": 32972.91,
|
| 29 |
+
"prompt_n": 208,
|
| 30 |
+
"prompt_tok_s": 6.31,
|
| 31 |
+
"encoded_ms_tok_s": {
|
| 32 |
+
"tokens": 8,
|
| 33 |
+
"seconds": 1.358,
|
| 34 |
+
"tok_s": 5.892
|
| 35 |
+
},
|
| 36 |
+
"decoded_ms_tok_s": {
|
| 37 |
+
"tokens": 132,
|
| 38 |
+
"seconds": 37.378,
|
| 39 |
+
"tok_s": 3.531
|
| 40 |
+
},
|
| 41 |
+
"n_draft": 4,
|
| 42 |
+
"n_predict": 132,
|
| 43 |
+
"n_drafted": 160,
|
| 44 |
+
"n_accept": 91,
|
| 45 |
+
"accept_pct": 56.875,
|
| 46 |
+
"rc": 0,
|
| 47 |
+
"draft_flag": [
|
| 48 |
+
"--spec-draft-n-max",
|
| 49 |
+
"4"
|
| 50 |
+
]
|
| 51 |
+
},
|
| 52 |
+
"spec_tail": "The three laws of thermodynamics state that energy is conserved, that the entropy of an isolated system can never decrease, and that the entropy of an isolated system can never increase. The first law is the conservation of energy, which states that energy cannot be created or destroyed, only transformed. The second law states that the entropy of an isolated system can never decrease. The third law states that the entropy of an isolated system can never increase. The second law is also known as the law of increasing entropy. The third law is also known as the law of zero entropy. The second law is also known as the law of increasing entropy. The third law is also known as the law of zero entropy.\n\n\n\n\n0.00.771.178 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n0.03.414.343 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n0.43.601.940 I encoded 8 tokens in 1.358 seconds, speed: 5.892 t/s\n0.43.601.942 I decoded 132 tokens in 37.378 seconds, speed: 3.531 t/s\n0.43.601.942 I \n0.43.601.943 I n_draft = 4\n0.43.601.943 I n_predict = 132\n0.43.601.943 I n_drafted = 160\n0.43.601.943 I n_accept = 91\n0.43.601.954 I accept = 56.875%\n0.43.601.954 I \n0.43.601.954 I draft:\n\n0.43.601.972 I \n0.43.601.972 I target:\n\n0.43.601.979 I common_perf_print: sampling time = 53.66 ms\n0.43.601.980 I common_perf_print: samplers time = 10.54 ms / 132 tokens\n0.43.601.983 I common_perf_print: load time = 2121.14 ms\n0.43.601.984 I common_perf_print: prompt eval time = 32972.91 ms / 208 tokens ( 158.52 ms per token, 6.31 tokens per second)\n0.43.601.985 I common_perf_print: eval time = 0.00 ms / 1 runs ( 0.00 ms per token, inf tokens per second)\n0.43.601.985 I common_perf_print: total time = 40811.34 ms / 209 tokens\n0.43.601.986 I common_perf_print: unaccounted time = 7784.78 ms / 19.1 % (total - sampling - prompt eval - eval) / (total)\n0.43.601.987 I common_perf_print: graphs reused = 39",
|
| 53 |
+
"speedup": 0.661
|
| 54 |
+
},
|
| 55 |
+
"prose": {
|
| 56 |
+
"prompt": "Once upon a time, in a quiet village by the sea,",
|
| 57 |
+
"n_tok": 128,
|
| 58 |
+
"baseline": {
|
| 59 |
+
"prompt_ms": 1974.15,
|
| 60 |
+
"prompt_n": 13,
|
| 61 |
+
"prompt_tok_s": 6.59,
|
| 62 |
+
"eval_ms": 23545.97,
|
| 63 |
+
"eval_n": 127,
|
| 64 |
+
"eval_tok_s": 5.39,
|
| 65 |
+
"rc": 0
|
| 66 |
+
},
|
| 67 |
+
"baseline_text_head": "there lived a young girl named Lila. She was known for her kindness and curiosity. One day, while exploring the forest near her home, she stumbled upon a hidden",
|
| 68 |
+
"spec": {
|
| 69 |
+
"prompt_ms": 49207.44,
|
| 70 |
+
"prompt_n": 308,
|
| 71 |
+
"prompt_tok_s": 6.26,
|
| 72 |
+
"encoded_ms_tok_s": {
|
| 73 |
+
"tokens": 13,
|
| 74 |
+
"seconds": 2.134,
|
| 75 |
+
"tok_s": 6.093
|
| 76 |
+
},
|
| 77 |
+
"decoded_ms_tok_s": {
|
| 78 |
+
"tokens": 131,
|
| 79 |
+
"seconds": 55.128,
|
| 80 |
+
"tok_s": 2.376
|
| 81 |
+
},
|
| 82 |
+
"n_draft": 4,
|
| 83 |
+
"n_predict": 131,
|
| 84 |
+
"n_drafted": 236,
|
| 85 |
+
"n_accept": 71,
|
| 86 |
+
"accept_pct": 30.085,
|
| 87 |
+
"rc": 0,
|
| 88 |
+
"draft_flag": [
|
| 89 |
+
"--spec-draft-n-max",
|
| 90 |
+
"4"
|
| 91 |
+
]
|
| 92 |
+
},
|
| 93 |
+
"spec_tail": "Once upon a time, in a quiet village by the sea, there lived a young girl named Lila. She was known for her kindness and curiosity. One day, while exploring the forest near her home, she stumbled upon a hidden cave. Inside, she found a glowing orb that seemed to pulse with a soft light. Intrigued, she picked it up and noticed that it was not just a regular rock. It was a crystal that emitted a faint light. As she held it closer, she noticed that the light was not just a natural glow but something more. It was as if the crystal was alive, responding to her touch. \n\nLila was fascinated and decided to take the crystal home. She placed\n\n\n\n\n0.00.762.994 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n0.03.368.212 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n1.01.817.657 I encoded 13 tokens in 2.134 seconds, speed: 6.093 t/s\n1.01.817.697 I decoded 131 tokens in 55.128 seconds, speed: 2.376 t/s\n1.01.817.697 I \n1.01.817.698 I n_draft = 4\n1.01.817.698 I n_predict = 131\n1.01.817.698 I n_drafted = 236\n1.01.817.698 I n_accept = 71\n1.01.817.699 I accept = 30.085%\n1.01.817.699 I \n1.01.817.699 I draft:\n\n1.01.817.715 I \n1.01.817.715 I target:\n\n1.01.817.723 I common_perf_print: sampling time = 54.68 ms\n1.01.817.723 I common_perf_print: samplers time = 10.39 ms / 131 tokens\n1.01.817.725 I common_perf_print: load time = 2083.05 ms\n1.01.817.726 I common_perf_print: prompt eval time = 49207.44 ms / 308 tokens ( 159.76 ms per token, 6.26 tokens per second)\n1.01.817.727 I common_perf_print: eval time = 0.00 ms / 1 runs ( 0.00 ms per token, inf tokens per second)\n1.01.817.728 I common_perf_print: total time = 59095.53 ms / 309 tokens\n1.01.817.729 I common_perf_print: unaccounted time = 9833.40 ms / 16.6 % (total - sampling - prompt eval - eval) / (total)\n1.01.817.729 I common_perf_print: graphs reused = 58",
|
| 94 |
+
"speedup": 0.441
|
| 95 |
+
},
|
| 96 |
+
"code": {
|
| 97 |
+
"prompt": "def fibonacci(n):",
|
| 98 |
+
"n_tok": 128,
|
| 99 |
+
"baseline": {
|
| 100 |
+
"prompt_ms": 639.29,
|
| 101 |
+
"prompt_n": 4,
|
| 102 |
+
"prompt_tok_s": 6.26,
|
| 103 |
+
"eval_ms": 23422.95,
|
| 104 |
+
"eval_n": 127,
|
| 105 |
+
"eval_tok_s": 5.42,
|
| 106 |
+
"rc": 0
|
| 107 |
+
},
|
| 108 |
+
"baseline_text_head": "if n < 0:\n return 0\n if n == 0:\n return 0\n if n == 1:\n return 1\n return fibonacci(n-1) + fibonacci(n-2)\n\ndef fibonacci(n):\n # b",
|
| 109 |
+
"spec": {
|
| 110 |
+
"prompt_ms": 36454.87,
|
| 111 |
+
"prompt_n": 229,
|
| 112 |
+
"prompt_tok_s": 6.28,
|
| 113 |
+
"encoded_ms_tok_s": {
|
| 114 |
+
"tokens": 4,
|
| 115 |
+
"seconds": 0.728,
|
| 116 |
+
"tok_s": 5.494
|
| 117 |
+
},
|
| 118 |
+
"decoded_ms_tok_s": {
|
| 119 |
+
"tokens": 130,
|
| 120 |
+
"seconds": 42.11,
|
| 121 |
+
"tok_s": 3.087
|
| 122 |
+
},
|
| 123 |
+
"n_draft": 4,
|
| 124 |
+
"n_predict": 130,
|
| 125 |
+
"n_drafted": 180,
|
| 126 |
+
"n_accept": 84,
|
| 127 |
+
"accept_pct": 46.667,
|
| 128 |
+
"rc": 0,
|
| 129 |
+
"draft_flag": [
|
| 130 |
+
"--spec-draft-n-max",
|
| 131 |
+
"4"
|
| 132 |
+
]
|
| 133 |
+
},
|
| 134 |
+
"spec_tail": " # base case\n if n == 0:\n return 0\n if n == 1:\n return 1\n # recursive case\n return fibonacci(n-1) + fibonacci(n-2)\n\n# test cases\nprint(fibonacci(0)) # 0\nprint(fibonacci(1)) # 1\nprint\n\n\n\n\n0.00.784.120 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n0.03.369.496 W load: control-looking token: 128247 '</s>' was not control-type; this is probably a bug in the model. its type will be overridden\n0.47.355.247 I encoded 4 tokens in 0.728 seconds, speed: 5.494 t/s\n0.47.355.249 I decoded 130 tokens in 42.110 seconds, speed: 3.087 t/s\n0.47.355.249 I \n0.47.355.250 I n_draft = 4\n0.47.355.250 I n_predict = 130\n0.47.355.250 I n_drafted = 180\n0.47.355.250 I n_accept = 84\n0.47.355.251 I accept = 46.667%\n0.47.355.251 I \n0.47.355.251 I draft:\n\n0.47.355.269 I \n0.47.355.269 I target:\n\n0.47.355.276 I common_perf_print: sampling time = 51.98 ms\n0.47.355.277 I common_perf_print: samplers time = 10.86 ms / 130 tokens\n0.47.355.279 I common_perf_print: load time = 2064.72 ms\n0.47.355.280 I common_perf_print: prompt eval time = 36454.87 ms / 229 tokens ( 159.19 ms per token, 6.28 tokens per second)\n0.47.355.281 I common_perf_print: eval time = 0.00 ms / 1 runs ( 0.00 ms per token, inf tokens per second)\n0.47.355.282 I common_perf_print: total time = 44627.08 ms / 230 tokens\n0.47.355.283 I common_perf_print: unaccounted time = 8120.24 ms / 18.2 % (total - sampling - prompt eval - eval) / (total)\n0.47.355.283 I common_perf_print: graphs reused = 44",
|
| 135 |
+
"speedup": 0.57
|
| 136 |
+
}
|
| 137 |
+
},
|
| 138 |
+
"wall_s": 324.4,
|
| 139 |
+
"ts": "2026-07-25T16:50:05Z",
|
| 140 |
+
"_annotation_20260726": "HONESTY NOTE (successor session): this quickstart was measured against the RETIRED W-baseline cut (neutrino-8b-wbase-fv5.gguf). The published pack is the s50 product cut (210195d7...). llama.cpp-native speculation was an honest NULL either way (0.14-0.66x); numbers kept for reference only, not re-run (README already labels the section EXPERIMENTAL)."
|
| 141 |
+
}
|
gguf/receipts/ref_8b.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "8b",
|
| 3 |
+
"dtype": "float32",
|
| 4 |
+
"steps": 128,
|
| 5 |
+
"trajectory_sha256": "96041dbdd766f1536303f5a8bd7577bd60ea8412008f525f2e8f9d3499980638",
|
| 6 |
+
"continuity_vs_banked_fp16": [
|
| 7 |
+
{
|
| 8 |
+
"prompt": 0,
|
| 9 |
+
"fp16_match": false,
|
| 10 |
+
"first_divergence": 0
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"prompt": 1,
|
| 14 |
+
"fp16_match": false,
|
| 15 |
+
"first_divergence": 1
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"prompt": 2,
|
| 19 |
+
"fp16_match": false,
|
| 20 |
+
"first_divergence": 1
|
| 21 |
+
}
|
| 22 |
+
],
|
| 23 |
+
"wall_s": 1701.4,
|
| 24 |
+
"cpu": "unknown",
|
| 25 |
+
"ts": "2026-07-27T15:26:19Z"
|
| 26 |
+
}
|
gguf/receipts_ifcs50_retired/bench_8b.json
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "8b",
|
| 3 |
+
"bench": [
|
| 4 |
+
{
|
| 5 |
+
"build_commit": "e07b3ec",
|
| 6 |
+
"build_number": 2,
|
| 7 |
+
"cpu_info": "unknown",
|
| 8 |
+
"gpu_info": "",
|
| 9 |
+
"backends": "CPU",
|
| 10 |
+
"model_filename": "/vol/gguf/neutrino-8b-ifc50-fv5.gguf",
|
| 11 |
+
"model_type": "qwen3 8B FV5 - 3.25 bpw Fermion five-value ternary",
|
| 12 |
+
"model_size": 4087058432,
|
| 13 |
+
"model_n_params": 8190735360,
|
| 14 |
+
"n_batch": 2048,
|
| 15 |
+
"n_ubatch": 512,
|
| 16 |
+
"n_threads": 16,
|
| 17 |
+
"cpu_mask": "0x0",
|
| 18 |
+
"cpu_strict": false,
|
| 19 |
+
"poll": 50,
|
| 20 |
+
"type_k": "f16",
|
| 21 |
+
"type_v": "f16",
|
| 22 |
+
"n_gpu_layers": -1,
|
| 23 |
+
"n_cpu_moe": 0,
|
| 24 |
+
"split_mode": "layer",
|
| 25 |
+
"main_gpu": 0,
|
| 26 |
+
"no_kv_offload": false,
|
| 27 |
+
"flash_attn": -1,
|
| 28 |
+
"devices": "auto",
|
| 29 |
+
"tensor_split": "0.00",
|
| 30 |
+
"tensor_buft_overrides": "none",
|
| 31 |
+
"load_mode": "mmap",
|
| 32 |
+
"embeddings": false,
|
| 33 |
+
"no_op_offload": 0,
|
| 34 |
+
"no_host": false,
|
| 35 |
+
"fit_target": 0,
|
| 36 |
+
"fit_min_ctx": 0,
|
| 37 |
+
"n_prompt": 512,
|
| 38 |
+
"n_gen": 0,
|
| 39 |
+
"n_depth": 0,
|
| 40 |
+
"test_time": "2026-07-26T18:21:45Z",
|
| 41 |
+
"avg_ns": 79011453606,
|
| 42 |
+
"stddev_ns": 10228708,
|
| 43 |
+
"avg_ts": 6.480073,
|
| 44 |
+
"stddev_ts": 0.000839,
|
| 45 |
+
"samples_ns": [
|
| 46 |
+
79004220817,
|
| 47 |
+
79018686395
|
| 48 |
+
],
|
| 49 |
+
"samples_ts": [
|
| 50 |
+
6.48067,
|
| 51 |
+
6.47948
|
| 52 |
+
]
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"build_commit": "e07b3ec",
|
| 56 |
+
"build_number": 2,
|
| 57 |
+
"cpu_info": "unknown",
|
| 58 |
+
"gpu_info": "",
|
| 59 |
+
"backends": "CPU",
|
| 60 |
+
"model_filename": "/vol/gguf/neutrino-8b-ifc50-fv5.gguf",
|
| 61 |
+
"model_type": "qwen3 8B FV5 - 3.25 bpw Fermion five-value ternary",
|
| 62 |
+
"model_size": 4087058432,
|
| 63 |
+
"model_n_params": 8190735360,
|
| 64 |
+
"n_batch": 2048,
|
| 65 |
+
"n_ubatch": 512,
|
| 66 |
+
"n_threads": 16,
|
| 67 |
+
"cpu_mask": "0x0",
|
| 68 |
+
"cpu_strict": false,
|
| 69 |
+
"poll": 50,
|
| 70 |
+
"type_k": "f16",
|
| 71 |
+
"type_v": "f16",
|
| 72 |
+
"n_gpu_layers": -1,
|
| 73 |
+
"n_cpu_moe": 0,
|
| 74 |
+
"split_mode": "layer",
|
| 75 |
+
"main_gpu": 0,
|
| 76 |
+
"no_kv_offload": false,
|
| 77 |
+
"flash_attn": -1,
|
| 78 |
+
"devices": "auto",
|
| 79 |
+
"tensor_split": "0.00",
|
| 80 |
+
"tensor_buft_overrides": "none",
|
| 81 |
+
"load_mode": "mmap",
|
| 82 |
+
"embeddings": false,
|
| 83 |
+
"no_op_offload": 0,
|
| 84 |
+
"no_host": false,
|
| 85 |
+
"fit_target": 0,
|
| 86 |
+
"fit_min_ctx": 0,
|
| 87 |
+
"n_prompt": 0,
|
| 88 |
+
"n_gen": 128,
|
| 89 |
+
"n_depth": 0,
|
| 90 |
+
"test_time": "2026-07-26T18:25:42Z",
|
| 91 |
+
"avg_ns": 25045562535,
|
| 92 |
+
"stddev_ns": 103809881,
|
| 93 |
+
"avg_ts": 5.11073,
|
| 94 |
+
"stddev_ts": 0.021183,
|
| 95 |
+
"samples_ns": [
|
| 96 |
+
25118967206,
|
| 97 |
+
24972157864
|
| 98 |
+
],
|
| 99 |
+
"samples_ts": [
|
| 100 |
+
5.09575,
|
| 101 |
+
5.12571
|
| 102 |
+
]
|
| 103 |
+
}
|
| 104 |
+
],
|
| 105 |
+
"smoke_rc": 0,
|
| 106 |
+
"smoke_text": "Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France is Paris. The capital of France",
|
| 107 |
+
"threads": 16,
|
| 108 |
+
"cpu": "unknown",
|
| 109 |
+
"nproc": 32,
|
| 110 |
+
"wall_s": 301.3,
|
| 111 |
+
"ts": "2026-07-26T18:26:46Z"
|
| 112 |
+
}
|
gguf/receipts_ifcs50_retired/convert_8b.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"status": "CONVERT_PASS",
|
| 3 |
+
"container": {
|
| 4 |
+
"name": "neutrino-8b_v4.bin",
|
| 5 |
+
"sha256": "39787a2e15dcffa20135c7c1bb1b8ed55c8632263f8457f3395f55d52007a3be"
|
| 6 |
+
},
|
| 7 |
+
"f32_gguf": {
|
| 8 |
+
"name": "neutrino-8b-ifc50-f32.gguf",
|
| 9 |
+
"bytes": 32768897920
|
| 10 |
+
},
|
| 11 |
+
"out": {
|
| 12 |
+
"name": "neutrino-8b-ifc50-fv5.gguf",
|
| 13 |
+
"bytes": 4093015136,
|
| 14 |
+
"sha256": "7dda995047dc70540d2d83f2c464dd118fff62eef1aab36ae71ceb3cc972e5d0"
|
| 15 |
+
},
|
| 16 |
+
"tensors": {
|
| 17 |
+
"fv5": 252,
|
| 18 |
+
"fv5b": 2,
|
| 19 |
+
"f32": 145,
|
| 20 |
+
"total": 399,
|
| 21 |
+
"bitwise_checked": 399
|
| 22 |
+
},
|
| 23 |
+
"tied_output_omitted": false,
|
| 24 |
+
"quantized_params": 8190427136,
|
| 25 |
+
"effective_bpw_quantized": 3.9979,
|
| 26 |
+
"header": {
|
| 27 |
+
"magic": 1129599572,
|
| 28 |
+
"version": 4,
|
| 29 |
+
"arch": 3,
|
| 30 |
+
"n_layers": 36,
|
| 31 |
+
"hidden": 4096,
|
| 32 |
+
"n_heads": 32,
|
| 33 |
+
"n_kv_heads": 8,
|
| 34 |
+
"head_dim": 128,
|
| 35 |
+
"rotary_dims": 128,
|
| 36 |
+
"intermediate": 12288,
|
| 37 |
+
"vocab": 151936,
|
| 38 |
+
"max_pos": 40960,
|
| 39 |
+
"rms_norm_eps": 9.999999974752427e-07,
|
| 40 |
+
"rope_theta": 1000000.0,
|
| 41 |
+
"eok": 2
|
| 42 |
+
},
|
| 43 |
+
"wall_seconds": 177.3
|
| 44 |
+
}
|
gguf/receipts_ifcs50_retired/gateverdict_8b_fv5.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "8b",
|
| 3 |
+
"variant": "fv5",
|
| 4 |
+
"verdict": "PASS",
|
| 5 |
+
"all_free_match": true,
|
| 6 |
+
"forced_mismatch_total": 0,
|
| 7 |
+
"gen_tok_per_s": 5.16,
|
| 8 |
+
"prompt_tok_per_s": 6.08,
|
| 9 |
+
"flips": [],
|
| 10 |
+
"unexplained": 0,
|
| 11 |
+
"gguf_sha256": "7dda995047dc70540d2d83f2c464dd118fff62eef1aab36ae71ceb3cc972e5d0",
|
| 12 |
+
"ref_trajectory_sha256": "3568b5a38cc72ec8b4cb9b61eba729b60a538647fad3ae455151b7c7a0620597",
|
| 13 |
+
"threads": 16,
|
| 14 |
+
"cpu": "unknown",
|
| 15 |
+
"wall_s": 422.5,
|
| 16 |
+
"ts": "2026-07-26T18:21:43Z"
|
| 17 |
+
}
|
gguf/receipts_ifcs50_retired/ref_8b.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "8b",
|
| 3 |
+
"dtype": "float32",
|
| 4 |
+
"steps": 128,
|
| 5 |
+
"trajectory_sha256": "3568b5a38cc72ec8b4cb9b61eba729b60a538647fad3ae455151b7c7a0620597",
|
| 6 |
+
"continuity_vs_banked_fp16": [
|
| 7 |
+
{
|
| 8 |
+
"prompt": 0,
|
| 9 |
+
"fp16_match": false,
|
| 10 |
+
"first_divergence": 0
|
| 11 |
+
},
|
| 12 |
+
{
|
| 13 |
+
"prompt": 1,
|
| 14 |
+
"fp16_match": false,
|
| 15 |
+
"first_divergence": 0
|
| 16 |
+
},
|
| 17 |
+
{
|
| 18 |
+
"prompt": 2,
|
| 19 |
+
"fp16_match": false,
|
| 20 |
+
"first_divergence": 0
|
| 21 |
+
}
|
| 22 |
+
],
|
| 23 |
+
"wall_s": 1756.0,
|
| 24 |
+
"cpu": "unknown",
|
| 25 |
+
"ts": "2026-07-26T18:08:52Z"
|
| 26 |
+
}
|
merges.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
mlx/MANIFEST.json
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"note": "MLX pack provenance: file -> bytes/sha256/source. Weights entry is declarative (not duplicated in this subfolder).",
|
| 3 |
+
"generated_utc": "2026-07-25T16:52:04Z",
|
| 4 |
+
"weights_container": {
|
| 5 |
+
"file": "neutrino-8b_v4.bin",
|
| 6 |
+
"status": "GATED ON THE RELEASE CONTAINER (cons3-B25 product bake, FOUNDER-AUTHORISED PROMOTE 2026-07-27): pathgate + refgate both PASS 2026-07-27 (fused-vs-mxops 32-token greedy IDENTICAL; TF rel max 0.000%, argmax 9/9, greedy 24/24, 0 flips) \u2014 receipts pathgate_8b_cons3b25.json / refgate_8b_cons3b25.json.",
|
| 7 |
+
"gated_on_file": "neutrino-8b_v4.bin",
|
| 8 |
+
"gated_on_bytes": 3875404812,
|
| 9 |
+
"gated_on_sha256": "016c6f362ad237a8cf4043601efbc1e766574f76ea58691e78fdef51b4155fa0",
|
| 10 |
+
"gated_on_source": "internal lab archive: archive/consolidation_3/export_v4_B25/neutrino-8b_v4.bin",
|
| 11 |
+
"retired_gates": [
|
| 12 |
+
{
|
| 13 |
+
"note": "RETIRED 2026-07-27 \u2014 d25 product bake; the pack was NEVER re-pinned to ifc-s50 even though its gates ran (receipts pathgate_8b_ifc50.json / refgate_8b_ifc50.json stayed in the lane dir and were never copied into the pack). Both are superseded by cons3-B25.",
|
| 14 |
+
"gated_on_sha256": "06af07b5f17c832186c95fce6252691ec84dbbc9320a4d4ce9ce989547c06e9b"
|
| 15 |
+
},
|
| 16 |
+
{
|
| 17 |
+
"note": "RETIRED 2026-07-27 \u2014 ifc-s50 ship brain, gated 2026-07-26 but never published in this manifest; superseded by the cons3-B25 promote.",
|
| 18 |
+
"gated_on_sha256": "39787a2e15dcffa20135c7c1bb1b8ed55c8632263f8457f3395f55d52007a3be"
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"note": "RETIRED 2026-07-26 ~10:45Z \u2014 s50 gate, superseded by d25 (round-2 winner).",
|
| 22 |
+
"gated_on_sha256": "fe85f93214376354f118a83235eeff6bc5d40e112474afacbd3e0310768b08d5"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"note": "RETIRED 2026-07-26 \u2014 original correctness gate ran on the W-baseline container (identical bytes count, different brain).",
|
| 26 |
+
"gated_on_file": "wbase_v4.bin",
|
| 27 |
+
"gated_on_sha256": "5af0c75b14fbac020324768455ce660a92110f4f71f507635fd160dd0b03f0a1",
|
| 28 |
+
"gated_on_source": "internal lab archive: archive/labarchive/dspec8b/artifacts/wbase_v4.bin"
|
| 29 |
+
}
|
| 30 |
+
]
|
| 31 |
+
},
|
| 32 |
+
"kernel_provenance": {
|
| 33 |
+
"vendored": "fermion_mlx/trit_kernels.py",
|
| 34 |
+
"repo_file": "kernels/metal/trit_kernels.py",
|
| 35 |
+
"sha256": "c56db6dd5e3743d19c54411f5f0b2dfef135f7805906a62cc4e40f1ee5e80f27",
|
| 36 |
+
"relation": "byte-identical"
|
| 37 |
+
},
|
| 38 |
+
"files": {
|
| 39 |
+
"README.md": {
|
| 40 |
+
"bytes": 5949,
|
| 41 |
+
"sha256": "1ab11a6f6e1a915db38da647ecbc66b524fed9e474b80067c60daf3d89dfaafe",
|
| 42 |
+
"source": "pack file"
|
| 43 |
+
},
|
| 44 |
+
"fermion_mlx/__init__.py": {
|
| 45 |
+
"bytes": 2306,
|
| 46 |
+
"sha256": "55d5e0a1efd92802c28f03ab526a01d4c26493fa5e523c495d2076ff56e4985a",
|
| 47 |
+
"source": "NEW: load()/generate()/chat() API"
|
| 48 |
+
},
|
| 49 |
+
"fermion_mlx/__main__.py": {
|
| 50 |
+
"bytes": 7347,
|
| 51 |
+
"sha256": "87190c0c23c712bfe5b31bc54d2dadd69a971c5d8ff8e66f05ba1fd2dbc1352f",
|
| 52 |
+
"source": "NEW: CLI (gen/chat/gate/cachegate/pathgate/bench) + spec/specgate/specbench modes (spec_paths)"
|
| 53 |
+
},
|
| 54 |
+
"fermion_mlx/arch3.py": {
|
| 55 |
+
"bytes": 7541,
|
| 56 |
+
"sha256": "a831e8a24b2b9a986b393d1c316aac66a3399a082a57176254b2feeae032f8cd",
|
| 57 |
+
"source": "NEW (mlx_packs lane 2026-07-25): fused per-head QK-RMSNorm+rope+KV-append kernel + s8 v3 x-staged int8 unembed"
|
| 58 |
+
},
|
| 59 |
+
"fermion_mlx/container.py": {
|
| 60 |
+
"bytes": 6059,
|
| 61 |
+
"sha256": "c5b352184ba9ddf9a3e424731226cb7dcb9901d3a1516c67d4557953b0101f2f",
|
| 62 |
+
"source": "NEW: TRTC v4 arch-3 reader (layout per scripts/expand_trtc_v4_to_hf.py + kernels/gpu_e2e/runtime8b.py)"
|
| 63 |
+
},
|
| 64 |
+
"fermion_mlx/gates.py": {
|
| 65 |
+
"bytes": 7454,
|
| 66 |
+
"sha256": "faf8634b0bd3afec5c4422decdbd8cc04fbb7f3426f70cdd6626fd6b177aa8b4",
|
| 67 |
+
"source": "NEW: house V1 refgate / cachegate / pathgate"
|
| 68 |
+
},
|
| 69 |
+
"fermion_mlx/mbatch.py": {
|
| 70 |
+
"bytes": 6492,
|
| 71 |
+
"sha256": "c9ec8d37f1cdc0554cda01cefbc435e67525779c79e0dac7fd760e7919e6fdd2",
|
| 72 |
+
"source": "NEW (spec_paths 2026-07-25): M-token batched trit/s8 GEMMs for speculative verify (one weight read per k+1 positions)"
|
| 73 |
+
},
|
| 74 |
+
"fermion_mlx/model.py": {
|
| 75 |
+
"bytes": 12748,
|
| 76 |
+
"sha256": "9488f8194ce8be006ce05dfaf6118353de44005ce54f38f73a0908ccac2749da",
|
| 77 |
+
"source": "NEW: MetalTritQwen3 walker (fused primary path + mx.fast-ops reference twin)"
|
| 78 |
+
},
|
| 79 |
+
"fermion_mlx/spec.py": {
|
| 80 |
+
"bytes": 11207,
|
| 81 |
+
"sha256": "969e946d5d2eeb63d44afa4c62dd2453d87fa14e83b936bc673482b59ee3b745",
|
| 82 |
+
"source": "NEW (spec_paths 2026-07-25): EXPERIMENTAL greedy-exact speculative decoding (0.6B drafts, 8B verifies), exactness-gated"
|
| 83 |
+
},
|
| 84 |
+
"fermion_mlx/trit_kernels.py": {
|
| 85 |
+
"bytes": 36881,
|
| 86 |
+
"sha256": "c56db6dd5e3743d19c54411f5f0b2dfef135f7805906a62cc4e40f1ee5e80f27",
|
| 87 |
+
"source": "VENDORED byte-identical from kernels/metal/trit_kernels.py (repo sha c56db6dd5e3743d19c54411f5f0b2dfef135f7805906a62cc4e40f1ee5e80f27) \u2014 the banked five-value Metal GEMV stack"
|
| 88 |
+
},
|
| 89 |
+
"merges.txt": {
|
| 90 |
+
"bytes": 1671853,
|
| 91 |
+
"sha256": "8831e4f1a044471340f7c0a83d7bd71306a5b867e95fd870f74d0c5308a904d5",
|
| 92 |
+
"source": "donor Qwen/Qwen3-8B (same files as the parent tree root)"
|
| 93 |
+
},
|
| 94 |
+
"receipts/bench_8b_wbase_m5_16gb_memcapped.json": {
|
| 95 |
+
"bytes": 599,
|
| 96 |
+
"sha256": "9d3b153ec49dce92846fb9140fc38267ed04a08b5adcda91f2036ce94869b555",
|
| 97 |
+
"source": "gate/bench receipt (mlx_packs lane, 2026-07-25)"
|
| 98 |
+
},
|
| 99 |
+
"receipts/pathgate_8b_wbase.json": {
|
| 100 |
+
"bytes": 612,
|
| 101 |
+
"sha256": "50d6584a9ba2dae87bf502f2e637916a604be72db473fc89977fb8e1aaf3413b",
|
| 102 |
+
"source": "gate/bench receipt (mlx_packs lane, 2026-07-25)"
|
| 103 |
+
},
|
| 104 |
+
"receipts/refgate_8b_wbase.json": {
|
| 105 |
+
"bytes": 1909,
|
| 106 |
+
"sha256": "851da3bb5c093182eb56532711dd9a95123ca44260e53de951b27c8b96eaf7a4",
|
| 107 |
+
"source": "gate/bench receipt (mlx_packs lane, 2026-07-25)"
|
| 108 |
+
},
|
| 109 |
+
"receipts/specbench_8b_0p6b_m5.json": {
|
| 110 |
+
"bytes": 826,
|
| 111 |
+
"sha256": "c36ef09659d8c7690d53c23d065943103a95417890f8defbd01e629d7b5feeca",
|
| 112 |
+
"source": "spec gate/bench receipt (spec_paths lane, 2026-07-25)"
|
| 113 |
+
},
|
| 114 |
+
"receipts/specgate_8b_0p6b.json": {
|
| 115 |
+
"bytes": 705,
|
| 116 |
+
"sha256": "64bcb90fe965835bc78887d873d3cfd0677b76c6d72cda04a84e295efee7c10c",
|
| 117 |
+
"source": "spec gate/bench receipt (spec_paths lane, 2026-07-25)"
|
| 118 |
+
},
|
| 119 |
+
"requirements.txt": {
|
| 120 |
+
"bytes": 191,
|
| 121 |
+
"sha256": "1cf4d1fa581aaa5deae0568f88388c51166bbd69e7a89080b5a53488a7ef39b6",
|
| 122 |
+
"source": "pack file"
|
| 123 |
+
},
|
| 124 |
+
"tokenizer.json": {
|
| 125 |
+
"bytes": 11422654,
|
| 126 |
+
"sha256": "aeb13307a71acd8fe81861d94ad54ab689df773318809eed3cbe794b4492dae4",
|
| 127 |
+
"source": "donor Qwen/Qwen3-8B (same files as the parent tree root)"
|
| 128 |
+
},
|
| 129 |
+
"tokenizer_config.json": {
|
| 130 |
+
"bytes": 9732,
|
| 131 |
+
"sha256": "d5d09f07b48c3086c508b30d1c9114bd1189145b74e982a265350c923acd8101",
|
| 132 |
+
"source": "donor Qwen/Qwen3-8B (same files as the parent tree root)"
|
| 133 |
+
},
|
| 134 |
+
"vocab.json": {
|
| 135 |
+
"bytes": 2776833,
|
| 136 |
+
"sha256": "ca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910",
|
| 137 |
+
"source": "donor Qwen/Qwen3-8B (same files as the parent tree root)"
|
| 138 |
+
}
|
| 139 |
+
},
|
| 140 |
+
"addenda": [
|
| 141 |
+
"2026-07-25 spec_paths: EXPERIMENTAL --spec speculative decoding added (new modules mbatch.py/spec.py + CLI modes + README section). Exactness-gated: specgate 6/6 token-identical vs plain (receipts). Existing gate battery re-verified on the updated package."
|
| 142 |
+
]
|
| 143 |
+
}
|
mlx/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Neutrino-8B — MLX pack (Apple silicon)
|
| 2 |
+
|
| 3 |
+
Run the [Neutrino-8B](../README.md) five-value (sub-2-bit) container
|
| 4 |
+
natively from Python on Apple silicon: MLX + our custom Metal kernels. The
|
| 5 |
+
~3.61 GiB TRTC v4 arch-3 container is memory-mapped and the five-value
|
| 6 |
+
{0, ±s_lo, ±s_hi} planes stay bit-packed at rest — decode happens inside
|
| 7 |
+
the GEMV kernels; nothing in the decode path is fp16/fp32 weight material.
|
| 8 |
+
|
| 9 |
+
> [!IMPORTANT]
|
| 10 |
+
> **PRODUCT-CONTAINER RE-GATE COMPLETE (2026-07-27).** The final shipping
|
| 11 |
+
> container is the cons3-B25 ship brain (`neutrino-8b_v4.bin`,
|
| 12 |
+
> 3,875,404,812 bytes, sha256 `016c6f36…55fa0`, baked 2026-07-27 from
|
| 13 |
+
> consolidation-3 arm B step 25 — see the parent card). This MLX pack's
|
| 14 |
+
> runtime + kernels are **correctness-gated on that exact container**:
|
| 15 |
+
> both gates were re-run against it on an Apple M5 (16 GB, MLX 0.31.2,
|
| 16 |
+
> 6 GiB cap) and both PASS — receipts `receipts/pathgate_8b_cons3b25.json`
|
| 17 |
+
> and `receipts/refgate_8b_cons3b25.json`. The runtime is geometry-driven
|
| 18 |
+
> (header-dispatched), so the swap involved no code change.
|
| 19 |
+
>
|
| 20 |
+
> RETIRED, do **not** verify against: ifc-s50 `39787a2e…07a3be`, d25
|
| 21 |
+
> `06af07b5…c06e9b`, s50 `fe85f932…768b08d5`, wbase `5af0c75b…03f0a1`.
|
| 22 |
+
|
| 23 |
+
## Install & run
|
| 24 |
+
|
| 25 |
+
```bash
|
| 26 |
+
pip install -r requirements.txt # mlx, numpy, transformers
|
| 27 |
+
python -m fermion_mlx --model neutrino-8b_v4.bin --mode chat \
|
| 28 |
+
--tokenizer . --prompt "Why is the sky blue?"
|
| 29 |
+
```
|
| 30 |
+
|
| 31 |
+
Python API:
|
| 32 |
+
|
| 33 |
+
```python
|
| 34 |
+
import fermion_mlx
|
| 35 |
+
from transformers import AutoTokenizer
|
| 36 |
+
|
| 37 |
+
model = fermion_mlx.load("neutrino-8b_v4.bin") # ~4.2 GiB unified memory
|
| 38 |
+
tok = AutoTokenizer.from_pretrained(".")
|
| 39 |
+
text, stats = fermion_mlx.chat(model, tok,
|
| 40 |
+
[{"role": "user", "content": "hi"}])
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
Memory: the loaded model holds ~3.8 GiB of packed planes + int8 embedding
|
| 44 |
+
lanes (untied: separate embed_in and lm_head records) plus the KV cache
|
| 45 |
+
(`maxseq` × 302 KiB/layer fp32). A 16 GB Mac runs it; 8 GB does not.
|
| 46 |
+
`--mem-limit-gb` makes the process fail fast instead of pressuring the
|
| 47 |
+
machine.
|
| 48 |
+
|
| 49 |
+
## What runs on the GPU
|
| 50 |
+
|
| 51 |
+
Identical stack to the Neutrino-0.6B MLX pack (one geometry-generic
|
| 52 |
+
runtime): five-value 3-bit-planar GEMV kernels (v7 LUT decode) with fused
|
| 53 |
+
RMSNorm/SwiGLU/residual epilogues, the fused arch-3 per-head
|
| 54 |
+
Q/K-RMSNorm + rope + KV-append kernel, int8 embed/unembed lanes with
|
| 55 |
+
threadgroup x-staging, MLX SDPA with block-grown windows, `mx.compile`d
|
| 56 |
+
step.
|
| 57 |
+
|
| 58 |
+
## Correctness receipts
|
| 59 |
+
|
| 60 |
+
House rule: greedy streams token-identical to the container's own fp32
|
| 61 |
+
expansion reference; near-tie flips at tiny logit margins are the only
|
| 62 |
+
tolerated class, each documented with its margin. Receipts in `receipts/`
|
| 63 |
+
(gated on the **shipping** container `016c6f36…55fa0`, re-run 2026-07-27):
|
| 64 |
+
|
| 65 |
+
| gate | reference | result |
|
| 66 |
+
|---|---|---|
|
| 67 |
+
| V1 refgate | fp32 HF expansion of **this** container (eager attn), teacher-forced 9 pos + 24-token greedy | **PASS — TF rel err 0.000% (max), cos 1.000000, argmax 9/9, greedy 24/24, 0 flips** |
|
| 68 |
+
| pathgate | fused-kernel path vs pure `mx.fast`-ops twin | 32-token greedy IDENTICAL |
|
| 69 |
+
|
| 70 |
+
## Speed (honest numbers, with venue)
|
| 71 |
+
|
| 72 |
+
- Apple-silicon M-series decode receipt for the 8B: **25.0 tok/s median**
|
| 73 |
+
(128-token greedy × 3 runs, 24.3–25.6, peak MLX memory **3.67 GiB**),
|
| 74 |
+
measured under a strict 6 GiB memory cap on a 16 GB M5 MacBook, macOS,
|
| 75 |
+
MLX 0.31.2, 2026-07-25 (wbase container;
|
| 76 |
+
`receipts/bench_8b_wbase_m5_16gb_memcapped.json`). Decode is
|
| 77 |
+
memory-bandwidth-bound, so this is representative for M5-class machines;
|
| 78 |
+
a fuller M-series story (longer runs, larger windows, bigger-RAM
|
| 79 |
+
machines) lands with the Metal arch-3 port rollout. The CUDA receipts in
|
| 80 |
+
the parent tree (H100: 379 tok/s plain greedy, up to 650+ tok/s with
|
| 81 |
+
certified speculative decoding — **research-harness numbers**, measured
|
| 82 |
+
on the research stack, not the shipped binaries) and the shipped CPU
|
| 83 |
+
`bin/` fermion-run path remain the primary 8B performance story.
|
| 84 |
+
|
| 85 |
+
## EXPERIMENTAL: speculative decoding with the 0.6B pack as draft
|
| 86 |
+
|
| 87 |
+
The package ships a `--spec` mode where the Neutrino-0.6B pack drafts and
|
| 88 |
+
this 8B verifies, with the house **greedy-exact acceptance rule**: a draft
|
| 89 |
+
token is accepted iff it equals the verifier's argmax at that position, so
|
| 90 |
+
the emitted stream is exactness-gated against plain decoding (receipt:
|
| 91 |
+
`receipts/specgate_8b_0p6b.json` — 3 prompts × {32, 256} tokens, spec vs
|
| 92 |
+
plain **token-identical 6/6**, both models in one process under a 6 GiB
|
| 93 |
+
MLX cap).
|
| 94 |
+
|
| 95 |
+
```bash
|
| 96 |
+
python -m fermion_mlx --model neutrino-8b_v4.bin \
|
| 97 |
+
--draft-model neutrino-0.6b_v4.bin --mode spec \
|
| 98 |
+
--tokenizer . --prompt "..." --mem-limit-gb 6 # --spec-k 4 default
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
Honest numbers (M5 MacBook 16 GB, 6 GiB cap, k=4, 512-token greedy × 3
|
| 102 |
+
runs, medians; `receipts/specbench_8b_0p6b_m5.json`):
|
| 103 |
+
|
| 104 |
+
| text type | plain 8B | 8B + 0.6B draft | speedup | acceptance |
|
| 105 |
+
|---|---|---|---|---|
|
| 106 |
+
| facts | 22.0 tok/s | **25.7 tok/s** | **1.17×** | 74% |
|
| 107 |
+
| chat-ish | 17.5 tok/s | 14.7 tok/s | 0.84× | 44% |
|
| 108 |
+
| prose | 17.9 tok/s | 12.2 tok/s | 0.68× | 30% |
|
| 109 |
+
|
| 110 |
+
Read before enabling: the speedup is real only on text where the 0.6B
|
| 111 |
+
drafts well (repetitive/factual continuations). Acceptance is
|
| 112 |
+
text-dependent because the draft is the *base* 0.6B, not a model distilled
|
| 113 |
+
from the 8B; and the current batched-verify kernel costs ~2.8× a single
|
| 114 |
+
step (M-wide mask-convert decode is compute-bound), so break-even needs
|
| 115 |
+
~2.9 accepted tokens/round. A LUT-based batched decode + adaptive k are
|
| 116 |
+
the known day-2 upgrades. This flag never changes outputs (exactness rule
|
| 117 |
+
above) — worst case it changes speed. Not memory contention: both-model
|
| 118 |
+
peak stayed under 4.3 GiB of the 6 GiB cap.
|
| 119 |
+
|
| 120 |
+
## Files
|
| 121 |
+
|
| 122 |
+
- `fermion_mlx/` — loader/runtime package (same bytes as the 0.6B pack's).
|
| 123 |
+
- tokenizer files — donor Qwen3-8B (shipped in the parent tree root too).
|
| 124 |
+
- `receipts/` — gate receipts (JSON), kernel-source sha sidecar.
|
| 125 |
+
- `MANIFEST.json` — file-by-file provenance + shas.
|
| 126 |
+
|
| 127 |
+
Weights: not duplicated in this subfolder — the parent `MANIFEST.json`
|
| 128 |
+
governs the container (interim vs final product swap included).
|
| 129 |
+
|
| 130 |
+
## License
|
| 131 |
+
|
| 132 |
+
Apache-2.0 (this pack's code). Weights per the parent card (Apache-2.0,
|
| 133 |
+
derivative of Qwen/Qwen3-8B). `bin/` EULA unchanged.
|
mlx/fermion_mlx/__init__.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""fermion_mlx — run Fermion Research TRTC v4 arch-3 (Qwen3-family) ternary
|
| 2 |
+
containers on Apple silicon via MLX + custom Metal kernels.
|
| 3 |
+
|
| 4 |
+
import fermion_mlx
|
| 5 |
+
model = fermion_mlx.load("neutrino-0p6b_v4_base.bin")
|
| 6 |
+
text = fermion_mlx.generate(model, tokenizer, "The capital of France is",
|
| 7 |
+
max_tokens=64)
|
| 8 |
+
|
| 9 |
+
The five-value {0, ±s_lo, ±s_hi} weights stay bit-packed at rest and are
|
| 10 |
+
decoded inside the GEMV kernels; nothing is expanded to fp16/fp32 weights.
|
| 11 |
+
Kernel version: env TRIT_METAL_V (default 7 = the banked LUT-decode bodies).
|
| 12 |
+
"""
|
| 13 |
+
import os
|
| 14 |
+
|
| 15 |
+
# choose the crown kernel bodies before the kernel module is imported
|
| 16 |
+
os.environ.setdefault("TRIT_METAL_V", "7")
|
| 17 |
+
|
| 18 |
+
from .model import MetalTritQwen3 # noqa: E402
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def load(container_path, maxseq=1024, verbose=True):
|
| 22 |
+
"""Memory-map a TRTC v4 arch-3 container and build the Metal model."""
|
| 23 |
+
return MetalTritQwen3(container_path, maxseq=maxseq, verbose=verbose)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
DEFAULT_EOS = (151645, 151643) # <|im_end|>, <|endoftext|>
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _as_ids(x):
|
| 30 |
+
"""Normalise whatever a tokenizer handed back into a flat list of ids.
|
| 31 |
+
|
| 32 |
+
transformers >= 4.56 flipped `apply_chat_template(tokenize=True)` to
|
| 33 |
+
return a BatchEncoding instead of a list of ids (return_dict defaults to
|
| 34 |
+
True). requirements.txt says `transformers>=4.53`, so a fresh install gets
|
| 35 |
+
the new behaviour and the old code fed a BatchEncoding straight into
|
| 36 |
+
`model.prefill`, which died with
|
| 37 |
+
`int() argument must be ... not 'tokenizers.Encoding'`.
|
| 38 |
+
"""
|
| 39 |
+
if hasattr(x, "input_ids"):
|
| 40 |
+
x = x.input_ids
|
| 41 |
+
elif isinstance(x, dict) and "input_ids" in x:
|
| 42 |
+
x = x["input_ids"]
|
| 43 |
+
if len(x) and isinstance(x[0], (list, tuple)): # batched -> first row
|
| 44 |
+
x = x[0]
|
| 45 |
+
return [int(t) for t in x]
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def _rp_greedy(model, ids, n, rp, eos=None):
|
| 49 |
+
"""Greedy decode with the CTRL/HF multiplicative repetition penalty.
|
| 50 |
+
|
| 51 |
+
Byte-for-byte the rule the graded MLX battery used
|
| 52 |
+
(handoffs/results_0p6b_chatv2/scripts/local_mlx_battery_v2.py:30-54,
|
| 53 |
+
`rp_greedy`): the penalty applies to every id SEEN SO FAR in the whole
|
| 54 |
+
context, not to a 256-token window — that is the MLX-side convention and
|
| 55 |
+
it is what the banked MLX numbers on the model card were produced at.
|
| 56 |
+
|
| 57 |
+
This path materialises the logits every step, so it forfeits the fused
|
| 58 |
+
fast path's one-step async lookahead. It is only taken when rp != 1.0;
|
| 59 |
+
`model.generate` (rp = 1.0) is untouched, so the banked tok/s receipts
|
| 60 |
+
still describe the default.
|
| 61 |
+
"""
|
| 62 |
+
import time
|
| 63 |
+
|
| 64 |
+
import mlx.core as mx
|
| 65 |
+
import numpy as np
|
| 66 |
+
|
| 67 |
+
eos = set(eos or ())
|
| 68 |
+
mask = model.new_mask()
|
| 69 |
+
x, pos0 = model.prefill(ids, mask)
|
| 70 |
+
seen = set(int(t) for t in ids)
|
| 71 |
+
toks = []
|
| 72 |
+
t0 = time.perf_counter()
|
| 73 |
+
for i in range(n):
|
| 74 |
+
pb = mx.array([pos0 + i], dtype=mx.int32)
|
| 75 |
+
W = model._window(pos0 + i)
|
| 76 |
+
nt, xn, lg = model.step_compiled(x, pb, mask, W)
|
| 77 |
+
lgn = np.array(lg, copy=True)
|
| 78 |
+
idxs = np.fromiter(seen, dtype=np.int64)
|
| 79 |
+
vals = lgn[idxs]
|
| 80 |
+
lgn[idxs] = np.where(vals > 0, vals / rp, vals * rp)
|
| 81 |
+
tsel = int(lgn.argmax())
|
| 82 |
+
toks.append(tsel)
|
| 83 |
+
if tsel in eos:
|
| 84 |
+
break
|
| 85 |
+
x = model.embed(tsel)
|
| 86 |
+
seen.add(tsel)
|
| 87 |
+
return toks, time.perf_counter() - t0
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def _decode(model, tokenizer, ids, max_tokens, eos, rep_penalty):
|
| 91 |
+
if rep_penalty and float(rep_penalty) != 1.0:
|
| 92 |
+
toks, dt = _rp_greedy(model, ids, max_tokens, float(rep_penalty),
|
| 93 |
+
eos=eos)
|
| 94 |
+
else:
|
| 95 |
+
toks, dt = model.generate(ids, max_tokens, eos=eos)
|
| 96 |
+
if eos:
|
| 97 |
+
cut = next((i for i, t in enumerate(toks) if t in eos), len(toks))
|
| 98 |
+
toks = toks[:cut]
|
| 99 |
+
text = tokenizer.decode(toks)
|
| 100 |
+
rate = (len(toks) or 1) / dt if dt > 0 else 0.0
|
| 101 |
+
return text, {"tokens": len(toks), "seconds": dt, "tok_s": rate}
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def generate(model, tokenizer, prompt, max_tokens=256, stop_on_eos=True,
|
| 105 |
+
rep_penalty=1.0):
|
| 106 |
+
"""Greedy completion of a plain-text prompt. Returns (text, stats).
|
| 107 |
+
|
| 108 |
+
`rep_penalty` > 1.0 enables the CTRL-style repetition penalty the graded
|
| 109 |
+
MLX battery ran at (1.05 on the chat SKU); 1.0, the default, is the
|
| 110 |
+
untouched pure-argmax fast path.
|
| 111 |
+
"""
|
| 112 |
+
ids = _as_ids(tokenizer(prompt))
|
| 113 |
+
eos = set(DEFAULT_EOS) if stop_on_eos else None
|
| 114 |
+
return _decode(model, tokenizer, ids, max_tokens, eos, rep_penalty)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def chat(model, tokenizer, messages, max_tokens=256, rep_penalty=1.0):
|
| 118 |
+
"""Chat completion via the donor chat template (thinking off).
|
| 119 |
+
|
| 120 |
+
`rep_penalty` as in `generate`; the chat SKU's card documents 1.05.
|
| 121 |
+
"""
|
| 122 |
+
try:
|
| 123 |
+
raw = tokenizer.apply_chat_template(messages, add_generation_prompt=True,
|
| 124 |
+
enable_thinking=False)
|
| 125 |
+
except TypeError:
|
| 126 |
+
raw = tokenizer.apply_chat_template(messages, add_generation_prompt=True)
|
| 127 |
+
ids = _as_ids(raw)
|
| 128 |
+
return _decode(model, tokenizer, ids, max_tokens, set(DEFAULT_EOS),
|
| 129 |
+
rep_penalty)
|
mlx/fermion_mlx/__main__.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""fermion_mlx CLI.
|
| 3 |
+
|
| 4 |
+
python -m fermion_mlx --model x.bin --mode gen --prompt "..."
|
| 5 |
+
python -m fermion_mlx --model x.bin --mode chat --prompt "..." --tokenizer DIR
|
| 6 |
+
python -m fermion_mlx --model x.bin --mode chat --prompt "..." --tokenizer DIR \
|
| 7 |
+
--rep-penalty 1.05 # the graded chat-SKU config
|
| 8 |
+
python -m fermion_mlx --model x.bin --mode gate --ref wbase_ref.npz
|
| 9 |
+
python -m fermion_mlx --model x.bin --mode cachegate --cache hf_greedy_cache.json
|
| 10 |
+
python -m fermion_mlx --model x.bin --mode pathgate
|
| 11 |
+
python -m fermion_mlx --model x.bin --mode bench --runs 5 --gen-tokens 512
|
| 12 |
+
|
| 13 |
+
EXPERIMENTAL speculative decoding (0.6B drafts, exactness-gated):
|
| 14 |
+
python -m fermion_mlx --model 8b.bin --draft-model 0.6b.bin --mode spec \
|
| 15 |
+
--prompt "..." --tokenizer DIR --mem-limit-gb 6
|
| 16 |
+
python -m fermion_mlx --model 8b.bin --draft-model 0.6b.bin --mode specgate \
|
| 17 |
+
--tokenizer DIR --mem-limit-gb 6 --receipt specgate.json
|
| 18 |
+
python -m fermion_mlx --model 8b.bin --draft-model 0.6b.bin --mode specbench \
|
| 19 |
+
--tokenizer DIR --mem-limit-gb 6 --receipt specbench.json
|
| 20 |
+
|
| 21 |
+
Gate receipts are written next to --receipt (JSON) when given.
|
| 22 |
+
"""
|
| 23 |
+
import argparse
|
| 24 |
+
import json
|
| 25 |
+
import sys
|
| 26 |
+
import time
|
| 27 |
+
|
| 28 |
+
import mlx.core as mx
|
| 29 |
+
|
| 30 |
+
import fermion_mlx
|
| 31 |
+
from fermion_mlx.gates import refgate, cachegate, pathgate
|
| 32 |
+
|
| 33 |
+
HOUSE_PROMPT = "The key idea behind ternary neural networks is"
|
| 34 |
+
# tokenizer-free fallback: Qwen3-tokenized HOUSE_PROMPT (donor-verified ids)
|
| 35 |
+
HOUSE_IDS = [785, 1376, 4522, 4815, 71617, 658, 29728, 14155, 374]
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _tok(args):
|
| 39 |
+
from transformers import AutoTokenizer
|
| 40 |
+
return AutoTokenizer.from_pretrained(args.tokenizer)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def main():
|
| 44 |
+
ap = argparse.ArgumentParser()
|
| 45 |
+
ap.add_argument("--model", required=True)
|
| 46 |
+
ap.add_argument("--mode", default="gen",
|
| 47 |
+
choices=["gen", "chat", "gate", "cachegate", "pathgate",
|
| 48 |
+
"bench", "spec", "specgate", "specbench"])
|
| 49 |
+
ap.add_argument("--draft-model", default="",
|
| 50 |
+
help="draft container for --mode spec* (e.g. the 0.6B pack)")
|
| 51 |
+
ap.add_argument("--spec-k", type=int, default=4,
|
| 52 |
+
help="draft tokens per speculative round")
|
| 53 |
+
ap.add_argument("--prompt", default=HOUSE_PROMPT)
|
| 54 |
+
ap.add_argument("--tokenizer", default="Qwen/Qwen3-0.6B")
|
| 55 |
+
ap.add_argument("--ref", default="")
|
| 56 |
+
ap.add_argument("--cache", default="")
|
| 57 |
+
ap.add_argument("--receipt", default="")
|
| 58 |
+
ap.add_argument("--tie-tol", type=float, default=0.04, dest="tie_tol")
|
| 59 |
+
ap.add_argument("--max-flips", type=int, default=8, dest="max_flips")
|
| 60 |
+
ap.add_argument("--gen-tokens", type=int, default=512)
|
| 61 |
+
ap.add_argument("--runs", type=int, default=5)
|
| 62 |
+
ap.add_argument("--maxseq", type=int, default=1024)
|
| 63 |
+
ap.add_argument("--no-tokenizer", action="store_true",
|
| 64 |
+
help="use the pinned house prompt ids (no transformers)")
|
| 65 |
+
ap.add_argument("--mem-limit-gb", type=float, default=0.0,
|
| 66 |
+
help="hard MLX memory limit; the process fails fast "
|
| 67 |
+
"instead of pressuring the machine (house OOM rule)")
|
| 68 |
+
ap.add_argument("--rep-penalty", type=float, default=1.0,
|
| 69 |
+
dest="rep_penalty",
|
| 70 |
+
help="CTRL-style repetition penalty over the whole "
|
| 71 |
+
"context, the rule the graded MLX battery ran at "
|
| 72 |
+
"(1.05 on the chat SKU). 1.0 (default) = the "
|
| 73 |
+
"untouched pure-argmax fast path.")
|
| 74 |
+
args = ap.parse_args()
|
| 75 |
+
|
| 76 |
+
if args.mem_limit_gb > 0:
|
| 77 |
+
mx.set_memory_limit(int(args.mem_limit_gb * 2**30))
|
| 78 |
+
print(f"[mem] MLX memory limit set to {args.mem_limit_gb:.1f} GiB")
|
| 79 |
+
|
| 80 |
+
model = fermion_mlx.load(args.model, maxseq=args.maxseq)
|
| 81 |
+
|
| 82 |
+
def save(rec):
|
| 83 |
+
if args.receipt:
|
| 84 |
+
json.dump(rec, open(args.receipt, "w"), indent=1)
|
| 85 |
+
print(f"receipt -> {args.receipt}")
|
| 86 |
+
|
| 87 |
+
if args.mode in ("spec", "specgate", "specbench"):
|
| 88 |
+
from fermion_mlx.spec import SpecDecoder, specgate, specbench
|
| 89 |
+
assert args.draft_model, "--mode spec* needs --draft-model"
|
| 90 |
+
draft = fermion_mlx.load(args.draft_model, maxseq=args.maxseq)
|
| 91 |
+
tok = _tok(args)
|
| 92 |
+
if args.mode == "specgate":
|
| 93 |
+
rec = specgate(model, draft, tok, k=args.spec_k,
|
| 94 |
+
receipt=args.receipt or None)
|
| 95 |
+
print(f"[specgate] {rec['verdict']}")
|
| 96 |
+
sys.exit(0 if rec["verdict"] == "PASS" else 1)
|
| 97 |
+
if args.mode == "specbench":
|
| 98 |
+
rec = specbench(model, draft, tok, k=args.spec_k,
|
| 99 |
+
n=args.gen_tokens, runs=args.runs,
|
| 100 |
+
receipt=args.receipt or None)
|
| 101 |
+
for t, v in rec["types"].items():
|
| 102 |
+
print(f"[specbench {t}] plain {v['plain_median_tok_s']} -> "
|
| 103 |
+
f"spec {v['spec_median_tok_s']} tok/s "
|
| 104 |
+
f"({v['speedup']}x, acc {v['acceptance_median']})")
|
| 105 |
+
return
|
| 106 |
+
sd = SpecDecoder(model, draft, k=args.spec_k)
|
| 107 |
+
ids = tok(args.prompt, add_special_tokens=False).input_ids
|
| 108 |
+
toks, dt, st = sd.generate(ids, args.gen_tokens)
|
| 109 |
+
print(tok.decode(toks))
|
| 110 |
+
print(f"[spec: {len(toks)} tok in {dt*1e3:.0f} ms -> "
|
| 111 |
+
f"{len(toks)/dt:.1f} tok/s | acceptance {st['acceptance']:.3f} "
|
| 112 |
+
f"| {st['tokens_per_round']} tok/round]")
|
| 113 |
+
return
|
| 114 |
+
|
| 115 |
+
if args.mode == "gate":
|
| 116 |
+
ok, rec = refgate(model, args.ref, args.tie_tol, args.max_flips)
|
| 117 |
+
rec["mode"], rec["model"] = "refgate", args.model
|
| 118 |
+
save(rec)
|
| 119 |
+
sys.exit(0 if ok else 1)
|
| 120 |
+
|
| 121 |
+
if args.mode == "cachegate":
|
| 122 |
+
ok, rec = cachegate(model, args.model, args.cache, args.tie_tol,
|
| 123 |
+
args.max_flips)
|
| 124 |
+
rec["mode"], rec["model"] = "cachegate", args.model
|
| 125 |
+
save(rec)
|
| 126 |
+
sys.exit(0 if ok else 1)
|
| 127 |
+
|
| 128 |
+
if args.mode == "pathgate":
|
| 129 |
+
ids = (HOUSE_IDS if args.no_tokenizer else
|
| 130 |
+
fermion_mlx._as_ids(_tok(args)(args.prompt)))
|
| 131 |
+
ok, rec = pathgate(model, ids, n=32)
|
| 132 |
+
rec["mode"], rec["model"] = "pathgate", args.model
|
| 133 |
+
save(rec)
|
| 134 |
+
sys.exit(0 if ok else 1)
|
| 135 |
+
|
| 136 |
+
if args.mode == "bench":
|
| 137 |
+
ids = (HOUSE_IDS if args.no_tokenizer else
|
| 138 |
+
fermion_mlx._as_ids(_tok(args)(args.prompt)))
|
| 139 |
+
n = args.gen_tokens
|
| 140 |
+
model.generate(ids, 16) # warmup + kernel JIT
|
| 141 |
+
rates, runs = [], []
|
| 142 |
+
for r in range(args.runs):
|
| 143 |
+
toks, dt = model.generate(ids, n) # no EOS stop: full budget
|
| 144 |
+
rates.append(n / dt)
|
| 145 |
+
runs.append({"run": r, "tokens": n, "seconds": dt, "tok_s": n / dt})
|
| 146 |
+
print(f" run {r}: {n} tok in {dt*1e3:.0f} ms -> {rates[-1]:.1f} tok/s")
|
| 147 |
+
rates.sort()
|
| 148 |
+
med = rates[len(rates) // 2]
|
| 149 |
+
peak = mx.get_peak_memory() / 2**30
|
| 150 |
+
print(f"[bench] median {med:.1f} tok/s over {args.runs} runs "
|
| 151 |
+
f"({n}-token greedy), peak mem {peak:.2f} GiB")
|
| 152 |
+
save({"mode": "bench", "model": args.model, "prompt_ids": ids,
|
| 153 |
+
"gen_tokens": n, "runs": runs, "median_tok_s": med,
|
| 154 |
+
"peak_mem_gib": peak,
|
| 155 |
+
"utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())})
|
| 156 |
+
return
|
| 157 |
+
|
| 158 |
+
tok = _tok(args)
|
| 159 |
+
if args.mode == "gen":
|
| 160 |
+
text, st = fermion_mlx.generate(model, tok, args.prompt,
|
| 161 |
+
max_tokens=args.gen_tokens,
|
| 162 |
+
rep_penalty=args.rep_penalty)
|
| 163 |
+
print(text)
|
| 164 |
+
print(f"[{st['tokens']} tokens in {st['seconds']*1e3:.0f} ms -> "
|
| 165 |
+
f"{st['tok_s']:.1f} tok/s]")
|
| 166 |
+
else:
|
| 167 |
+
text, st = fermion_mlx.chat(model, tok,
|
| 168 |
+
[{"role": "user", "content": args.prompt}],
|
| 169 |
+
max_tokens=args.gen_tokens,
|
| 170 |
+
rep_penalty=args.rep_penalty)
|
| 171 |
+
print(text)
|
| 172 |
+
print(f"[{st['tokens']} tokens in {st['seconds']*1e3:.0f} ms -> "
|
| 173 |
+
f"{st['tok_s']:.1f} tok/s]")
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
if __name__ == "__main__":
|
| 177 |
+
main()
|
mlx/fermion_mlx/arch3.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Arch-3 (Qwen3) Metal kernel: fused per-head Q/K-RMSNorm + NeoX rope +
|
| 3 |
+
KV-cache append, one dispatch per decode step.
|
| 4 |
+
|
| 5 |
+
The Qwen3 arch delta vs arch-1 is the per-head RMSNorm applied to the qkv
|
| 6 |
+
GEMV output BEFORE rope (HF order: q_norm/k_norm over head_dim, weights
|
| 7 |
+
shared across heads). This kernel is the arch-3 twin of
|
| 8 |
+
trit_kernels.rope_append: one simdgroup per head; q/k heads compute the
|
| 9 |
+
head's rms via simd_sum then rope the normed values; v heads copy raw
|
| 10 |
+
values into the cache. kc/vc/mask are written in place at position pos
|
| 11 |
+
(stream-serial ordering with the consumer SDPA — the banked rope_append
|
| 12 |
+
pattern; correctness enforced by the e2e gates).
|
| 13 |
+
"""
|
| 14 |
+
import mlx.core as mx
|
| 15 |
+
|
| 16 |
+
_CACHE = {}
|
| 17 |
+
SG_PER_TG = 8
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def _s8_source_v3(rows, cols, rs, sg_per_tg, fuse_norm, eps, rpsg=8):
|
| 21 |
+
"""arch-3 lane addition: int8 GEMV with threadgroup x staging.
|
| 22 |
+
|
| 23 |
+
The vendored s8 v2 kernel re-reads the full x vector from device memory
|
| 24 |
+
once per simdgroup (rows/rpsg simdgroups x cols floats — for the 151936-
|
| 25 |
+
row Qwen3 unembed that is +50% of the weight bytes). v3 stages the
|
| 26 |
+
effective x (rmsnorm applied) into threadgroup memory once per
|
| 27 |
+
THREADGROUP, tile by tile; every simdgroup then streams only its int8
|
| 28 |
+
rows. Requires rows % (rpsg * sg_per_tg) == 0 (true for vocab 151936)."""
|
| 29 |
+
assert rs % 4 == 0 and cols % 4 == 0 and rows % (rpsg * sg_per_tg) == 0
|
| 30 |
+
tile = min(cols, 1024)
|
| 31 |
+
assert cols % tile == 0
|
| 32 |
+
ntiles = cols // tile
|
| 33 |
+
nthreads = sg_per_tg * 32
|
| 34 |
+
if fuse_norm:
|
| 35 |
+
norm_pre = f"""
|
| 36 |
+
threadgroup float ssq_sh[{sg_per_tg}];
|
| 37 |
+
{{
|
| 38 |
+
float ssq = 0.0f;
|
| 39 |
+
for (uint c = tid; c < {cols}u; c += {nthreads}u) {{
|
| 40 |
+
float v = x[c]; ssq = metal::fma(v, v, ssq);
|
| 41 |
+
}}
|
| 42 |
+
ssq = metal::simd_sum(ssq);
|
| 43 |
+
if (lane == 0u) ssq_sh[sg] = ssq;
|
| 44 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 45 |
+
float tt = 0.0f;
|
| 46 |
+
for (uint i = 0u; i < {sg_per_tg}u; ++i) tt += ssq_sh[i];
|
| 47 |
+
rms = metal::rsqrt(tt / {float(cols)}f + {eps:.9g}f);
|
| 48 |
+
}}
|
| 49 |
+
"""
|
| 50 |
+
stage = f"xb[cl] = x[c] * nw[c] * rms;"
|
| 51 |
+
else:
|
| 52 |
+
norm_pre = ""
|
| 53 |
+
stage = "xb[cl] = x[c];"
|
| 54 |
+
return f"""
|
| 55 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 56 |
+
uint lane = tid & 31u;
|
| 57 |
+
uint sg = tid >> 5u;
|
| 58 |
+
uint row0 = (threadgroup_position_in_grid.x * {sg_per_tg}u + sg) * {rpsg}u;
|
| 59 |
+
float rms = 1.0f;
|
| 60 |
+
{norm_pre}
|
| 61 |
+
threadgroup float xb[{tile}];
|
| 62 |
+
float acc[{rpsg}];
|
| 63 |
+
for (uint r = 0u; r < {rpsg}u; ++r) acc[r] = 0.0f;
|
| 64 |
+
const float4 one4 = float4(1.0f);
|
| 65 |
+
for (uint t = 0u; t < {ntiles}u; ++t) {{
|
| 66 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 67 |
+
for (uint cl = tid; cl < {tile}u; cl += {nthreads}u) {{
|
| 68 |
+
uint c = t * {tile}u + cl;
|
| 69 |
+
{stage}
|
| 70 |
+
}}
|
| 71 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 72 |
+
for (uint i = lane * 4u; i < {tile}u; i += 128u) {{
|
| 73 |
+
float4 xv = *(const threadgroup float4*)(xb + i);
|
| 74 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 75 |
+
char4 wv = *(const device char4*)((const device char*)W
|
| 76 |
+
+ (ulong)(row0 + r) * {rs}u + t * {tile}u + i);
|
| 77 |
+
acc[r] += metal::dot(float4(wv.x, wv.y, wv.z, wv.w) * xv, one4);
|
| 78 |
+
}}
|
| 79 |
+
}}
|
| 80 |
+
}}
|
| 81 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 82 |
+
float a = metal::simd_sum(acc[r]);
|
| 83 |
+
if (lane == 0u) out[row0 + r] = scale[row0 + r] * a;
|
| 84 |
+
}}
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def get_s8_gemv_v3(rows, cols, rs, fuse_norm=False, eps=1e-6):
|
| 89 |
+
key = ("s8v3", rows, cols, rs, fuse_norm, round(eps, 12))
|
| 90 |
+
if key in _CACHE:
|
| 91 |
+
return _CACHE[key]
|
| 92 |
+
inputs = ["W", "scale", "x"]
|
| 93 |
+
if fuse_norm:
|
| 94 |
+
inputs.append("nw")
|
| 95 |
+
src = _s8_source_v3(rows, cols, rs, SG_PER_TG, fuse_norm, eps)
|
| 96 |
+
k = mx.fast.metal_kernel(name="s8gemv3_r%d_c%d_n%d" % (rows, cols, fuse_norm),
|
| 97 |
+
input_names=inputs, output_names=["out"], source=src)
|
| 98 |
+
_CACHE[key] = (k, inputs)
|
| 99 |
+
return _CACHE[key]
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def s8_gemv_v3(W, scale, x, norm_w=None, eps=1e-6, stream=None):
|
| 103 |
+
"""drop-in for trit_kernels.s8_gemv where rows % 64 == 0."""
|
| 104 |
+
rows, rs = W.shape
|
| 105 |
+
cols = x.shape[0]
|
| 106 |
+
k, names = get_s8_gemv_v3(rows, cols, rs, norm_w is not None, eps)
|
| 107 |
+
vals = {"W": W, "scale": scale, "x": x, "nw": norm_w}
|
| 108 |
+
nsg = rows // 8
|
| 109 |
+
ntg = (nsg + SG_PER_TG - 1) // SG_PER_TG
|
| 110 |
+
return k(inputs=[vals[n] for n in names],
|
| 111 |
+
grid=(ntg * SG_PER_TG * 32, 1, 1), threadgroup=(SG_PER_TG * 32, 1, 1),
|
| 112 |
+
output_shapes=[(rows,)], output_dtypes=[mx.float32], stream=stream)[0]
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def get_qknorm_rope_append(n_heads, n_kv, hd, maxseq, eps):
|
| 116 |
+
key = ("qkra", n_heads, n_kv, hd, maxseq, round(eps, 12))
|
| 117 |
+
if key in _CACHE:
|
| 118 |
+
return _CACHE[key]
|
| 119 |
+
h2 = hd // 2
|
| 120 |
+
nheads_total = n_heads + 2 * n_kv
|
| 121 |
+
src = f"""
|
| 122 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 123 |
+
uint lane = tid & 31u;
|
| 124 |
+
uint sg = tid >> 5u;
|
| 125 |
+
uint head = threadgroup_position_in_grid.x * {SG_PER_TG}u + sg;
|
| 126 |
+
if (head >= {nheads_total}u) return;
|
| 127 |
+
uint pos = (uint)posbuf[0];
|
| 128 |
+
if (head == 0u && lane == 0u) {{ ((device float*)mask)[pos] = 0.0f; }}
|
| 129 |
+
device float* kc_w = (device float*)kc;
|
| 130 |
+
device float* vc_w = (device float*)vc;
|
| 131 |
+
if (head < {n_heads + n_kv}u) {{
|
| 132 |
+
uint base = head * {hd}u;
|
| 133 |
+
const device float* g = (head < {n_heads}u) ? qn : kn;
|
| 134 |
+
float ssq = 0.0f;
|
| 135 |
+
for (uint d = lane; d < {hd}u; d += 32u) {{
|
| 136 |
+
float v = qkv[base + d];
|
| 137 |
+
ssq = metal::fma(v, v, ssq);
|
| 138 |
+
}}
|
| 139 |
+
ssq = metal::simd_sum(ssq);
|
| 140 |
+
float rms = metal::rsqrt(ssq / {float(hd)}f + {eps:.9g}f);
|
| 141 |
+
for (uint d = lane; d < {hd}u; d += 32u) {{
|
| 142 |
+
float c = ct[pos * {h2}u + (d % {h2}u)];
|
| 143 |
+
float sn = st[pos * {h2}u + (d % {h2}u)];
|
| 144 |
+
float v = qkv[base + d] * rms * g[d];
|
| 145 |
+
float vr = (d < {h2}u)
|
| 146 |
+
? -(qkv[base + d + {h2}u] * rms * g[d + {h2}u])
|
| 147 |
+
: (qkv[base + d - {h2}u] * rms * g[d - {h2}u]);
|
| 148 |
+
float r = metal::fma(v, c, vr * sn);
|
| 149 |
+
if (head < {n_heads}u) {{
|
| 150 |
+
qout[base + d] = r;
|
| 151 |
+
}} else {{
|
| 152 |
+
uint kh = head - {n_heads}u;
|
| 153 |
+
kc_w[(ulong)(kh * {maxseq}u + pos) * {hd}u + d] = r;
|
| 154 |
+
}}
|
| 155 |
+
}}
|
| 156 |
+
}} else {{
|
| 157 |
+
uint vh = head - {n_heads + n_kv}u;
|
| 158 |
+
for (uint d = lane; d < {hd}u; d += 32u) {{
|
| 159 |
+
vc_w[(ulong)(vh * {maxseq}u + pos) * {hd}u + d] =
|
| 160 |
+
qkv[{(n_heads + n_kv) * hd}u + vh * {hd}u + d];
|
| 161 |
+
}}
|
| 162 |
+
}}
|
| 163 |
+
"""
|
| 164 |
+
k = mx.fast.metal_kernel(
|
| 165 |
+
name="qknorm_rope_append_%d_%d_%d" % (n_heads, n_kv, hd),
|
| 166 |
+
input_names=["qkv", "qn", "kn", "kc", "vc", "ct", "st", "posbuf", "mask"],
|
| 167 |
+
output_names=["qout"], source=src)
|
| 168 |
+
_CACHE[key] = k
|
| 169 |
+
return k
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
def qknorm_rope_append(qkv, qn, kn, kc, vc, ct, st, posbuf, mask,
|
| 173 |
+
n_heads, n_kv, hd, maxseq, eps, stream=None):
|
| 174 |
+
"""qkv fp32 [(n_heads+2*n_kv)*hd] -> roped+normed q fp32 [n_heads*hd];
|
| 175 |
+
writes normed+roped k and raw v into kc/vc at position posbuf[0] and
|
| 176 |
+
zeroes mask[pos]."""
|
| 177 |
+
k = get_qknorm_rope_append(n_heads, n_kv, hd, maxseq, eps)
|
| 178 |
+
nheads_total = n_heads + 2 * n_kv
|
| 179 |
+
ntg = (nheads_total + SG_PER_TG - 1) // SG_PER_TG
|
| 180 |
+
return k(inputs=[qkv, qn, kn, kc, vc, ct, st, posbuf, mask],
|
| 181 |
+
grid=(ntg * SG_PER_TG * 32, 1, 1), threadgroup=(SG_PER_TG * 32, 1, 1),
|
| 182 |
+
output_shapes=[(n_heads * hd,)], output_dtypes=[mx.float32],
|
| 183 |
+
stream=stream)[0]
|
mlx/fermion_mlx/container.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""TRTC v4 arch-3 (Qwen3) container reader — numpy memmap views, zero-copy
|
| 3 |
+
until the caller materializes.
|
| 4 |
+
|
| 5 |
+
Layout (ground truth: scripts/export_trit_c_model.py; cross-checks:
|
| 6 |
+
scripts/expand_trtc_v4_to_hf.py and kernels/gpu_e2e/runtime8b.py):
|
| 7 |
+
60 B header <Iii9i2fi>: magic 'TRTC', version=4, arch=3, n_layers, hidden,
|
| 8 |
+
n_heads, n_kv_heads, head_dim, rotary_dims, intermediate, vocab, max_pos,
|
| 9 |
+
f32 rms_norm_eps, f32 rope_theta, i32 embed_out_kind (eok)
|
| 10 |
+
eok=2 (untied, 8B): int8 embed_in record right after the header
|
| 11 |
+
per layer: input_layernorm f32[H], post_attention_layernorm f32[H],
|
| 12 |
+
q_norm f32[head_dim], k_norm f32[head_dim],
|
| 13 |
+
then 7 trit records (q,k,v,o,gate,up,down) — arch-3 QKV are bias-less
|
| 14 |
+
final norm f32[H]
|
| 15 |
+
int8 embed_out (lm_head) record; eok=1 (tied, 0.6B) derives embed_in
|
| 16 |
+
from this record.
|
| 17 |
+
trit record: i32 rows, cols, cols_pad, row_stride(bytes);
|
| 18 |
+
u8 bp/bn/br [rows*row_stride] LSB-first; f32 s_lo[rows], s_hi[rows];
|
| 19 |
+
i32 rs_lo[rows], rs_hi[rows] (skipped); i32 has_bias (+ f32 bias[rows])
|
| 20 |
+
int8 record: i32 rows, cols, cols_pad, row_stride (== cols_pad);
|
| 21 |
+
s8 W[rows*row_stride]; f32 scale[rows]; i32 wsum[rows] (skipped)
|
| 22 |
+
|
| 23 |
+
Weight semantics: w[r,c] = (bp-bn) * (s_hi[r] if br else s_lo[r]);
|
| 24 |
+
int8 embeds: w[r,c] = scale[r] * Q[r,c].
|
| 25 |
+
"""
|
| 26 |
+
import struct
|
| 27 |
+
|
| 28 |
+
import numpy as np
|
| 29 |
+
|
| 30 |
+
MAGIC = 0x43545254
|
| 31 |
+
HDR_FMT = "<Iii9i2fi"
|
| 32 |
+
HDR_BYTES = struct.calcsize(HDR_FMT) # 60
|
| 33 |
+
PROJS = ("q", "k", "v", "o", "gate", "up", "down")
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class TritRecord:
|
| 37 |
+
__slots__ = ("rows", "cols", "cols_pad", "row_stride", "bp", "bn", "br",
|
| 38 |
+
"slo", "shi", "bias")
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
class Int8Record:
|
| 42 |
+
__slots__ = ("rows", "cols", "cols_pad", "row_stride", "W", "scale")
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class Container:
|
| 46 |
+
pass
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _trit_record(mm, off):
|
| 50 |
+
r = TritRecord()
|
| 51 |
+
r.rows, r.cols, r.cols_pad, r.row_stride = (
|
| 52 |
+
int(v) for v in np.frombuffer(mm, np.int32, 4, off))
|
| 53 |
+
off += 16
|
| 54 |
+
n = r.rows * r.row_stride
|
| 55 |
+
r.bp = np.frombuffer(mm, np.uint8, n, off).reshape(r.rows, r.row_stride); off += n
|
| 56 |
+
r.bn = np.frombuffer(mm, np.uint8, n, off).reshape(r.rows, r.row_stride); off += n
|
| 57 |
+
r.br = np.frombuffer(mm, np.uint8, n, off).reshape(r.rows, r.row_stride); off += n
|
| 58 |
+
r.slo = np.frombuffer(mm, np.float32, r.rows, off); off += 4 * r.rows
|
| 59 |
+
r.shi = np.frombuffer(mm, np.float32, r.rows, off); off += 4 * r.rows
|
| 60 |
+
off += 8 * r.rows # rs_lo, rs_hi trit-sum sidecars (integrity data, unused here)
|
| 61 |
+
has_bias = int(np.frombuffer(mm, np.int32, 1, off)[0]); off += 4
|
| 62 |
+
r.bias = None
|
| 63 |
+
if has_bias:
|
| 64 |
+
r.bias = np.frombuffer(mm, np.float32, r.rows, off); off += 4 * r.rows
|
| 65 |
+
return r, off
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def _int8_record(mm, off):
|
| 69 |
+
r = Int8Record()
|
| 70 |
+
r.rows, r.cols, r.cols_pad, r.row_stride = (
|
| 71 |
+
int(v) for v in np.frombuffer(mm, np.int32, 4, off))
|
| 72 |
+
assert r.row_stride == r.cols_pad, "int8 record: row_stride must equal cols_pad"
|
| 73 |
+
off += 16
|
| 74 |
+
n = r.rows * r.row_stride
|
| 75 |
+
r.W = np.frombuffer(mm, np.int8, n, off).reshape(r.rows, r.row_stride); off += n
|
| 76 |
+
r.scale = np.frombuffer(mm, np.float32, r.rows, off); off += 4 * r.rows
|
| 77 |
+
off += 4 * r.rows # wsum
|
| 78 |
+
return r, off
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def read_container(path):
|
| 82 |
+
"""Memory-map a TRTC v4 arch-3 container; all arrays are views into the map."""
|
| 83 |
+
mm = np.memmap(path, dtype=np.uint8, mode="r")
|
| 84 |
+
h = struct.unpack(HDR_FMT, bytes(mm[:HDR_BYTES]))
|
| 85 |
+
c = Container()
|
| 86 |
+
(magic, c.version, c.arch, c.n_layers, c.hidden, c.n_heads, c.n_kv,
|
| 87 |
+
c.head_dim, c.rot, c.inter, c.vocab, c.maxpos, c.eps, c.theta, c.eok) = h
|
| 88 |
+
assert magic == MAGIC, f"bad magic {magic:#x}"
|
| 89 |
+
assert c.version == 4 and c.arch == 3, \
|
| 90 |
+
f"need TRTC v4 arch-3 (got v={c.version} arch={c.arch})"
|
| 91 |
+
assert c.eok in (1, 2), f"bad embed_out_kind {c.eok}"
|
| 92 |
+
off = HDR_BYTES
|
| 93 |
+
c.embed_in = None
|
| 94 |
+
if c.eok == 2: # untied: int8 embed_in follows the header
|
| 95 |
+
c.embed_in, off = _int8_record(mm, off)
|
| 96 |
+
assert (c.embed_in.rows, c.embed_in.cols) == (c.vocab, c.hidden)
|
| 97 |
+
H, hd = c.hidden, c.head_dim
|
| 98 |
+
q_dim, kv_dim = c.n_heads * hd, c.n_kv * hd
|
| 99 |
+
shapes = {"q": (q_dim, H), "k": (kv_dim, H), "v": (kv_dim, H),
|
| 100 |
+
"o": (H, q_dim), "gate": (c.inter, H), "up": (c.inter, H),
|
| 101 |
+
"down": (H, c.inter)}
|
| 102 |
+
c.layers = []
|
| 103 |
+
for li in range(c.n_layers):
|
| 104 |
+
L = {}
|
| 105 |
+
L["norm1"] = np.frombuffer(mm, np.float32, H, off); off += 4 * H
|
| 106 |
+
L["norm2"] = np.frombuffer(mm, np.float32, H, off); off += 4 * H
|
| 107 |
+
L["qnorm"] = np.frombuffer(mm, np.float32, hd, off); off += 4 * hd
|
| 108 |
+
L["knorm"] = np.frombuffer(mm, np.float32, hd, off); off += 4 * hd
|
| 109 |
+
for nm in PROJS:
|
| 110 |
+
L[nm], off = _trit_record(mm, off)
|
| 111 |
+
assert (L[nm].rows, L[nm].cols) == shapes[nm], \
|
| 112 |
+
f"L{li}.{nm}: {(L[nm].rows, L[nm].cols)} != {shapes[nm]}"
|
| 113 |
+
assert L[nm].bias is None, f"L{li}.{nm}: arch-3 records must be bias-less"
|
| 114 |
+
c.layers.append(L)
|
| 115 |
+
c.final_norm = np.frombuffer(mm, np.float32, H, off); off += 4 * H
|
| 116 |
+
c.embed_out, off = _int8_record(mm, off)
|
| 117 |
+
assert (c.embed_out.rows, c.embed_out.cols) == (c.vocab, c.hidden)
|
| 118 |
+
assert off == mm.size, f"reader ended at {off}, file is {mm.size}"
|
| 119 |
+
if c.embed_in is None: # tied: same int8 record serves both
|
| 120 |
+
c.embed_in = c.embed_out
|
| 121 |
+
c.mm = mm # keep the map alive
|
| 122 |
+
return c
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
# ---------------- fp32 reference math (gate-side, numpy) ----------------
|
| 126 |
+
|
| 127 |
+
def unpack_plane(plane, cols):
|
| 128 |
+
return np.unpackbits(plane, axis=1, bitorder="little")[:, :cols]
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def dense_weights(rec, dtype=np.float32):
|
| 132 |
+
p = unpack_plane(rec.bp, rec.cols).astype(np.int8)
|
| 133 |
+
n = unpack_plane(rec.bn, rec.cols).astype(np.int8)
|
| 134 |
+
h = unpack_plane(rec.br, rec.cols)
|
| 135 |
+
s = (p - n).astype(dtype)
|
| 136 |
+
mag = np.where(h.astype(bool), rec.shi[:, None].astype(dtype),
|
| 137 |
+
rec.slo[:, None].astype(dtype))
|
| 138 |
+
return s * mag
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def ref_rmsnorm(x, g, eps, dtype=np.float32):
|
| 142 |
+
x = x.astype(dtype)
|
| 143 |
+
return (x * (1.0 / np.sqrt(np.mean(x * x) + eps))) * g.astype(dtype)
|
mlx/fermion_mlx/gates.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""House correctness gates for the MLX arch-3 path.
|
| 3 |
+
|
| 4 |
+
refgate : V1 gate (dspec runtime8b.py protocol) — teacher-forced logits +
|
| 5 |
+
greedy-with-resync vs the container's own fp expansion reference
|
| 6 |
+
npz {prompt_ids, tf_logits, greedy, meta}. Every argmax
|
| 7 |
+
divergence must be a near-tie <= tie_tol of the logit scale;
|
| 8 |
+
greedy flips resynced and bounded.
|
| 9 |
+
cachegate : greedy-with-resync vs a banked hf_greedy_cache.json
|
| 10 |
+
(results_arch3_runtime format, keyed to container md5).
|
| 11 |
+
pathgate : path-consistency — the fused-kernel primary path and the pure
|
| 12 |
+
mx.fast-ops reference twin must produce the same greedy stream.
|
| 13 |
+
|
| 14 |
+
All gates return (ok, receipt_dict); receipts carry per-flip absolute and
|
| 15 |
+
relative margins so near-tie flips are documented, never waved through.
|
| 16 |
+
"""
|
| 17 |
+
import hashlib
|
| 18 |
+
import json
|
| 19 |
+
|
| 20 |
+
import mlx.core as mx
|
| 21 |
+
import numpy as np
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _md5(path):
|
| 25 |
+
h = hashlib.md5()
|
| 26 |
+
with open(path, "rb") as f:
|
| 27 |
+
while chunk := f.read(1 << 24):
|
| 28 |
+
h.update(chunk)
|
| 29 |
+
return h.hexdigest()
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def refgate(model, ref_path, tie_tol=0.04, max_flips=8):
|
| 33 |
+
ref = np.load(ref_path, allow_pickle=True)
|
| 34 |
+
ids = [int(t) for t in ref["prompt_ids"].tolist()]
|
| 35 |
+
tf = ref["tf_logits"]
|
| 36 |
+
greedy_ref = [int(t) for t in ref["greedy"].tolist()]
|
| 37 |
+
meta = json.loads(str(ref["meta"])) if "meta" in ref else {}
|
| 38 |
+
print(f"[refgate] prompt ids ({len(ids)}): {ids}")
|
| 39 |
+
print(f"[refgate] ref meta: {meta}")
|
| 40 |
+
rec = {"ref": str(ref_path), "meta": meta, "tf": [], "greedy_flips": [],
|
| 41 |
+
"tie_tol": tie_tol, "max_flips": max_flips}
|
| 42 |
+
# stage A: teacher-forced logits
|
| 43 |
+
mask = model.new_mask()
|
| 44 |
+
rels, ams, near, worst_gap = [], 0, 0, 0.0
|
| 45 |
+
for i, t in enumerate(ids):
|
| 46 |
+
_, _, lg = model.step(model.embed(t), i, mask)
|
| 47 |
+
g = np.array(lg, np.float64)
|
| 48 |
+
r = tf[i].astype(np.float64)
|
| 49 |
+
scale = float(np.abs(r).max())
|
| 50 |
+
rel = float(np.abs(g - r).max() / scale)
|
| 51 |
+
cos = float((g * r).sum() / (np.linalg.norm(g) * np.linalg.norm(r)))
|
| 52 |
+
gam, ram = int(g.argmax()), int(r.argmax())
|
| 53 |
+
am = gam == ram
|
| 54 |
+
ams += am
|
| 55 |
+
rels.append(rel)
|
| 56 |
+
extra = ""
|
| 57 |
+
if not am:
|
| 58 |
+
gap = float(g[gam] - g[ram]) / scale
|
| 59 |
+
worst_gap = max(worst_gap, gap)
|
| 60 |
+
near += gap <= tie_tol
|
| 61 |
+
extra = f" gap {100*gap:.3f}% ({'near-tie' if gap <= tie_tol else 'REAL'})"
|
| 62 |
+
rec["tf"].append({"pos": i, "rel": rel, "cos": cos, "argmax_match": am})
|
| 63 |
+
print(f" pos {i:2d}: rel {100*rel:6.3f}% | cos {cos:.6f} | argmax "
|
| 64 |
+
f"{'match' if am else 'MISMATCH'} (mlx {gam} ref {ram}){extra}")
|
| 65 |
+
print(f"[refgate] stage A: TF rel max {100*max(rels):.3f}% / mean "
|
| 66 |
+
f"{100*float(np.mean(rels)):.3f}%, argmax {ams}/{len(ids)} "
|
| 67 |
+
f"(+{near} near-tie), worst gap {100*worst_gap:.3f}%")
|
| 68 |
+
rec["tf_summary"] = {"rel_max": max(rels), "rel_mean": float(np.mean(rels)),
|
| 69 |
+
"argmax": f"{ams}/{len(ids)}", "near_tie": near}
|
| 70 |
+
# stage B: greedy with resync
|
| 71 |
+
mask = model.new_mask()
|
| 72 |
+
x, pos0 = model.prefill(ids, mask, compiled=False)
|
| 73 |
+
flips, g_toks, ok = 0, [], True
|
| 74 |
+
for s in range(len(greedy_ref)):
|
| 75 |
+
_, _, lg = model.step(x, pos0 + s, mask)
|
| 76 |
+
l = np.array(lg, np.float64)
|
| 77 |
+
g_tok = int(l.argmax())
|
| 78 |
+
g_toks.append(g_tok)
|
| 79 |
+
tok = g_tok
|
| 80 |
+
if g_tok != greedy_ref[s]:
|
| 81 |
+
gap_abs = float(l[g_tok] - l[greedy_ref[s]])
|
| 82 |
+
scale = float(np.abs(l).max())
|
| 83 |
+
gap = gap_abs / scale
|
| 84 |
+
flips += 1
|
| 85 |
+
worst_gap = max(worst_gap, gap)
|
| 86 |
+
rec["greedy_flips"].append({"step": s, "mlx": g_tok,
|
| 87 |
+
"ref": greedy_ref[s],
|
| 88 |
+
"margin_abs_logits": gap_abs,
|
| 89 |
+
"margin_rel": gap})
|
| 90 |
+
print(f" step {s}: mlx {g_tok} vs ref {greedy_ref[s]}, gap "
|
| 91 |
+
f"{gap_abs:.4f} logits = {100*gap:.3f}% of scale -> resync")
|
| 92 |
+
if flips > max_flips:
|
| 93 |
+
ok = False
|
| 94 |
+
break
|
| 95 |
+
tok = greedy_ref[s]
|
| 96 |
+
x = model.embed(tok)
|
| 97 |
+
mx.eval(x)
|
| 98 |
+
print(f"[refgate] ref greedy: {greedy_ref}")
|
| 99 |
+
print(f"[refgate] mlx greedy: {g_toks}")
|
| 100 |
+
ok = ok and worst_gap <= tie_tol and flips <= max_flips
|
| 101 |
+
rec.update(flips=flips, worst_gap=worst_gap, ok=bool(ok),
|
| 102 |
+
mlx_greedy=g_toks, ref_greedy=greedy_ref)
|
| 103 |
+
print(f"[refgate] {'PASS' if ok else 'FAIL'} (worst gap {100*worst_gap:.3f}% "
|
| 104 |
+
f"tol {100*tie_tol:.1f}%; {flips} flips, tol {max_flips})")
|
| 105 |
+
return ok, rec
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def cachegate(model, container_path, cache_path, tie_tol=0.04, max_flips=8):
|
| 109 |
+
cache = json.load(open(cache_path))
|
| 110 |
+
md5 = _md5(container_path)
|
| 111 |
+
print(f"[cachegate] container md5 {md5} vs cache key {cache['container_md5']}")
|
| 112 |
+
assert md5 == cache["container_md5"], "cache is for a different container"
|
| 113 |
+
rec = {"cache": str(cache_path), "container_md5": md5, "prompts": [],
|
| 114 |
+
"tie_tol": tie_tol, "max_flips_per_prompt": max_flips}
|
| 115 |
+
ok = True
|
| 116 |
+
tot_flips, worst = 0, 0.0
|
| 117 |
+
for pi, (prompt, ref) in enumerate(zip(cache["prompts"], cache["tokens"])):
|
| 118 |
+
mask = model.new_mask()
|
| 119 |
+
x, pos0 = model.prefill([int(t) for t in prompt], mask)
|
| 120 |
+
flips, fliprec = 0, []
|
| 121 |
+
for s, rt in enumerate(ref):
|
| 122 |
+
_, _, lg = model.step(x, pos0 + s, mask)
|
| 123 |
+
l = np.array(lg, np.float64)
|
| 124 |
+
g_tok = int(l.argmax())
|
| 125 |
+
tok = g_tok
|
| 126 |
+
if g_tok != int(rt):
|
| 127 |
+
gap_abs = float(l[g_tok] - l[int(rt)])
|
| 128 |
+
gap = gap_abs / float(np.abs(l).max())
|
| 129 |
+
worst = max(worst, gap)
|
| 130 |
+
flips += 1
|
| 131 |
+
fliprec.append({"step": s, "mlx": g_tok, "ref": int(rt),
|
| 132 |
+
"margin_abs_logits": gap_abs, "margin_rel": gap})
|
| 133 |
+
print(f" p{pi} step {s}: mlx {g_tok} vs hf {rt}, gap "
|
| 134 |
+
f"{gap_abs:.4f} logits = {100*gap:.3f}% -> resync")
|
| 135 |
+
tok = int(rt)
|
| 136 |
+
x = model.embed(tok)
|
| 137 |
+
mx.eval(x)
|
| 138 |
+
tot_flips += flips
|
| 139 |
+
good = flips <= max_flips
|
| 140 |
+
ok = ok and good
|
| 141 |
+
rec["prompts"].append({"prompt": prompt, "steps": len(ref),
|
| 142 |
+
"flips": flips, "flip_detail": fliprec,
|
| 143 |
+
"pass": bool(good)})
|
| 144 |
+
print(f" prompt {pi}: {len(ref)} steps, {flips} resynced flips -> "
|
| 145 |
+
f"{'PASS' if good else 'FAIL'}")
|
| 146 |
+
ok = ok and worst <= tie_tol
|
| 147 |
+
rec.update(total_flips=tot_flips, worst_gap=worst, ok=bool(ok))
|
| 148 |
+
print(f"[cachegate] {'PASS' if ok else 'FAIL'} ({tot_flips} flips total, "
|
| 149 |
+
f"worst gap {100*worst:.3f}% tol {100*tie_tol:.1f}%)")
|
| 150 |
+
return ok, rec
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def pathgate(model, ids, n=32):
|
| 154 |
+
"""fused-kernel path vs pure-mx.fast-ops twin: greedy streams must be
|
| 155 |
+
IDENTICAL (same weights, same GEMV kernels; isolates the new kernel)."""
|
| 156 |
+
fused, _ = model.generate(ids, n, compiled=True)
|
| 157 |
+
ref = model.generate_ref(ids, n)
|
| 158 |
+
same = fused == ref
|
| 159 |
+
d = next((i for i in range(min(len(fused), len(ref)))
|
| 160 |
+
if fused[i] != ref[i]), None)
|
| 161 |
+
print(f"[pathgate] fused-vs-mxops {n}-token greedy: "
|
| 162 |
+
f"{'IDENTICAL' if same else f'FIRST DIVERGENCE at {d}'}")
|
| 163 |
+
return same, {"n": n, "identical": bool(same), "first_divergence": d,
|
| 164 |
+
"fused": fused, "mxops": ref}
|
mlx/fermion_mlx/mbatch.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Small-batch (M-token) GEMM kernels for speculative verify — EXPERIMENTAL.
|
| 3 |
+
|
| 4 |
+
Why these exist: the shipped decode path is M=1 GEMV. Speculative decoding
|
| 5 |
+
only wins if the verifier scores k+1 draft positions while streaming the
|
| 6 |
+
weight planes ONCE; re-running the GEMV k+1 times re-reads the weights k+1
|
| 7 |
+
times and cannot beat plain decode. These kernels keep the v5/v7 interleaved
|
| 8 |
+
plane layout + one-weight-read structure and add an M-wide accumulator:
|
| 9 |
+
|
| 10 |
+
trit_gemm_m : five-value planes X[M, cols] -> out[M, rows]
|
| 11 |
+
v7 lane mapping (lane = row(8) x word-slot(4), planes read
|
| 12 |
+
once, fully coalesced) + v6 mask-convert-FMA decode (float4
|
| 13 |
+
(uint4(a) & m4) == 2^bit when set; x pre-scaled by 2^-bit,
|
| 14 |
+
exact in fp32). No LUT: a per-token LUT tile for M=5 exceeds
|
| 15 |
+
the 32KB threadgroup budget at inter=12288.
|
| 16 |
+
s8_gemm_m : int8 rows (lm_head / untied embeds), one simdgroup per row,
|
| 17 |
+
weight char4 loaded once and dotted against all M x-vectors.
|
| 18 |
+
|
| 19 |
+
No norm/GLU/residual template flags here: the spec verify path applies
|
| 20 |
+
RMSNorm / SwiGLU / residual adds as plain MLX elementwise ops on [M, ...]
|
| 21 |
+
tensors (cheap at M<=8), keeping these kernels pure GEMMs.
|
| 22 |
+
|
| 23 |
+
Additive module — nothing in trit_kernels.py / arch3.py / model.py changes.
|
| 24 |
+
"""
|
| 25 |
+
import mlx.core as mx
|
| 26 |
+
|
| 27 |
+
SG_PER_TG = 8
|
| 28 |
+
_CACHE = {}
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def _trit_gemm_m_source(M, rows, cols, rs):
|
| 32 |
+
assert rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0 and rows % 8 == 0
|
| 33 |
+
W = rs // 4
|
| 34 |
+
kbody = []
|
| 35 |
+
for k in range(8):
|
| 36 |
+
m4 = [1 << (4 * k + i) for i in range(4)]
|
| 37 |
+
ps = ", ".join("%.9ef" % (2.0 ** -(4 * k + i)) for i in range(4))
|
| 38 |
+
loads = "\n".join(
|
| 39 |
+
f""" {{
|
| 40 |
+
float4 xs = x4[{m}u * {cols // 4}u + wl * 8u + {k}u] * ps{k};
|
| 41 |
+
accLo[{m}] += metal::dot(sa, xs);
|
| 42 |
+
accHi[{m}] += metal::dot(sb, xs);
|
| 43 |
+
}}""" for m in range(M))
|
| 44 |
+
kbody.append(f""" {{
|
| 45 |
+
const uint4 m4 = uint4({m4[0]}u, {m4[1]}u, {m4[2]}u, {m4[3]}u);
|
| 46 |
+
const float4 ps{k} = float4({ps});
|
| 47 |
+
float4 sa = float4(uint4(a) & m4) - float4(uint4(b) & m4);
|
| 48 |
+
float4 sb = float4(uint4(cc) & m4) - float4(uint4(d) & m4);
|
| 49 |
+
{loads}
|
| 50 |
+
}}""")
|
| 51 |
+
kbody = "\n".join(kbody)
|
| 52 |
+
outs = "\n".join(
|
| 53 |
+
f""" accLo[{m}] += metal::simd_shuffle_down(accLo[{m}], 16u);
|
| 54 |
+
accLo[{m}] += metal::simd_shuffle_down(accLo[{m}], 8u);
|
| 55 |
+
accHi[{m}] += metal::simd_shuffle_down(accHi[{m}], 16u);
|
| 56 |
+
accHi[{m}] += metal::simd_shuffle_down(accHi[{m}], 8u);"""
|
| 57 |
+
for m in range(M))
|
| 58 |
+
writes = "\n".join(
|
| 59 |
+
f" out[{m}u * {rows}u + row] = "
|
| 60 |
+
f"metal::fma(slo[row], accLo[{m}], shi[row] * accHi[{m}]);"
|
| 61 |
+
for m in range(M))
|
| 62 |
+
return f"""
|
| 63 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 64 |
+
uint lane = tid & 31u;
|
| 65 |
+
uint sg = tid >> 5u;
|
| 66 |
+
uint blk = threadgroup_position_in_grid.x * {SG_PER_TG}u + sg;
|
| 67 |
+
uint row0 = blk * 8u;
|
| 68 |
+
if (row0 >= {rows}u) return;
|
| 69 |
+
uint r = lane & 7u;
|
| 70 |
+
uint wsl = lane >> 3u;
|
| 71 |
+
const device uint* p32 = (const device uint*)bp;
|
| 72 |
+
const device uint* n32 = (const device uint*)bn;
|
| 73 |
+
const device uint* h32 = (const device uint*)br;
|
| 74 |
+
const device float4* x4 = (const device float4*)x;
|
| 75 |
+
float accLo[{M}], accHi[{M}];
|
| 76 |
+
for (uint m = 0u; m < {M}u; ++m) {{ accLo[m] = 0.0f; accHi[m] = 0.0f; }}
|
| 77 |
+
for (uint wl = wsl; wl < {W}u; wl += 4u) {{
|
| 78 |
+
ulong base = ((ulong)blk * {W}u + wl) * 8u + r;
|
| 79 |
+
uint pw = p32[base], nw2 = n32[base], hw = h32[base];
|
| 80 |
+
uint a = pw & ~hw, b = nw2 & ~hw, cc = pw & hw, d = nw2 & hw;
|
| 81 |
+
{kbody}
|
| 82 |
+
}}
|
| 83 |
+
{outs}
|
| 84 |
+
if (lane < 8u) {{
|
| 85 |
+
uint row = row0 + lane;
|
| 86 |
+
{writes}
|
| 87 |
+
}}
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _s8_gemm_m_source(M, rows, cols, rs):
|
| 92 |
+
assert rs % 4 == 0 and cols % 4 == 0
|
| 93 |
+
accs = "\n".join(
|
| 94 |
+
f" acc[{m}] += metal::dot(wf, x4[{m}u * {cols // 4}u + i]);"
|
| 95 |
+
for m in range(M))
|
| 96 |
+
reduces = "\n".join(
|
| 97 |
+
f""" {{
|
| 98 |
+
float a = metal::simd_sum(acc[{m}]);
|
| 99 |
+
if (lane == 0u) out[{m}u * {rows}u + row] = scale[row] * a;
|
| 100 |
+
}}""" for m in range(M))
|
| 101 |
+
return f"""
|
| 102 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 103 |
+
uint lane = tid & 31u;
|
| 104 |
+
uint sg = tid >> 5u;
|
| 105 |
+
uint row = threadgroup_position_in_grid.x * {SG_PER_TG}u + sg;
|
| 106 |
+
if (row >= {rows}u) return;
|
| 107 |
+
const device char* w = (const device char*)W + (ulong)row * {rs}u;
|
| 108 |
+
const device float4* x4 = (const device float4*)x;
|
| 109 |
+
float acc[{M}];
|
| 110 |
+
for (uint m = 0u; m < {M}u; ++m) acc[m] = 0.0f;
|
| 111 |
+
for (uint i = lane; i < {cols // 4}u; i += 32u) {{
|
| 112 |
+
char4 wv = *(const device char4*)(w + i * 4u);
|
| 113 |
+
float4 wf = float4(wv.x, wv.y, wv.z, wv.w);
|
| 114 |
+
{accs}
|
| 115 |
+
}}
|
| 116 |
+
{reduces}
|
| 117 |
+
"""
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def _get(kind, M, rows, cols, rs):
|
| 121 |
+
key = (kind, M, rows, cols, rs)
|
| 122 |
+
if key in _CACHE:
|
| 123 |
+
return _CACHE[key]
|
| 124 |
+
if kind == "trit":
|
| 125 |
+
src = _trit_gemm_m_source(M, rows, cols, rs)
|
| 126 |
+
k = mx.fast.metal_kernel(
|
| 127 |
+
name=f"tritgemm_m{M}_r{rows}_c{cols}",
|
| 128 |
+
input_names=["bp", "bn", "br", "slo", "shi", "x"],
|
| 129 |
+
output_names=["out"], source=src)
|
| 130 |
+
else:
|
| 131 |
+
src = _s8_gemm_m_source(M, rows, cols, rs)
|
| 132 |
+
k = mx.fast.metal_kernel(
|
| 133 |
+
name=f"s8gemm_m{M}_r{rows}_c{cols}",
|
| 134 |
+
input_names=["W", "scale", "x"],
|
| 135 |
+
output_names=["out"], source=src)
|
| 136 |
+
_CACHE[key] = k
|
| 137 |
+
return k
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
def trit_gemm_m(planes, slo, shi, X, stream=None):
|
| 141 |
+
"""planes v5-interleaved (KERNEL_VERSION>=5 prep). X fp32 [M, cols]
|
| 142 |
+
(already normed / activated). Returns fp32 [M, rows]."""
|
| 143 |
+
bp, bn, br = planes
|
| 144 |
+
rows, rs = bp.shape
|
| 145 |
+
M, cols = X.shape
|
| 146 |
+
k = _get("trit", M, rows, cols, rs)
|
| 147 |
+
nsg = rows // 8
|
| 148 |
+
ntg = (nsg + SG_PER_TG - 1) // SG_PER_TG
|
| 149 |
+
return k(inputs=[bp, bn, br, slo, shi, X],
|
| 150 |
+
grid=(ntg * SG_PER_TG * 32, 1, 1), threadgroup=(SG_PER_TG * 32, 1, 1),
|
| 151 |
+
output_shapes=[(M, rows)], output_dtypes=[mx.float32],
|
| 152 |
+
stream=stream)[0]
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def s8_gemm_m(W, scale, X, stream=None):
|
| 156 |
+
"""int8 W [rows, rs], X fp32 [M, cols] (already normed). -> [M, rows]."""
|
| 157 |
+
rows, rs = W.shape
|
| 158 |
+
M, cols = X.shape
|
| 159 |
+
k = _get("s8", M, rows, cols, rs)
|
| 160 |
+
ntg = (rows + SG_PER_TG - 1) // SG_PER_TG
|
| 161 |
+
return k(inputs=[W, scale, X],
|
| 162 |
+
grid=(ntg * SG_PER_TG * 32, 1, 1), threadgroup=(SG_PER_TG * 32, 1, 1),
|
| 163 |
+
output_shapes=[(M, rows)], output_dtypes=[mx.float32],
|
| 164 |
+
stream=stream)[0]
|
mlx/fermion_mlx/model.py
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""End-to-end MLX/Metal decode for TRTC v4 **arch-3 (Qwen3)** five-value
|
| 3 |
+
containers — Neutrino-0.6B (tied int8 embeds) and Neutrino-8B (untied).
|
| 4 |
+
|
| 5 |
+
Every transformer linear runs on the banked five-value {0,±s_lo,±s_hi}
|
| 6 |
+
3-bit-planar Metal GEMV kernels (vendored trit_kernels.py, byte-identical
|
| 7 |
+
to kernels/metal/trit_kernels.py): qkv / o / gate+up / down fused per layer
|
| 8 |
+
+ the int8 lm_head GEMV. The arch-3 delta — per-head Q/K-RMSNorm before
|
| 9 |
+
rope, biasless qkv, int8 embed_in — is handled by the new fused
|
| 10 |
+
qknorm_rope_append kernel (arch3.py) and an int8 embedding dequant.
|
| 11 |
+
|
| 12 |
+
Fusions per decode token: rmsnorm folded into qkv/gu/lm_head GEMVs, SwiGLU
|
| 13 |
+
folded into the down GEMV, residual adds folded into o/down epilogues,
|
| 14 |
+
QK-norm+rope+KV-append in one dispatch. MLX supplies SDPA, argmax and the
|
| 15 |
+
lazy-graph dispatch batching.
|
| 16 |
+
|
| 17 |
+
Two forward implementations:
|
| 18 |
+
_step_fn : primary path (fused kernel, preallocated KV + additive mask,
|
| 19 |
+
mx.compile-able) — the shipped/gated/benched path.
|
| 20 |
+
step_ref : pure-MLX-ops reference twin for the NEW kernel (mx.fast
|
| 21 |
+
rms_norm/rope, concat KV) — used by the path-consistency gate.
|
| 22 |
+
"""
|
| 23 |
+
import time
|
| 24 |
+
|
| 25 |
+
import mlx.core as mx
|
| 26 |
+
import numpy as np
|
| 27 |
+
|
| 28 |
+
from .container import read_container
|
| 29 |
+
from .trit_kernels import trit_gemv, s8_gemv, prep_planes
|
| 30 |
+
from . import trit_kernels
|
| 31 |
+
from .arch3 import qknorm_rope_append, s8_gemv_v3
|
| 32 |
+
|
| 33 |
+
NEG_INF = -1e9
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class _Layer:
|
| 37 |
+
pass
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _cat(arrs):
|
| 41 |
+
return np.ascontiguousarray(np.concatenate(arrs, axis=0))
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class MetalTritQwen3:
|
| 45 |
+
"""TRTC v4 arch-3 walker (0.6B or 8B, dims from the container header)."""
|
| 46 |
+
|
| 47 |
+
def __init__(self, path, maxseq=1024, verbose=True):
|
| 48 |
+
t0 = time.time()
|
| 49 |
+
c = read_container(path)
|
| 50 |
+
self.meta = c
|
| 51 |
+
self.eps, self.theta = float(c.eps), float(c.theta)
|
| 52 |
+
self.n_layers, self.hidden = c.n_layers, c.hidden
|
| 53 |
+
self.n_heads, self.n_kv, self.hd = c.n_heads, c.n_kv, c.head_dim
|
| 54 |
+
self.inter, self.vocab, self.eok = c.inter, c.vocab, c.eok
|
| 55 |
+
assert c.rot == self.hd, "kernels assume full-head_dim rope"
|
| 56 |
+
self.scale = self.hd ** -0.5
|
| 57 |
+
self.qd = self.n_heads * self.hd
|
| 58 |
+
self.kd = self.n_kv * self.hd
|
| 59 |
+
self.maxseq = maxseq
|
| 60 |
+
|
| 61 |
+
# int8 lm_head (+ tied/untied int8 embed_in)
|
| 62 |
+
eo = c.embed_out
|
| 63 |
+
self.eoW = mx.array(np.ascontiguousarray(eo.W)) # int8 [V, H]
|
| 64 |
+
self.eo_scale = mx.array(np.ascontiguousarray(eo.scale))
|
| 65 |
+
mx.eval(self.eoW, self.eo_scale)
|
| 66 |
+
if c.eok == 2:
|
| 67 |
+
ei = c.embed_in
|
| 68 |
+
self.embW = mx.array(np.ascontiguousarray(ei.W)) # int8 [V, H]
|
| 69 |
+
self.embS = mx.array(np.ascontiguousarray(ei.scale))
|
| 70 |
+
mx.eval(self.embW, self.embS)
|
| 71 |
+
else:
|
| 72 |
+
self.embW, self.embS = self.eoW, self.eo_scale
|
| 73 |
+
|
| 74 |
+
self.layers = []
|
| 75 |
+
for L in c.layers:
|
| 76 |
+
q, k, v, o, gate, up, down = (L[n] for n in
|
| 77 |
+
("q", "k", "v", "o", "gate", "up", "down"))
|
| 78 |
+
y = _Layer()
|
| 79 |
+
y.norm1 = mx.array(np.ascontiguousarray(L["norm1"]))
|
| 80 |
+
y.norm2 = mx.array(np.ascontiguousarray(L["norm2"]))
|
| 81 |
+
y.qnorm = mx.array(np.ascontiguousarray(L["qnorm"]))
|
| 82 |
+
y.knorm = mx.array(np.ascontiguousarray(L["knorm"]))
|
| 83 |
+
y.qkv_planes = prep_planes(tuple(
|
| 84 |
+
mx.array(_cat([getattr(r, pl) for r in (q, k, v)]))
|
| 85 |
+
for pl in ("bp", "bn", "br")), q.cols)
|
| 86 |
+
y.qkv_slo = mx.array(_cat([q.slo, k.slo, v.slo]))
|
| 87 |
+
y.qkv_shi = mx.array(_cat([q.shi, k.shi, v.shi]))
|
| 88 |
+
y.o_planes = prep_planes(tuple(
|
| 89 |
+
mx.array(np.ascontiguousarray(getattr(o, pl)))
|
| 90 |
+
for pl in ("bp", "bn", "br")), o.cols)
|
| 91 |
+
y.o_slo = mx.array(np.ascontiguousarray(o.slo))
|
| 92 |
+
y.o_shi = mx.array(np.ascontiguousarray(o.shi))
|
| 93 |
+
y.gu_planes = prep_planes(tuple(
|
| 94 |
+
mx.array(_cat([getattr(gate, pl), getattr(up, pl)]))
|
| 95 |
+
for pl in ("bp", "bn", "br")), gate.cols)
|
| 96 |
+
y.gu_slo = mx.array(_cat([gate.slo, up.slo]))
|
| 97 |
+
y.gu_shi = mx.array(_cat([gate.shi, up.shi]))
|
| 98 |
+
y.dn_planes = prep_planes(tuple(
|
| 99 |
+
mx.array(np.ascontiguousarray(getattr(down, pl)))
|
| 100 |
+
for pl in ("bp", "bn", "br")), down.cols)
|
| 101 |
+
y.dn_slo = mx.array(np.ascontiguousarray(down.slo))
|
| 102 |
+
y.dn_shi = mx.array(np.ascontiguousarray(down.shi))
|
| 103 |
+
mx.eval(*(y.qkv_planes + y.o_planes + y.gu_planes + y.dn_planes),
|
| 104 |
+
y.qkv_slo, y.qkv_shi, y.o_slo, y.o_shi,
|
| 105 |
+
y.gu_slo, y.gu_shi, y.dn_slo, y.dn_shi,
|
| 106 |
+
y.norm1, y.norm2, y.qnorm, y.knorm)
|
| 107 |
+
self.layers.append(y)
|
| 108 |
+
self.final_norm = mx.array(np.ascontiguousarray(c.final_norm))
|
| 109 |
+
|
| 110 |
+
# rope tables + preallocated KV + mask
|
| 111 |
+
inv = self.theta ** -(np.arange(0, self.hd, 2, dtype=np.float64) / self.hd)
|
| 112 |
+
ang = np.outer(np.arange(maxseq, dtype=np.float64), inv)
|
| 113 |
+
self.ct = mx.array(np.cos(ang).astype(np.float32).ravel())
|
| 114 |
+
self.st = mx.array(np.sin(ang).astype(np.float32).ravel())
|
| 115 |
+
self.kc = [mx.zeros((self.n_kv, maxseq, self.hd), dtype=mx.float32)
|
| 116 |
+
for _ in self.layers]
|
| 117 |
+
self.vc = [mx.zeros((self.n_kv, maxseq, self.hd), dtype=mx.float32)
|
| 118 |
+
for _ in self.layers]
|
| 119 |
+
mx.eval(self.ct, self.st, self.final_norm, *self.kc, *self.vc)
|
| 120 |
+
self._stepc = None
|
| 121 |
+
# x-restaged int8 unembed when the row count allows it
|
| 122 |
+
self._s8 = s8_gemv_v3 if self.vocab % 64 == 0 else s8_gemv
|
| 123 |
+
if verbose:
|
| 124 |
+
gb = mx.get_active_memory() / 2**30
|
| 125 |
+
print(f"loaded {path} in {time.time()-t0:.1f}s (arch-3, "
|
| 126 |
+
f"{self.n_layers}L/{self.hidden}H/{self.n_heads}q/{self.n_kv}kv/"
|
| 127 |
+
f"{self.inter}i/{self.vocab}V eok={self.eok}, kernel v"
|
| 128 |
+
f"{trit_kernels.KERNEL_VERSION}, maxseq {maxseq}, "
|
| 129 |
+
f"active mem {gb:.2f} GiB)")
|
| 130 |
+
|
| 131 |
+
# ---------------- primary path ----------------
|
| 132 |
+
|
| 133 |
+
def new_mask(self):
|
| 134 |
+
m = mx.full((self.maxseq,), NEG_INF, dtype=mx.float32)
|
| 135 |
+
mx.eval(m)
|
| 136 |
+
return m
|
| 137 |
+
|
| 138 |
+
def embed(self, tok):
|
| 139 |
+
"""int8 embedding dequant: fp32 row = scale[tok] * W8[tok]."""
|
| 140 |
+
return self.embW[tok].astype(mx.float32) * self.embS[tok]
|
| 141 |
+
|
| 142 |
+
def _window(self, pos):
|
| 143 |
+
"""KV window for position pos: grown in 256-slot blocks. Slicing off
|
| 144 |
+
the -1e9-masked tail is numerically exact (exp underflows to 0.0 in
|
| 145 |
+
fp32), so shrinking the SDPA read never changes the math — it only
|
| 146 |
+
stops streaming megabytes of masked cache per step."""
|
| 147 |
+
return min(self.maxseq, ((pos + 1 + 255) // 256) * 256)
|
| 148 |
+
|
| 149 |
+
def _step_fn(self, x, posbuf, mask, W):
|
| 150 |
+
"""x fp32 [H] at position posbuf[0] -> (next_tok, x_next, logits).
|
| 151 |
+
Fixed shapes per W: SDPA over the first W cache slots with additive
|
| 152 |
+
mask (stale slots -1e9; qknorm_rope_append maintains mask[pos]=0)."""
|
| 153 |
+
eps = self.eps
|
| 154 |
+
m4 = mask[:W].reshape(1, 1, 1, W)
|
| 155 |
+
for li, L in enumerate(self.layers):
|
| 156 |
+
qkv = trit_gemv(L.qkv_planes, L.qkv_slo, L.qkv_shi, x,
|
| 157 |
+
norm_w=L.norm1, eps=eps)
|
| 158 |
+
q = qknorm_rope_append(qkv, L.qnorm, L.knorm, self.kc[li], self.vc[li],
|
| 159 |
+
self.ct, self.st, posbuf, mask,
|
| 160 |
+
self.n_heads, self.n_kv, self.hd,
|
| 161 |
+
self.maxseq, eps)
|
| 162 |
+
q = q.reshape(1, self.n_heads, 1, self.hd)
|
| 163 |
+
a = mx.fast.scaled_dot_product_attention(
|
| 164 |
+
q, self.kc[li][:, :W][None], self.vc[li][:, :W][None],
|
| 165 |
+
scale=self.scale, mask=m4)
|
| 166 |
+
a = a.reshape(self.qd)
|
| 167 |
+
x = trit_gemv(L.o_planes, L.o_slo, L.o_shi, a, resid=x, eps=eps)
|
| 168 |
+
gu = trit_gemv(L.gu_planes, L.gu_slo, L.gu_shi, x,
|
| 169 |
+
norm_w=L.norm2, eps=eps)
|
| 170 |
+
x = trit_gemv(L.dn_planes, L.dn_slo, L.dn_shi, gu, glu=True,
|
| 171 |
+
resid=x, eps=eps)
|
| 172 |
+
logits = self._s8(self.eoW, self.eo_scale, x, norm_w=self.final_norm, eps=eps)
|
| 173 |
+
nt = mx.argmax(logits)
|
| 174 |
+
return nt, self.embed(nt), logits
|
| 175 |
+
|
| 176 |
+
def step(self, x, pos, mask):
|
| 177 |
+
"""uncompiled single step (gates)."""
|
| 178 |
+
return self._step_fn(x, mx.array([pos], dtype=mx.int32), mask,
|
| 179 |
+
self._window(pos))
|
| 180 |
+
|
| 181 |
+
def step_compiled(self, x, posbuf, mask, W):
|
| 182 |
+
if self._stepc is None:
|
| 183 |
+
self._stepc = {}
|
| 184 |
+
if W not in self._stepc:
|
| 185 |
+
self._stepc[W] = mx.compile(
|
| 186 |
+
lambda xx, pb, mk, _W=W: self._step_fn(xx, pb, mk, _W))
|
| 187 |
+
return self._stepc[W](x, posbuf, mask)
|
| 188 |
+
|
| 189 |
+
def prefill(self, ids, mask, compiled=True):
|
| 190 |
+
"""feed ids[:-1]; returns (x armed with ids[-1], pos of ids[-1])."""
|
| 191 |
+
for i, t in enumerate(ids[:-1]):
|
| 192 |
+
pb = mx.array([i], dtype=mx.int32)
|
| 193 |
+
W = self._window(i)
|
| 194 |
+
if compiled:
|
| 195 |
+
nt, xn, lg = self.step_compiled(self.embed(int(t)), pb, mask, W)
|
| 196 |
+
else:
|
| 197 |
+
nt, xn, lg = self._step_fn(self.embed(int(t)), pb, mask, W)
|
| 198 |
+
mx.eval(lg)
|
| 199 |
+
x = self.embed(int(ids[-1]))
|
| 200 |
+
mx.eval(x)
|
| 201 |
+
return x, len(ids) - 1
|
| 202 |
+
|
| 203 |
+
def generate(self, ids, n, compiled=True, eos=None):
|
| 204 |
+
"""greedy decode n tokens after prompt ids; returns (tokens,
|
| 205 |
+
decode_seconds). eos: optional set of stop ids (stop AFTER emitting)."""
|
| 206 |
+
mask = self.new_mask()
|
| 207 |
+
x, pos0 = self.prefill(ids, mask, compiled=compiled)
|
| 208 |
+
toks = []
|
| 209 |
+
prev = None
|
| 210 |
+
t0 = time.perf_counter()
|
| 211 |
+
for i in range(n):
|
| 212 |
+
pos = pos0 + i
|
| 213 |
+
pb = mx.array([pos], dtype=mx.int32)
|
| 214 |
+
W = self._window(pos)
|
| 215 |
+
if compiled:
|
| 216 |
+
nt, x, lg = self.step_compiled(x, pb, mask, W)
|
| 217 |
+
else:
|
| 218 |
+
nt, x, lg = self._step_fn(x, pb, mask, W)
|
| 219 |
+
mx.async_eval(nt, x)
|
| 220 |
+
if prev is not None:
|
| 221 |
+
toks.append(int(prev))
|
| 222 |
+
if eos and toks[-1] in eos:
|
| 223 |
+
prev = None
|
| 224 |
+
break
|
| 225 |
+
prev = nt
|
| 226 |
+
if prev is not None:
|
| 227 |
+
toks.append(int(prev))
|
| 228 |
+
mx.eval(x)
|
| 229 |
+
dt = time.perf_counter() - t0
|
| 230 |
+
return toks, dt
|
| 231 |
+
|
| 232 |
+
# ---------------- pure-MLX-ops reference twin ----------------
|
| 233 |
+
|
| 234 |
+
def step_ref(self, x, kv, pos):
|
| 235 |
+
"""same GEMV kernels, but QK-norm/rope/cache via mx.fast ops +
|
| 236 |
+
concat KV — the cross-check for the NEW fused kernel."""
|
| 237 |
+
eps = self.eps
|
| 238 |
+
for li, L in enumerate(self.layers):
|
| 239 |
+
qkv = trit_gemv(L.qkv_planes, L.qkv_slo, L.qkv_shi, x,
|
| 240 |
+
norm_w=L.norm1, eps=eps)
|
| 241 |
+
q = mx.fast.rms_norm(qkv[:self.qd].reshape(self.n_heads, self.hd),
|
| 242 |
+
L.qnorm, eps)
|
| 243 |
+
k = mx.fast.rms_norm(qkv[self.qd:self.qd + self.kd]
|
| 244 |
+
.reshape(self.n_kv, self.hd), L.knorm, eps)
|
| 245 |
+
qk = mx.concatenate([q, k], axis=0).reshape(
|
| 246 |
+
1, self.n_heads + self.n_kv, 1, self.hd)
|
| 247 |
+
qk = mx.fast.rope(qk, self.hd, traditional=False, base=self.theta,
|
| 248 |
+
scale=1.0, offset=pos)
|
| 249 |
+
qh = qk[:, :self.n_heads]
|
| 250 |
+
kh = qk[:, self.n_heads:]
|
| 251 |
+
vh = qkv[self.qd + self.kd:].reshape(1, self.n_kv, 1, self.hd)
|
| 252 |
+
if kv[li] is None:
|
| 253 |
+
kv[li] = (kh, vh)
|
| 254 |
+
else:
|
| 255 |
+
kv[li] = (mx.concatenate([kv[li][0], kh], axis=2),
|
| 256 |
+
mx.concatenate([kv[li][1], vh], axis=2))
|
| 257 |
+
keys, values = kv[li]
|
| 258 |
+
a = mx.fast.scaled_dot_product_attention(qh, keys, values,
|
| 259 |
+
scale=self.scale)
|
| 260 |
+
a = a.reshape(self.qd)
|
| 261 |
+
x = trit_gemv(L.o_planes, L.o_slo, L.o_shi, a, resid=x, eps=eps)
|
| 262 |
+
gu = trit_gemv(L.gu_planes, L.gu_slo, L.gu_shi, x,
|
| 263 |
+
norm_w=L.norm2, eps=eps)
|
| 264 |
+
x = trit_gemv(L.dn_planes, L.dn_slo, L.dn_shi, gu, glu=True,
|
| 265 |
+
resid=x, eps=eps)
|
| 266 |
+
return self._s8(self.eoW, self.eo_scale, x, norm_w=self.final_norm, eps=eps)
|
| 267 |
+
|
| 268 |
+
def generate_ref(self, ids, n):
|
| 269 |
+
kv = [None] * self.n_layers
|
| 270 |
+
for i, t in enumerate(ids[:-1]):
|
| 271 |
+
lg = self.step_ref(self.embed(int(t)), kv, i)
|
| 272 |
+
mx.eval(lg)
|
| 273 |
+
x = self.embed(int(ids[-1]))
|
| 274 |
+
toks = []
|
| 275 |
+
for i in range(n):
|
| 276 |
+
lg = self.step_ref(x, kv, len(ids) - 1 + i)
|
| 277 |
+
nt = mx.argmax(lg)
|
| 278 |
+
x = self.embed(nt)
|
| 279 |
+
mx.eval(x)
|
| 280 |
+
toks.append(int(nt))
|
| 281 |
+
return toks
|
mlx/fermion_mlx/spec.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""EXPERIMENTAL speculative decoding: 0.6B pack drafts, 8B pack verifies.
|
| 3 |
+
|
| 4 |
+
House exactness rule (greedy-exact acceptance): a draft token is accepted
|
| 5 |
+
iff it equals the verifier's argmax at that position. The emitted stream is
|
| 6 |
+
therefore exactly the verifier's own greedy stream *as computed by the
|
| 7 |
+
batched verify path* — the ship gate (specgate) checks that stream is
|
| 8 |
+
token-identical to the shipped single-token path at 32 and 256 tokens.
|
| 9 |
+
|
| 10 |
+
Mechanics per round (k=4 draft tokens, M=k+1 verify batch):
|
| 11 |
+
1. draft consumes its pending tokens, then chains k greedy proposals
|
| 12 |
+
d_1..d_k on its own single-token path;
|
| 13 |
+
2. verifier scores [pending[-1], d_1..d_k] in ONE batched pass
|
| 14 |
+
(mbatch.trit_gemm_m / s8_gemm_m — weight planes stream once, which is
|
| 15 |
+
the entire speedup mechanism on a bandwidth-bound GPU);
|
| 16 |
+
3. accept the longest prefix with d_i == argmax_{i-1}; emit accepted
|
| 17 |
+
tokens + the verifier's own next token (correction or bonus);
|
| 18 |
+
4. KV rewind: positions are the source of truth. The verifier's stale
|
| 19 |
+
rows get overwritten by the next round's appends before any read (the
|
| 20 |
+
verify mask only allows slots <= row position). The draft re-masks its
|
| 21 |
+
stale slots (additive mask back to -1e9) and rolls its position back.
|
| 22 |
+
|
| 23 |
+
Both models live in one process; run under --mem-limit-gb 6 (house OOM
|
| 24 |
+
rule). Additive module: model.py / gates.py / existing CLI modes untouched.
|
| 25 |
+
"""
|
| 26 |
+
import json
|
| 27 |
+
import time
|
| 28 |
+
|
| 29 |
+
import mlx.core as mx
|
| 30 |
+
|
| 31 |
+
from .mbatch import trit_gemm_m, s8_gemm_m
|
| 32 |
+
|
| 33 |
+
NEG_INF = -1e9
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _silu(g):
|
| 37 |
+
return g * mx.sigmoid(g)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class SpecDecoder:
|
| 41 |
+
def __init__(self, verifier, draft, k=4):
|
| 42 |
+
assert verifier.vocab == draft.vocab, "draft/verifier vocab mismatch"
|
| 43 |
+
self.V, self.D, self.k = verifier, draft, k
|
| 44 |
+
self._vc = {} # compiled verify fns keyed (M, W)
|
| 45 |
+
self._specmask = verifier.new_mask() # kernel side-channel, never read
|
| 46 |
+
self.stats = None
|
| 47 |
+
|
| 48 |
+
# ---------------- batched verify ----------------
|
| 49 |
+
|
| 50 |
+
def _verify(self, toks, pos0):
|
| 51 |
+
from .arch3 import qknorm_rope_append
|
| 52 |
+
V = self.V
|
| 53 |
+
M = len(toks)
|
| 54 |
+
W = V._window(pos0 + M - 1)
|
| 55 |
+
key = (M, W)
|
| 56 |
+
if key not in self._vc:
|
| 57 |
+
# build an uncompiled closure once; mx.compile wrapper around it
|
| 58 |
+
eps = V.eps
|
| 59 |
+
|
| 60 |
+
def fn(X, pbs, amask, _M=M, _W=W):
|
| 61 |
+
m4 = amask.reshape(1, 1, _M, _W)
|
| 62 |
+
for li, L in enumerate(V.layers):
|
| 63 |
+
qkv = trit_gemm_m(L.qkv_planes, L.qkv_slo, L.qkv_shi,
|
| 64 |
+
mx.fast.rms_norm(X, L.norm1, eps))
|
| 65 |
+
qrows = [qknorm_rope_append(
|
| 66 |
+
qkv[m], L.qnorm, L.knorm, V.kc[li], V.vc[li],
|
| 67 |
+
V.ct, V.st, pbs[m:m + 1], self._specmask,
|
| 68 |
+
V.n_heads, V.n_kv, V.hd, V.maxseq, eps)
|
| 69 |
+
for m in range(_M)]
|
| 70 |
+
q = mx.stack(qrows).reshape(_M, V.n_heads, V.hd)
|
| 71 |
+
q = q.transpose(1, 0, 2)[None]
|
| 72 |
+
a = mx.fast.scaled_dot_product_attention(
|
| 73 |
+
q, V.kc[li][:, :_W][None], V.vc[li][:, :_W][None],
|
| 74 |
+
scale=V.scale, mask=m4)
|
| 75 |
+
a = a[0].transpose(1, 0, 2).reshape(_M, V.qd)
|
| 76 |
+
X = X + trit_gemm_m(L.o_planes, L.o_slo, L.o_shi, a)
|
| 77 |
+
gu = trit_gemm_m(L.gu_planes, L.gu_slo, L.gu_shi,
|
| 78 |
+
mx.fast.rms_norm(X, L.norm2, eps))
|
| 79 |
+
h = _silu(gu[:, :V.inter]) * gu[:, V.inter:]
|
| 80 |
+
X = X + trit_gemm_m(L.dn_planes, L.dn_slo, L.dn_shi, h)
|
| 81 |
+
logits = s8_gemm_m(V.eoW, V.eo_scale,
|
| 82 |
+
mx.fast.rms_norm(X, V.final_norm, eps))
|
| 83 |
+
return mx.argmax(logits, axis=1)
|
| 84 |
+
|
| 85 |
+
self._vc[key] = mx.compile(fn)
|
| 86 |
+
tarr = mx.array(toks, dtype=mx.int32)
|
| 87 |
+
X = V.embW[tarr].astype(mx.float32) * V.embS[tarr][:, None]
|
| 88 |
+
pbs = mx.array([pos0 + m for m in range(M)], dtype=mx.int32)
|
| 89 |
+
idx = mx.arange(W)[None, :]
|
| 90 |
+
rowp = (pos0 + mx.arange(M))[:, None]
|
| 91 |
+
amask = mx.where(idx <= rowp, mx.array(0.0, dtype=mx.float32),
|
| 92 |
+
mx.array(NEG_INF, dtype=mx.float32))
|
| 93 |
+
am = self._vc[(M, W)](X, pbs, amask)
|
| 94 |
+
mx.eval(am)
|
| 95 |
+
return [int(t) for t in am]
|
| 96 |
+
|
| 97 |
+
# ---------------- draft helpers (single-token path) ----------------
|
| 98 |
+
|
| 99 |
+
def _draft_chain(self, pending, dmask, dpos):
|
| 100 |
+
"""consume pending tokens, then chain k greedy proposals.
|
| 101 |
+
Returns (proposals, new_dpos). Draft KV/mask advance by
|
| 102 |
+
len(pending) + k - 1 consumed inputs."""
|
| 103 |
+
# NOTE: each step is dispatched with async_eval before the next is
|
| 104 |
+
# built — the KV/mask writes are input mutations invisible to the
|
| 105 |
+
# lazy graph, and batching several steps into one evaluation makes
|
| 106 |
+
# MLX copy-on-write the caches and silently drop intermediate
|
| 107 |
+
# appends (found the hard way; the shipped generate() uses the same
|
| 108 |
+
# per-step dispatch discipline).
|
| 109 |
+
D, k = self.D, self.k
|
| 110 |
+
pos = dpos
|
| 111 |
+
nt = x = None
|
| 112 |
+
for t in pending:
|
| 113 |
+
pb = mx.array([pos], dtype=mx.int32)
|
| 114 |
+
nt, x, _ = D.step_compiled(D.embed(int(t)), pb, dmask,
|
| 115 |
+
D._window(pos))
|
| 116 |
+
mx.async_eval(nt, x)
|
| 117 |
+
pos += 1
|
| 118 |
+
props = [nt] # d_1
|
| 119 |
+
for _ in range(k - 1):
|
| 120 |
+
pb = mx.array([pos], dtype=mx.int32)
|
| 121 |
+
nt, x, _ = D.step_compiled(x, pb, dmask, D._window(pos))
|
| 122 |
+
mx.async_eval(nt, x)
|
| 123 |
+
props.append(nt)
|
| 124 |
+
pos += 1
|
| 125 |
+
mx.eval(*props)
|
| 126 |
+
return [int(p) for p in props], pos
|
| 127 |
+
|
| 128 |
+
# ---------------- main loop ----------------
|
| 129 |
+
|
| 130 |
+
def generate(self, ids, n):
|
| 131 |
+
"""greedy spec decode: n tokens after prompt ids.
|
| 132 |
+
Returns (tokens, decode_seconds, stats)."""
|
| 133 |
+
V, D, k = self.V, self.D, self.k
|
| 134 |
+
vmask = V.new_mask()
|
| 135 |
+
dmask = D.new_mask()
|
| 136 |
+
_, p0 = V.prefill(ids, vmask)
|
| 137 |
+
_, _ = D.prefill(ids, dmask)
|
| 138 |
+
# both consumed ids[:-1]; next input ids[-1] at position p0
|
| 139 |
+
pending = [int(ids[-1])]
|
| 140 |
+
vpos = dpos = p0
|
| 141 |
+
toks = []
|
| 142 |
+
rounds = accepted = proposed = 0
|
| 143 |
+
hist = [0] * (k + 1)
|
| 144 |
+
t0 = time.perf_counter()
|
| 145 |
+
while len(toks) < n:
|
| 146 |
+
# invariant: pending[-1] sits at position vpos for the verifier
|
| 147 |
+
# and at dpos + len(pending) - 1 for the draft — same slot.
|
| 148 |
+
assert dpos + len(pending) - 1 == vpos, (dpos, len(pending), vpos)
|
| 149 |
+
props, dpos_after = self._draft_chain(pending, dmask, dpos)
|
| 150 |
+
am = self._verify([pending[-1]] + props, vpos)
|
| 151 |
+
nacc = 0
|
| 152 |
+
for i in range(k):
|
| 153 |
+
if props[i] == am[i]:
|
| 154 |
+
nacc += 1
|
| 155 |
+
else:
|
| 156 |
+
break
|
| 157 |
+
rounds += 1
|
| 158 |
+
proposed += k
|
| 159 |
+
accepted += nacc
|
| 160 |
+
hist[nacc] += 1
|
| 161 |
+
base = vpos # position of pending[-1]
|
| 162 |
+
if nacc == k:
|
| 163 |
+
emitted = props + [am[k]]
|
| 164 |
+
pending = [props[k - 1], am[k]]
|
| 165 |
+
# draft consumed through d_{k-1} at base+k-1: no rewind
|
| 166 |
+
dpos = dpos_after
|
| 167 |
+
else:
|
| 168 |
+
emitted = props[:nacc] + [am[nacc]]
|
| 169 |
+
pending = [am[nacc]]
|
| 170 |
+
# draft rewind: valid through input d_nacc at base+nacc
|
| 171 |
+
lo, hi = base + nacc + 1, dpos_after
|
| 172 |
+
if hi > lo:
|
| 173 |
+
dmask[lo:hi] = NEG_INF
|
| 174 |
+
dpos = lo
|
| 175 |
+
vpos = base + nacc + 1 # verifier valid: <= base+nacc
|
| 176 |
+
toks.extend(emitted)
|
| 177 |
+
dt = time.perf_counter() - t0
|
| 178 |
+
toks = toks[:n]
|
| 179 |
+
self.stats = {
|
| 180 |
+
"k": k, "rounds": rounds, "proposed": proposed,
|
| 181 |
+
"accepted": accepted,
|
| 182 |
+
"acceptance": round(accepted / max(proposed, 1), 4),
|
| 183 |
+
"tokens_per_round": round(len(toks) / max(rounds, 1), 3),
|
| 184 |
+
"accept_hist": hist,
|
| 185 |
+
}
|
| 186 |
+
return toks, dt, self.stats
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
# ---------------- gate + bench drivers ----------------
|
| 190 |
+
|
| 191 |
+
GATE_PROMPTS = {
|
| 192 |
+
"facts": "The three laws of thermodynamics state that",
|
| 193 |
+
"prose": "Once upon a time, in a quiet village by the sea,",
|
| 194 |
+
"chat": "User: How do I reverse a list in Python?\nAssistant:",
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
def specgate(verifier, draft, tok, k=4, lens=(32, 256), receipt=None):
|
| 199 |
+
"""[G1 frozen] WITH --spec vs WITHOUT: token-identical at each length,
|
| 200 |
+
every prompt. Any mismatch = flag does not ship."""
|
| 201 |
+
sd = SpecDecoder(verifier, draft, k=k)
|
| 202 |
+
out = {"gate": "specgate", "k": k, "lens": list(lens), "prompts": {},
|
| 203 |
+
"verdict": "PASS"}
|
| 204 |
+
for name, text in GATE_PROMPTS.items():
|
| 205 |
+
ids = tok(text, add_special_tokens=False).input_ids if tok else None
|
| 206 |
+
assert ids, "specgate needs a tokenizer"
|
| 207 |
+
res = {}
|
| 208 |
+
for n in lens:
|
| 209 |
+
plain, _ = verifier.generate(ids, n)
|
| 210 |
+
spec, _, st = sd.generate(ids, n)
|
| 211 |
+
same = plain == spec
|
| 212 |
+
first = next((i for i, (a, b) in enumerate(zip(plain, spec))
|
| 213 |
+
if a != b), -1)
|
| 214 |
+
res[str(n)] = {"identical": same, "first_divergence": first,
|
| 215 |
+
"acceptance": st["acceptance"]}
|
| 216 |
+
if not same:
|
| 217 |
+
out["verdict"] = "FAIL"
|
| 218 |
+
print(f" [{name} n={n}] identical={same} "
|
| 219 |
+
f"acc={st['acceptance']:.3f}", flush=True)
|
| 220 |
+
out["prompts"][name] = res
|
| 221 |
+
if receipt:
|
| 222 |
+
json.dump(out, open(receipt, "w"), indent=1)
|
| 223 |
+
print(f"receipt -> {receipt}")
|
| 224 |
+
return out
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def specbench(verifier, draft, tok, k=4, n=512, runs=3, receipt=None):
|
| 228 |
+
"""[G2 frozen] plain-8B vs 8B+draft medians, 3 text types, same process."""
|
| 229 |
+
sd = SpecDecoder(verifier, draft, k=k)
|
| 230 |
+
out = {"bench": "specbench", "k": k, "n": n, "runs": runs, "types": {}}
|
| 231 |
+
for name, text in GATE_PROMPTS.items():
|
| 232 |
+
ids = tok(text, add_special_tokens=False).input_ids
|
| 233 |
+
plain_r, spec_r, accs = [], [], []
|
| 234 |
+
for r in range(runs):
|
| 235 |
+
t, dt = verifier.generate(ids, n)
|
| 236 |
+
plain_r.append(n / dt)
|
| 237 |
+
st_toks, sdt, st = sd.generate(ids, n)
|
| 238 |
+
spec_r.append(n / sdt)
|
| 239 |
+
accs.append(st["acceptance"])
|
| 240 |
+
print(f" [{name} run{r}] plain {plain_r[-1]:.1f} tok/s | "
|
| 241 |
+
f"spec {spec_r[-1]:.1f} tok/s | acc {accs[-1]:.3f} | "
|
| 242 |
+
f"tok/round {st['tokens_per_round']}", flush=True)
|
| 243 |
+
med = lambda v: sorted(v)[len(v) // 2]
|
| 244 |
+
out["types"][name] = {
|
| 245 |
+
"plain_median_tok_s": round(med(plain_r), 2),
|
| 246 |
+
"spec_median_tok_s": round(med(spec_r), 2),
|
| 247 |
+
"speedup": round(med(spec_r) / med(plain_r), 3),
|
| 248 |
+
"acceptance_median": round(med(accs), 4),
|
| 249 |
+
"plain_runs": [round(v, 2) for v in plain_r],
|
| 250 |
+
"spec_runs": [round(v, 2) for v in spec_r],
|
| 251 |
+
}
|
| 252 |
+
out["peak_mem_gib"] = round(mx.get_peak_memory() / 2**30, 2)
|
| 253 |
+
if receipt:
|
| 254 |
+
json.dump(out, open(receipt, "w"), indent=1)
|
| 255 |
+
print(f"receipt -> {receipt}")
|
| 256 |
+
return out
|
mlx/fermion_mlx/trit_kernels.py
ADDED
|
@@ -0,0 +1,877 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Five-value {0, +-s_lo, +-s_hi} 3-bit-planar GEMV as mx.fast.metal_kernel.
|
| 3 |
+
|
| 4 |
+
Decode: per output row, three bit planes (bp=positive, bn=negative, br=high-mag),
|
| 5 |
+
LSB-first. sign = p - n, magnitude selects per-row s_lo / s_hi. The kernel keeps
|
| 6 |
+
two fp32 accumulators (lo/hi) so the per-row scales multiply once at the end —
|
| 7 |
+
the inner loop is pure bit-extract + FMA (the Metal twin of the NEON tbl /
|
| 8 |
+
CUDA PRMT decode; MSL has no byte shuffle, ALU decode is the standard move).
|
| 9 |
+
|
| 10 |
+
Layout: one simdgroup per output row, SG_PER_TG simdgroups per threadgroup.
|
| 11 |
+
Lanes stride uchar4 loads across the row's plane bytes; simd_sum reduction.
|
| 12 |
+
|
| 13 |
+
Template flags (baked into per-shape kernel source, cached):
|
| 14 |
+
HAS_BIAS : add fp32 bias[row]
|
| 15 |
+
FUSE_NORM : x is pre-norm; kernel computes rmsnorm(x)*g inline (g = extra input)
|
| 16 |
+
ADD_RESID : out[row] = resid[row] + y
|
| 17 |
+
GLU : down-proj mode; x has 2*INTER entries [gate;up], col value is
|
| 18 |
+
silu(gate[c]) * up[c] (fuses the SwiGLU elementwise pass into GEMV)
|
| 19 |
+
"""
|
| 20 |
+
import mlx.core as mx
|
| 21 |
+
|
| 22 |
+
_HEADER = """
|
| 23 |
+
static inline float trit_silu(float v) { return v / (1.0f + metal::exp(-v)); }
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
_CACHE = {}
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _norm_pre(cols, eps):
|
| 30 |
+
return f"""
|
| 31 |
+
float ssq = 0.0f;
|
| 32 |
+
for (uint c = lane; c < {cols}u; c += 32u) {{ float v = x[c]; ssq = metal::fma(v, v, ssq); }}
|
| 33 |
+
ssq = metal::simd_sum(ssq);
|
| 34 |
+
float rms = metal::rsqrt(ssq / {float(cols)}f + {eps:.9g}f);
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def _gemv_source(rows, cols, rs, sg_per_tg, has_bias, fuse_norm, add_resid, glu, eps):
|
| 39 |
+
"""v1: per-bit scalar decode with sparsity branches."""
|
| 40 |
+
assert rs % 4 == 0, "row_stride must be multiple of 4 for uchar4 loads"
|
| 41 |
+
tail = rs * 8 != cols
|
| 42 |
+
# x effective value macro
|
| 43 |
+
if glu:
|
| 44 |
+
xv = "trit_silu(x[c]) * x[c + %d]" % cols
|
| 45 |
+
norm_pre = ""
|
| 46 |
+
elif fuse_norm:
|
| 47 |
+
xv = "x[c] * nw[c] * rms"
|
| 48 |
+
norm_pre = _norm_pre(cols, eps)
|
| 49 |
+
else:
|
| 50 |
+
xv = "x[c]"
|
| 51 |
+
norm_pre = ""
|
| 52 |
+
guard = f"if (c >= {cols}u) break;" if tail else ""
|
| 53 |
+
return f"""
|
| 54 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 55 |
+
uint lane = tid & 31u;
|
| 56 |
+
uint sg = tid >> 5u;
|
| 57 |
+
uint row = threadgroup_position_in_grid.x * {sg_per_tg}u + sg;
|
| 58 |
+
if (row >= {rows}u) return;
|
| 59 |
+
{norm_pre}
|
| 60 |
+
const device uchar4* p4 = (const device uchar4*)bp + row * {rs // 4}u;
|
| 61 |
+
const device uchar4* n4 = (const device uchar4*)bn + row * {rs // 4}u;
|
| 62 |
+
const device uchar4* h4 = (const device uchar4*)br + row * {rs // 4}u;
|
| 63 |
+
float accLo = 0.0f, accHi = 0.0f;
|
| 64 |
+
for (uint i = lane; i < {rs // 4}u; i += 32u) {{
|
| 65 |
+
uchar4 pb = p4[i], nb = n4[i], hb = h4[i];
|
| 66 |
+
uint cb = i * 32u;
|
| 67 |
+
for (uint k = 0u; k < 4u; ++k) {{
|
| 68 |
+
uint pw = pb[k], nw2 = nb[k], hw = hb[k];
|
| 69 |
+
if ((pw | nw2) == 0u) continue;
|
| 70 |
+
for (uint j = 0u; j < 8u; ++j) {{
|
| 71 |
+
uint c = cb + k * 8u + j;
|
| 72 |
+
{guard}
|
| 73 |
+
float s = (float)((pw >> j) & 1u) - (float)((nw2 >> j) & 1u);
|
| 74 |
+
if (s == 0.0f) continue;
|
| 75 |
+
float v = s * ({xv});
|
| 76 |
+
if ((hw >> j) & 1u) accHi += v; else accLo += v;
|
| 77 |
+
}}
|
| 78 |
+
}}
|
| 79 |
+
}}
|
| 80 |
+
accLo = metal::simd_sum(accLo);
|
| 81 |
+
accHi = metal::simd_sum(accHi);
|
| 82 |
+
if (lane == 0u) {{
|
| 83 |
+
float y = metal::fma(slo[row], accLo, shi[row] * accHi);
|
| 84 |
+
{"y += bias[row];" if has_bias else ""}
|
| 85 |
+
{"y += resid[row];" if add_resid else ""}
|
| 86 |
+
out[row] = y;
|
| 87 |
+
}}
|
| 88 |
+
"""
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def _gemv_source_v2(rows, cols, rs, sg_per_tg, has_bias, fuse_norm, add_resid, glu, eps):
|
| 92 |
+
"""v2: branchless decode. Planes loaded as uint32 (32 weights per load),
|
| 93 |
+
per-4-bit float4 select against float4 x loads. Requires cols % 32 == 0
|
| 94 |
+
and rs % 4 == 0 with rs*8 == cols (true for all Qwen shapes)."""
|
| 95 |
+
assert rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0
|
| 96 |
+
if glu:
|
| 97 |
+
xload = f"""float4 g = xg4[w * 8u + k], u = xu4[w * 8u + k];
|
| 98 |
+
float4 xv = g / (1.0f + metal::exp(-g)) * u;"""
|
| 99 |
+
norm_pre = ""
|
| 100 |
+
xpre = f"""
|
| 101 |
+
const device float4* xg4 = (const device float4*)x;
|
| 102 |
+
const device float4* xu4 = (const device float4*)(x + {cols}u);"""
|
| 103 |
+
elif fuse_norm:
|
| 104 |
+
xload = """float4 xv = x4[w * 8u + k] * nw4[w * 8u + k] * rms;"""
|
| 105 |
+
norm_pre = _norm_pre(cols, eps)
|
| 106 |
+
xpre = """
|
| 107 |
+
const device float4* x4 = (const device float4*)x;
|
| 108 |
+
const device float4* nw4 = (const device float4*)nw;"""
|
| 109 |
+
else:
|
| 110 |
+
xload = """float4 xv = x4[w * 8u + k];"""
|
| 111 |
+
norm_pre = ""
|
| 112 |
+
xpre = """
|
| 113 |
+
const device float4* x4 = (const device float4*)x;"""
|
| 114 |
+
return f"""
|
| 115 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 116 |
+
uint lane = tid & 31u;
|
| 117 |
+
uint sg = tid >> 5u;
|
| 118 |
+
uint row = threadgroup_position_in_grid.x * {sg_per_tg}u + sg;
|
| 119 |
+
if (row >= {rows}u) return;
|
| 120 |
+
{norm_pre}{xpre}
|
| 121 |
+
const device uint* p32 = (const device uint*)bp + row * {rs // 4}u;
|
| 122 |
+
const device uint* n32 = (const device uint*)bn + row * {rs // 4}u;
|
| 123 |
+
const device uint* h32 = (const device uint*)br + row * {rs // 4}u;
|
| 124 |
+
float4 accLo4 = 0.0f, accHi4 = 0.0f;
|
| 125 |
+
for (uint w = lane; w < {rs // 4}u; w += 32u) {{
|
| 126 |
+
uint pw = p32[w], nw2 = n32[w], hw = h32[w];
|
| 127 |
+
for (uint k = 0u; k < 8u; ++k) {{
|
| 128 |
+
{xload}
|
| 129 |
+
uint sh = k * 4u;
|
| 130 |
+
float4 s = float4(float((pw >> sh) & 1u), float((pw >> (sh+1u)) & 1u),
|
| 131 |
+
float((pw >> (sh+2u)) & 1u), float((pw >> (sh+3u)) & 1u))
|
| 132 |
+
- float4(float((nw2 >> sh) & 1u), float((nw2 >> (sh+1u)) & 1u),
|
| 133 |
+
float((nw2 >> (sh+2u)) & 1u), float((nw2 >> (sh+3u)) & 1u));
|
| 134 |
+
float4 v = s * xv;
|
| 135 |
+
bool4 hi = bool4((hw >> sh) & 1u, (hw >> (sh+1u)) & 1u,
|
| 136 |
+
(hw >> (sh+2u)) & 1u, (hw >> (sh+3u)) & 1u);
|
| 137 |
+
accHi4 += metal::select(float4(0.0f), v, hi);
|
| 138 |
+
accLo4 += metal::select(v, float4(0.0f), hi);
|
| 139 |
+
}}
|
| 140 |
+
}}
|
| 141 |
+
float accLo = metal::simd_sum(accLo4.x + accLo4.y + accLo4.z + accLo4.w);
|
| 142 |
+
float accHi = metal::simd_sum(accHi4.x + accHi4.y + accHi4.z + accHi4.w);
|
| 143 |
+
if (lane == 0u) {{
|
| 144 |
+
float y = metal::fma(slo[row], accLo, shi[row] * accHi);
|
| 145 |
+
{"y += bias[row];" if has_bias else ""}
|
| 146 |
+
{"y += resid[row];" if add_resid else ""}
|
| 147 |
+
out[row] = y;
|
| 148 |
+
}}
|
| 149 |
+
"""
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def _s8_source(rows, cols, rs, sg_per_tg, fuse_norm, eps):
|
| 153 |
+
assert rs % 4 == 0
|
| 154 |
+
if fuse_norm:
|
| 155 |
+
xv = lambda c: f"x[{c}] * nw[{c}] * rms"
|
| 156 |
+
norm_pre = f"""
|
| 157 |
+
float ssq = 0.0f;
|
| 158 |
+
for (uint c = lane; c < {cols}u; c += 32u) {{ float v = x[c]; ssq = metal::fma(v, v, ssq); }}
|
| 159 |
+
ssq = metal::simd_sum(ssq);
|
| 160 |
+
float rms = metal::rsqrt(ssq / {float(cols)}f + {eps:.9g}f);
|
| 161 |
+
"""
|
| 162 |
+
else:
|
| 163 |
+
xv = lambda c: f"x[{c}]"
|
| 164 |
+
norm_pre = ""
|
| 165 |
+
assert rs * 8 >= cols and rs >= cols, "int8 record: expect row_stride >= cols"
|
| 166 |
+
nvec = cols // 4
|
| 167 |
+
return f"""
|
| 168 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 169 |
+
uint lane = tid & 31u;
|
| 170 |
+
uint sg = tid >> 5u;
|
| 171 |
+
uint row = threadgroup_position_in_grid.x * {sg_per_tg}u + sg;
|
| 172 |
+
if (row >= {rows}u) return;
|
| 173 |
+
{norm_pre}
|
| 174 |
+
const device char* w = (const device char*)W + (ulong)row * {rs}u;
|
| 175 |
+
float acc = 0.0f;
|
| 176 |
+
for (uint i = lane * 4u; i + 3u < {cols}u; i += 128u) {{
|
| 177 |
+
char4 wv = *(const device char4*)(w + i);
|
| 178 |
+
uint c = i;
|
| 179 |
+
acc = metal::fma((float)wv.x, {xv('c')}, acc);
|
| 180 |
+
acc = metal::fma((float)wv.y, {xv('c + 1u')}, acc);
|
| 181 |
+
acc = metal::fma((float)wv.z, {xv('c + 2u')}, acc);
|
| 182 |
+
acc = metal::fma((float)wv.w, {xv('c + 3u')}, acc);
|
| 183 |
+
}}
|
| 184 |
+
acc = metal::simd_sum(acc);
|
| 185 |
+
if (lane == 0u) out[row] = scale[row] * acc;
|
| 186 |
+
""" if nvec * 4 == cols else None
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
SG_PER_TG = int(__import__("os").environ.get("TRIT_METAL_SGPTG", "8"))
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
import os
|
| 193 |
+
KERNEL_VERSION = int(os.environ.get("TRIT_METAL_V", "1"))
|
| 194 |
+
RPSG = int(os.environ.get("TRIT_METAL_RPSG", "8"))
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def _gemv_source_v3(rows, cols, rs, sg_per_tg, has_bias, fuse_norm, add_resid, glu, eps,
|
| 198 |
+
rpsg=RPSG):
|
| 199 |
+
"""v3: rpsg rows per simdgroup; x (and silu/norm work) loaded once per
|
| 200 |
+
column window and reused across rows in registers — kills the per-row
|
| 201 |
+
x-reload that bound v1/v2 at ~20 GB/s. 4-wide horizontal accumulate."""
|
| 202 |
+
assert rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0 and rows % rpsg == 0
|
| 203 |
+
if glu:
|
| 204 |
+
xload = f"""float4 g = xg4[w * 8u + k], u = xu4[w * 8u + k];
|
| 205 |
+
float4 xv = g / (1.0f + metal::exp(-g)) * u;"""
|
| 206 |
+
norm_pre = ""
|
| 207 |
+
xpre = f"""
|
| 208 |
+
const device float4* xg4 = (const device float4*)x;
|
| 209 |
+
const device float4* xu4 = (const device float4*)(x + {cols}u);"""
|
| 210 |
+
elif fuse_norm:
|
| 211 |
+
xload = "float4 xv = x4[w * 8u + k] * nw4[w * 8u + k] * rms;"
|
| 212 |
+
norm_pre = _norm_pre(cols, eps)
|
| 213 |
+
xpre = """
|
| 214 |
+
const device float4* x4 = (const device float4*)x;
|
| 215 |
+
const device float4* nw4 = (const device float4*)nw;"""
|
| 216 |
+
else:
|
| 217 |
+
xload = "float4 xv = x4[w * 8u + k];"
|
| 218 |
+
norm_pre = ""
|
| 219 |
+
xpre = """
|
| 220 |
+
const device float4* x4 = (const device float4*)x;"""
|
| 221 |
+
return f"""
|
| 222 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 223 |
+
uint lane = tid & 31u;
|
| 224 |
+
uint sg = tid >> 5u;
|
| 225 |
+
uint row0 = (threadgroup_position_in_grid.x * {sg_per_tg}u + sg) * {rpsg}u;
|
| 226 |
+
if (row0 >= {rows}u) return;
|
| 227 |
+
{norm_pre}{xpre}
|
| 228 |
+
const device uint* p32 = (const device uint*)bp;
|
| 229 |
+
const device uint* n32 = (const device uint*)bn;
|
| 230 |
+
const device uint* h32 = (const device uint*)br;
|
| 231 |
+
float accLo[{rpsg}], accHi[{rpsg}];
|
| 232 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{ accLo[r] = 0.0f; accHi[r] = 0.0f; }}
|
| 233 |
+
const float4 one4 = float4(1.0f);
|
| 234 |
+
for (uint w = lane; w < {rs // 4}u; w += 32u) {{
|
| 235 |
+
uint pw[{rpsg}], nw2[{rpsg}], hw[{rpsg}];
|
| 236 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 237 |
+
ulong off = (ulong)(row0 + r) * {rs // 4}u + w;
|
| 238 |
+
pw[r] = p32[off]; nw2[r] = n32[off]; hw[r] = h32[off];
|
| 239 |
+
}}
|
| 240 |
+
for (uint k = 0u; k < 8u; ++k) {{
|
| 241 |
+
{xload}
|
| 242 |
+
uint sh = k * 4u;
|
| 243 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 244 |
+
float4 s = float4(float((pw[r] >> sh) & 1u), float((pw[r] >> (sh+1u)) & 1u),
|
| 245 |
+
float((pw[r] >> (sh+2u)) & 1u), float((pw[r] >> (sh+3u)) & 1u))
|
| 246 |
+
- float4(float((nw2[r] >> sh) & 1u), float((nw2[r] >> (sh+1u)) & 1u),
|
| 247 |
+
float((nw2[r] >> (sh+2u)) & 1u), float((nw2[r] >> (sh+3u)) & 1u));
|
| 248 |
+
float4 v = s * xv;
|
| 249 |
+
bool4 hi = bool4((hw[r] >> sh) & 1u, (hw[r] >> (sh+1u)) & 1u,
|
| 250 |
+
(hw[r] >> (sh+2u)) & 1u, (hw[r] >> (sh+3u)) & 1u);
|
| 251 |
+
accHi[r] += metal::dot(metal::select(float4(0.0f), v, hi), one4);
|
| 252 |
+
accLo[r] += metal::dot(metal::select(v, float4(0.0f), hi), one4);
|
| 253 |
+
}}
|
| 254 |
+
}}
|
| 255 |
+
}}
|
| 256 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 257 |
+
float lo = metal::simd_sum(accLo[r]);
|
| 258 |
+
float hi = metal::simd_sum(accHi[r]);
|
| 259 |
+
if (lane == 0u) {{
|
| 260 |
+
uint row = row0 + r;
|
| 261 |
+
float y = metal::fma(slo[row], lo, shi[row] * hi);
|
| 262 |
+
{"y += bias[row];" if has_bias else ""}
|
| 263 |
+
{"y += resid[row];" if add_resid else ""}
|
| 264 |
+
out[row] = y;
|
| 265 |
+
}}
|
| 266 |
+
}}
|
| 267 |
+
"""
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
def _gemv_source_v4(rows, cols, rs, sg_per_tg, has_bias, fuse_norm, add_resid, glu, eps,
|
| 271 |
+
rpsg=RPSG):
|
| 272 |
+
"""v4: threadgroup-memory LUT decode (Metal twin of NEON vqtbl1q).
|
| 273 |
+
Per 4-column group g, lut[g][m] = sum of xv[4g+i] over set bits of m
|
| 274 |
+
(16 subset sums). Split planes on the fly into lo/hi pos/neg nibbles
|
| 275 |
+
(a=p&~h, b=n&~h, c=p&h, d=n&h): 4 weights cost 4 LUT loads + 4 adds.
|
| 276 |
+
LUT stride 17 floats kills the 16-float bank-conflict pattern. Tiled over
|
| 277 |
+
columns when the LUT would exceed threadgroup memory (down proj)."""
|
| 278 |
+
assert rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0 and rows % rpsg == 0
|
| 279 |
+
# column tiling: LUT floats = tile/4*17, xbuf floats = tile -> keep <= ~26KB
|
| 280 |
+
ntiles = 1
|
| 281 |
+
while (cols // ntiles) % 32 != 0 or (cols // ntiles) * (17 / 4 + 1) * 4 > 26624:
|
| 282 |
+
ntiles += 1
|
| 283 |
+
assert ntiles <= 16
|
| 284 |
+
tile = cols // ntiles
|
| 285 |
+
gpt = tile // 4 # groups per tile
|
| 286 |
+
w0 = tile // 32 # 32-weight words per tile (per row)
|
| 287 |
+
nthreads = sg_per_tg * 32
|
| 288 |
+
if glu:
|
| 289 |
+
xv_expr = "{ float gg = x[c]; xb[cl] = gg / (1.0f + metal::exp(-gg)) * x[c + %du]; }" % cols
|
| 290 |
+
elif fuse_norm:
|
| 291 |
+
xv_expr = "xb[cl] = x[c] * nw[c] * rms;"
|
| 292 |
+
else:
|
| 293 |
+
xv_expr = "xb[cl] = x[c];"
|
| 294 |
+
if fuse_norm:
|
| 295 |
+
norm_pre = f"""
|
| 296 |
+
threadgroup float ssq_sh[{sg_per_tg}];
|
| 297 |
+
{{
|
| 298 |
+
float ssq = 0.0f;
|
| 299 |
+
for (uint c = tid; c < {cols}u; c += {nthreads}u) {{ float v = x[c]; ssq = metal::fma(v, v, ssq); }}
|
| 300 |
+
ssq = metal::simd_sum(ssq);
|
| 301 |
+
if (lane == 0u) ssq_sh[sg] = ssq;
|
| 302 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 303 |
+
float t = 0.0f;
|
| 304 |
+
for (uint i = 0u; i < {sg_per_tg}u; ++i) t += ssq_sh[i];
|
| 305 |
+
rms = metal::rsqrt(t / {float(cols)}f + {eps:.9g}f);
|
| 306 |
+
}}
|
| 307 |
+
"""
|
| 308 |
+
else:
|
| 309 |
+
norm_pre = ""
|
| 310 |
+
return f"""
|
| 311 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 312 |
+
uint lane = tid & 31u;
|
| 313 |
+
uint sg = tid >> 5u;
|
| 314 |
+
uint row0 = (threadgroup_position_in_grid.x * {sg_per_tg}u + sg) * {rpsg}u;
|
| 315 |
+
float rms = 1.0f;
|
| 316 |
+
{norm_pre}
|
| 317 |
+
threadgroup float xb[{tile}];
|
| 318 |
+
threadgroup float lut[{gpt} * 17];
|
| 319 |
+
const device uint* p32 = (const device uint*)bp;
|
| 320 |
+
const device uint* n32 = (const device uint*)bn;
|
| 321 |
+
const device uint* h32 = (const device uint*)br;
|
| 322 |
+
float accLo[{rpsg}], accHi[{rpsg}];
|
| 323 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{ accLo[r] = 0.0f; accHi[r] = 0.0f; }}
|
| 324 |
+
for (uint t = 0u; t < {ntiles}u; ++t) {{
|
| 325 |
+
// stage effective x for this tile
|
| 326 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 327 |
+
for (uint cl = tid; cl < {tile}u; cl += {nthreads}u) {{
|
| 328 |
+
uint c = t * {tile}u + cl;
|
| 329 |
+
{xv_expr}
|
| 330 |
+
}}
|
| 331 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 332 |
+
// build 16 subset sums per 4-col group
|
| 333 |
+
for (uint e = tid; e < {gpt}u * 16u; e += {nthreads}u) {{
|
| 334 |
+
uint g = e >> 4u, m = e & 15u;
|
| 335 |
+
const threadgroup float* xg = xb + g * 4u;
|
| 336 |
+
float v = 0.0f;
|
| 337 |
+
if (m & 1u) v += xg[0];
|
| 338 |
+
if (m & 2u) v += xg[1];
|
| 339 |
+
if (m & 4u) v += xg[2];
|
| 340 |
+
if (m & 8u) v += xg[3];
|
| 341 |
+
lut[g * 17u + m] = v;
|
| 342 |
+
}}
|
| 343 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 344 |
+
if (row0 < {rows}u) {{
|
| 345 |
+
for (uint wl = lane; wl < {w0}u; wl += 32u) {{
|
| 346 |
+
uint w = t * {w0}u + wl;
|
| 347 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 348 |
+
ulong off = (ulong)(row0 + r) * {rs // 4}u + w;
|
| 349 |
+
uint pw = p32[off], nw2 = n32[off], hw = h32[off];
|
| 350 |
+
uint a = pw & ~hw, b = nw2 & ~hw, cc = pw & hw, d = nw2 & hw;
|
| 351 |
+
uint gbase = wl * 8u;
|
| 352 |
+
float lo = 0.0f, hi = 0.0f;
|
| 353 |
+
for (uint k = 0u; k < 8u; ++k) {{
|
| 354 |
+
const threadgroup float* lg = lut + (gbase + k) * 17u;
|
| 355 |
+
uint sh = k * 4u;
|
| 356 |
+
lo += lg[(a >> sh) & 15u] - lg[(b >> sh) & 15u];
|
| 357 |
+
hi += lg[(cc >> sh) & 15u] - lg[(d >> sh) & 15u];
|
| 358 |
+
}}
|
| 359 |
+
accLo[r] += lo; accHi[r] += hi;
|
| 360 |
+
}}
|
| 361 |
+
}}
|
| 362 |
+
}}
|
| 363 |
+
}}
|
| 364 |
+
if (row0 >= {rows}u) return;
|
| 365 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 366 |
+
float lo = metal::simd_sum(accLo[r]);
|
| 367 |
+
float hi = metal::simd_sum(accHi[r]);
|
| 368 |
+
if (lane == 0u) {{
|
| 369 |
+
uint row = row0 + r;
|
| 370 |
+
float y = metal::fma(slo[row], lo, shi[row] * hi);
|
| 371 |
+
{"y += bias[row];" if has_bias else ""}
|
| 372 |
+
{"y += resid[row];" if add_resid else ""}
|
| 373 |
+
out[row] = y;
|
| 374 |
+
}}
|
| 375 |
+
}}
|
| 376 |
+
"""
|
| 377 |
+
|
| 378 |
+
|
| 379 |
+
import os as _os
|
| 380 |
+
_TILE_MAX = int(_os.environ.get("TRIT_METAL_TILE", "448"))
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
def _v5_tile(cols):
|
| 384 |
+
"""largest tile <= TILE_MAX with tile % 32 == 0 and cols % tile == 0."""
|
| 385 |
+
for t in range(_TILE_MAX, 31, -32):
|
| 386 |
+
if cols % t == 0 and t % 32 == 0:
|
| 387 |
+
return t
|
| 388 |
+
raise ValueError(cols)
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def _gemv_source_v5(rows, cols, rs, sg_per_tg, has_bias, fuse_norm, add_resid, glu, eps,
|
| 392 |
+
rpsg=8):
|
| 393 |
+
"""v5 = v4 LUT decode with the two occupancy/bandwidth fixes:
|
| 394 |
+
(1) planes pre-interleaved on load: uint32 words stored [row_block, word,
|
| 395 |
+
row_in_block] so a lane's 8-row load is 32 contiguous bytes (streams
|
| 396 |
+
at DRAM bandwidth; the v3/v4 scattered pattern ran at 59% of it);
|
| 397 |
+
(2) small LUT tiles (<= ~7.6KB threadgroup memory) so several threadgroups
|
| 398 |
+
fit per GPU core (v4's 19-26KB allowed only one, exposing latency).
|
| 399 |
+
Requires planes packed with pack_planes_v5. rpsg fixed at 8 by layout."""
|
| 400 |
+
assert rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0 and rows % 8 == 0
|
| 401 |
+
tile = _v5_tile(cols)
|
| 402 |
+
ntiles = cols // tile
|
| 403 |
+
gpt = tile // 4
|
| 404 |
+
w0 = tile // 32
|
| 405 |
+
W = rs // 4
|
| 406 |
+
nthreads = sg_per_tg * 32
|
| 407 |
+
if glu:
|
| 408 |
+
stage = f"""
|
| 409 |
+
for (uint cl = tid; cl < {tile}u; cl += {nthreads}u) {{
|
| 410 |
+
uint c = t * {tile}u + cl;
|
| 411 |
+
float gg = x[c];
|
| 412 |
+
xb[cl] = gg / (1.0f + metal::exp(-gg)) * x[c + {cols}u];
|
| 413 |
+
}}
|
| 414 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);"""
|
| 415 |
+
xval = "xb[gl * 4u + i]"
|
| 416 |
+
xb_decl = f"threadgroup float xb[{tile}];"
|
| 417 |
+
elif fuse_norm:
|
| 418 |
+
stage = ""
|
| 419 |
+
xval = "x[t * %du + gl * 4u + i] * nw[t * %du + gl * 4u + i] * rms" % (tile, tile)
|
| 420 |
+
xb_decl = ""
|
| 421 |
+
else:
|
| 422 |
+
stage = ""
|
| 423 |
+
xval = "x[t * %du + gl * 4u + i]" % tile
|
| 424 |
+
xb_decl = ""
|
| 425 |
+
if fuse_norm:
|
| 426 |
+
norm_pre = f"""
|
| 427 |
+
threadgroup float ssq_sh[{sg_per_tg}];
|
| 428 |
+
{{
|
| 429 |
+
float ssq = 0.0f;
|
| 430 |
+
for (uint c = tid; c < {cols}u; c += {nthreads}u) {{ float v = x[c]; ssq = metal::fma(v, v, ssq); }}
|
| 431 |
+
ssq = metal::simd_sum(ssq);
|
| 432 |
+
if (lane == 0u) ssq_sh[sg] = ssq;
|
| 433 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 434 |
+
float tt = 0.0f;
|
| 435 |
+
for (uint i = 0u; i < {sg_per_tg}u; ++i) tt += ssq_sh[i];
|
| 436 |
+
rms = metal::rsqrt(tt / {float(cols)}f + {eps:.9g}f);
|
| 437 |
+
}}
|
| 438 |
+
"""
|
| 439 |
+
else:
|
| 440 |
+
norm_pre = ""
|
| 441 |
+
return f"""
|
| 442 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 443 |
+
uint lane = tid & 31u;
|
| 444 |
+
uint sg = tid >> 5u;
|
| 445 |
+
uint blk = threadgroup_position_in_grid.x * {sg_per_tg}u + sg; // 8-row block
|
| 446 |
+
uint row0 = blk * 8u;
|
| 447 |
+
float rms = 1.0f;
|
| 448 |
+
{norm_pre}
|
| 449 |
+
{xb_decl}
|
| 450 |
+
threadgroup float lut[{gpt} * 17];
|
| 451 |
+
const device uint* p32 = (const device uint*)bp;
|
| 452 |
+
const device uint* n32 = (const device uint*)bn;
|
| 453 |
+
const device uint* h32 = (const device uint*)br;
|
| 454 |
+
float accLo[8], accHi[8];
|
| 455 |
+
for (uint r = 0u; r < 8u; ++r) {{ accLo[r] = 0.0f; accHi[r] = 0.0f; }}
|
| 456 |
+
for (uint t = 0u; t < {ntiles}u; ++t) {{
|
| 457 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 458 |
+
{stage}
|
| 459 |
+
for (uint e = tid; e < {gpt}u * 16u; e += {nthreads}u) {{
|
| 460 |
+
uint gl = e >> 4u, m = e & 15u;
|
| 461 |
+
float v = 0.0f;
|
| 462 |
+
for (uint i = 0u; i < 4u; ++i)
|
| 463 |
+
if ((m >> i) & 1u) v += {xval};
|
| 464 |
+
lut[gl * 17u + m] = v;
|
| 465 |
+
}}
|
| 466 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 467 |
+
if (row0 < {rows}u) {{
|
| 468 |
+
for (uint wl = lane; wl < {w0}u; wl += 32u) {{
|
| 469 |
+
uint w = t * {w0}u + wl;
|
| 470 |
+
ulong base = ((ulong)blk * {W}u + w) * 8u; // 8 rows contiguous
|
| 471 |
+
uint gbase = wl * 8u;
|
| 472 |
+
for (uint r = 0u; r < 8u; ++r) {{
|
| 473 |
+
uint pw = p32[base + r], nw2 = n32[base + r], hw = h32[base + r];
|
| 474 |
+
uint a = pw & ~hw, b = nw2 & ~hw, cc = pw & hw, d = nw2 & hw;
|
| 475 |
+
float lo = 0.0f, hi = 0.0f;
|
| 476 |
+
for (uint k = 0u; k < 8u; ++k) {{
|
| 477 |
+
const threadgroup float* lg = lut + (gbase + k) * 17u;
|
| 478 |
+
uint sh = k * 4u;
|
| 479 |
+
lo += lg[(a >> sh) & 15u] - lg[(b >> sh) & 15u];
|
| 480 |
+
hi += lg[(cc >> sh) & 15u] - lg[(d >> sh) & 15u];
|
| 481 |
+
}}
|
| 482 |
+
accLo[r] += lo; accHi[r] += hi;
|
| 483 |
+
}}
|
| 484 |
+
}}
|
| 485 |
+
}}
|
| 486 |
+
}}
|
| 487 |
+
if (row0 >= {rows}u) return;
|
| 488 |
+
for (uint r = 0u; r < 8u; ++r) {{
|
| 489 |
+
float lo = metal::simd_sum(accLo[r]);
|
| 490 |
+
float hi = metal::simd_sum(accHi[r]);
|
| 491 |
+
if (lane == 0u) {{
|
| 492 |
+
uint row = row0 + r;
|
| 493 |
+
float y = metal::fma(slo[row], lo, shi[row] * hi);
|
| 494 |
+
{"y += bias[row];" if has_bias else ""}
|
| 495 |
+
{"y += resid[row];" if add_resid else ""}
|
| 496 |
+
out[row] = y;
|
| 497 |
+
}}
|
| 498 |
+
}}
|
| 499 |
+
"""
|
| 500 |
+
|
| 501 |
+
|
| 502 |
+
def _gemv_source_v6(rows, cols, rs, sg_per_tg, has_bias, fuse_norm, add_resid, glu, eps):
|
| 503 |
+
"""v6: interleaved 8-row plane layout (pack_planes_v5) + mask-convert-fma
|
| 504 |
+
decode. For bit j of word a: (float)(a & (1<<j)) == 2^j when set, so
|
| 505 |
+
multiplying by x pre-scaled with 2^-j turns decode into AND + convert +
|
| 506 |
+
FMA (no shifts) — 4 masks (lo/hi x pos/neg) ~3.5 instr/weight, exact in
|
| 507 |
+
fp32 (power-of-two scales). x reused across the 8 rows in registers."""
|
| 508 |
+
assert rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0 and rows % 8 == 0
|
| 509 |
+
W = rs // 4
|
| 510 |
+
if glu:
|
| 511 |
+
xload = f"""float4 g = xg4[w * 8u + k], u = xu4[w * 8u + k];
|
| 512 |
+
float4 xv = g / (1.0f + metal::exp(-g)) * u;"""
|
| 513 |
+
norm_pre = ""
|
| 514 |
+
xpre = f"""
|
| 515 |
+
const device float4* xg4 = (const device float4*)x;
|
| 516 |
+
const device float4* xu4 = (const device float4*)(x + {cols}u);"""
|
| 517 |
+
elif fuse_norm:
|
| 518 |
+
xload = "float4 xv = x4[w * 8u + k] * nw4[w * 8u + k] * rms;"
|
| 519 |
+
norm_pre = _norm_pre(cols, eps)
|
| 520 |
+
xpre = """
|
| 521 |
+
const device float4* x4 = (const device float4*)x;
|
| 522 |
+
const device float4* nw4 = (const device float4*)nw;"""
|
| 523 |
+
else:
|
| 524 |
+
xload = "float4 xv = x4[w * 8u + k];"
|
| 525 |
+
norm_pre = ""
|
| 526 |
+
xpre = """
|
| 527 |
+
const device float4* x4 = (const device float4*)x;"""
|
| 528 |
+
# unrolled k-body with literal masks / prescales
|
| 529 |
+
kbody = []
|
| 530 |
+
for k in range(8):
|
| 531 |
+
m = [1 << (4 * k + i) for i in range(4)]
|
| 532 |
+
ps = ", ".join("%.9ef" % (2.0 ** -(4 * k + i)) for i in range(4))
|
| 533 |
+
kbody.append(f""" {{
|
| 534 |
+
uint k = {k}u; {xload}
|
| 535 |
+
float4 xs = xv * float4({ps});
|
| 536 |
+
const uint4 m4 = uint4({m[0]}u, {m[1]}u, {m[2]}u, {m[3]}u);
|
| 537 |
+
for (uint r = 0u; r < 8u; ++r) {{
|
| 538 |
+
uint pw = pl[r], nw2 = nl[r], hw = hl[r];
|
| 539 |
+
uint a = pw & ~hw, b = nw2 & ~hw, cc = pw & hw, d = nw2 & hw;
|
| 540 |
+
accLo[r] += metal::dot(float4(uint4(a) & m4) - float4(uint4(b) & m4), xs);
|
| 541 |
+
accHi[r] += metal::dot(float4(uint4(cc) & m4) - float4(uint4(d) & m4), xs);
|
| 542 |
+
}}
|
| 543 |
+
}}""")
|
| 544 |
+
kbody = "\n".join(kbody)
|
| 545 |
+
return f"""
|
| 546 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 547 |
+
uint lane = tid & 31u;
|
| 548 |
+
uint sg = tid >> 5u;
|
| 549 |
+
uint blk = threadgroup_position_in_grid.x * {sg_per_tg}u + sg;
|
| 550 |
+
uint row0 = blk * 8u;
|
| 551 |
+
if (row0 >= {rows}u) return;
|
| 552 |
+
{norm_pre}{xpre}
|
| 553 |
+
const device uint* p32 = (const device uint*)bp;
|
| 554 |
+
const device uint* n32 = (const device uint*)bn;
|
| 555 |
+
const device uint* h32 = (const device uint*)br;
|
| 556 |
+
float accLo[8], accHi[8];
|
| 557 |
+
for (uint r = 0u; r < 8u; ++r) {{ accLo[r] = 0.0f; accHi[r] = 0.0f; }}
|
| 558 |
+
for (uint w = lane; w < {W}u; w += 32u) {{
|
| 559 |
+
ulong base = ((ulong)blk * {W}u + w) * 8u;
|
| 560 |
+
uint pl[8], nl[8], hl[8];
|
| 561 |
+
for (uint r = 0u; r < 8u; ++r) {{ pl[r] = p32[base+r]; nl[r] = n32[base+r]; hl[r] = h32[base+r]; }}
|
| 562 |
+
{kbody}
|
| 563 |
+
}}
|
| 564 |
+
for (uint r = 0u; r < 8u; ++r) {{
|
| 565 |
+
float lo = metal::simd_sum(accLo[r]);
|
| 566 |
+
float hi = metal::simd_sum(accHi[r]);
|
| 567 |
+
if (lane == 0u) {{
|
| 568 |
+
uint row = row0 + r;
|
| 569 |
+
float y = metal::fma(slo[row], lo, shi[row] * hi);
|
| 570 |
+
{"y += bias[row];" if has_bias else ""}
|
| 571 |
+
{"y += resid[row];" if add_resid else ""}
|
| 572 |
+
out[row] = y;
|
| 573 |
+
}}
|
| 574 |
+
}}
|
| 575 |
+
"""
|
| 576 |
+
|
| 577 |
+
|
| 578 |
+
def _gemv_source_v7(rows, cols, rs, sg_per_tg, has_bias, fuse_norm, add_resid, glu, eps):
|
| 579 |
+
"""v7: v5 interleaved layout + LUT decode with full lane utilization.
|
| 580 |
+
Lane = (row r = lane&7, word-slot = lane>>3): each lane accumulates ONE
|
| 581 |
+
row over words wslot, wslot+4, ... — all 32 lanes busy for any w0 >= 4.
|
| 582 |
+
Row sums gathered with two simd_shuffle_down folds. LUT tile 448 cols
|
| 583 |
+
(7.6KB threadgroup mem) so multiple threadgroups stay resident per core."""
|
| 584 |
+
assert rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0 and rows % 8 == 0
|
| 585 |
+
tile = _v5_tile(cols)
|
| 586 |
+
ntiles = cols // tile
|
| 587 |
+
gpt = tile // 4
|
| 588 |
+
w0 = tile // 32
|
| 589 |
+
W = rs // 4
|
| 590 |
+
nthreads = sg_per_tg * 32
|
| 591 |
+
if glu:
|
| 592 |
+
stage = f"""
|
| 593 |
+
for (uint cl = tid; cl < {tile}u; cl += {nthreads}u) {{
|
| 594 |
+
uint c = t * {tile}u + cl;
|
| 595 |
+
float gg = x[c];
|
| 596 |
+
xb[cl] = gg / (1.0f + metal::exp(-gg)) * x[c + {cols}u];
|
| 597 |
+
}}
|
| 598 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);"""
|
| 599 |
+
xval = "xb[gl * 4u + i]"
|
| 600 |
+
xb_decl = f"threadgroup float xb[{tile}];"
|
| 601 |
+
elif fuse_norm:
|
| 602 |
+
stage = ""
|
| 603 |
+
xval = "x[t * %du + gl * 4u + i] * nw[t * %du + gl * 4u + i] * rms" % (tile, tile)
|
| 604 |
+
xb_decl = ""
|
| 605 |
+
else:
|
| 606 |
+
stage = ""
|
| 607 |
+
xval = "x[t * %du + gl * 4u + i]" % tile
|
| 608 |
+
xb_decl = ""
|
| 609 |
+
if fuse_norm:
|
| 610 |
+
norm_pre = f"""
|
| 611 |
+
threadgroup float ssq_sh[{sg_per_tg}];
|
| 612 |
+
{{
|
| 613 |
+
float ssq = 0.0f;
|
| 614 |
+
for (uint c = tid; c < {cols}u; c += {nthreads}u) {{ float v = x[c]; ssq = metal::fma(v, v, ssq); }}
|
| 615 |
+
ssq = metal::simd_sum(ssq);
|
| 616 |
+
if (lane == 0u) ssq_sh[sg] = ssq;
|
| 617 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 618 |
+
float tt = 0.0f;
|
| 619 |
+
for (uint i = 0u; i < {sg_per_tg}u; ++i) tt += ssq_sh[i];
|
| 620 |
+
rms = metal::rsqrt(tt / {float(cols)}f + {eps:.9g}f);
|
| 621 |
+
}}
|
| 622 |
+
"""
|
| 623 |
+
else:
|
| 624 |
+
norm_pre = ""
|
| 625 |
+
return f"""
|
| 626 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 627 |
+
uint lane = tid & 31u;
|
| 628 |
+
uint sg = tid >> 5u;
|
| 629 |
+
uint blk = threadgroup_position_in_grid.x * {sg_per_tg}u + sg;
|
| 630 |
+
uint row0 = blk * 8u;
|
| 631 |
+
uint r = lane & 7u;
|
| 632 |
+
uint wsl = lane >> 3u;
|
| 633 |
+
float rms = 1.0f;
|
| 634 |
+
{norm_pre}
|
| 635 |
+
{xb_decl}
|
| 636 |
+
threadgroup float lut[{gpt} * 17];
|
| 637 |
+
const device uint* p32 = (const device uint*)bp;
|
| 638 |
+
const device uint* n32 = (const device uint*)bn;
|
| 639 |
+
const device uint* h32 = (const device uint*)br;
|
| 640 |
+
float accLo = 0.0f, accHi = 0.0f;
|
| 641 |
+
for (uint t = 0u; t < {ntiles}u; ++t) {{
|
| 642 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 643 |
+
{stage}
|
| 644 |
+
for (uint e = tid; e < {gpt}u * 16u; e += {nthreads}u) {{
|
| 645 |
+
uint gl = e >> 4u, m = e & 15u;
|
| 646 |
+
float v = 0.0f;
|
| 647 |
+
for (uint i = 0u; i < 4u; ++i)
|
| 648 |
+
if ((m >> i) & 1u) v += {xval};
|
| 649 |
+
lut[gl * 17u + m] = v;
|
| 650 |
+
}}
|
| 651 |
+
threadgroup_barrier(metal::mem_flags::mem_threadgroup);
|
| 652 |
+
if (row0 < {rows}u) {{
|
| 653 |
+
for (uint wl = wsl; wl < {w0}u; wl += 4u) {{
|
| 654 |
+
ulong base = ((ulong)blk * {W}u + t * {w0}u + wl) * 8u + r;
|
| 655 |
+
uint pw = p32[base], nw2 = n32[base], hw = h32[base];
|
| 656 |
+
uint a = pw & ~hw, b = nw2 & ~hw, cc = pw & hw, d = nw2 & hw;
|
| 657 |
+
uint gbase = wl * 8u;
|
| 658 |
+
for (uint k = 0u; k < 8u; ++k) {{
|
| 659 |
+
const threadgroup float* lg = lut + (gbase + k) * 17u;
|
| 660 |
+
uint sh = k * 4u;
|
| 661 |
+
accLo += lg[(a >> sh) & 15u] - lg[(b >> sh) & 15u];
|
| 662 |
+
accHi += lg[(cc >> sh) & 15u] - lg[(d >> sh) & 15u];
|
| 663 |
+
}}
|
| 664 |
+
}}
|
| 665 |
+
}}
|
| 666 |
+
}}
|
| 667 |
+
if (row0 >= {rows}u) return;
|
| 668 |
+
accLo += metal::simd_shuffle_down(accLo, 16u);
|
| 669 |
+
accLo += metal::simd_shuffle_down(accLo, 8u);
|
| 670 |
+
accHi += metal::simd_shuffle_down(accHi, 16u);
|
| 671 |
+
accHi += metal::simd_shuffle_down(accHi, 8u);
|
| 672 |
+
if (lane < 8u) {{
|
| 673 |
+
uint row = row0 + lane;
|
| 674 |
+
float y = metal::fma(slo[row], accLo, shi[row] * accHi);
|
| 675 |
+
{"y += bias[row];" if has_bias else ""}
|
| 676 |
+
{"y += resid[row];" if add_resid else ""}
|
| 677 |
+
out[row] = y;
|
| 678 |
+
}}
|
| 679 |
+
"""
|
| 680 |
+
|
| 681 |
+
|
| 682 |
+
def prep_planes(planes, cols=None):
|
| 683 |
+
"""Trim row padding (row_stride*8 > cols in real exports; pad bits are
|
| 684 |
+
zero) so rs*8 == cols, then apply the layout transform required by the
|
| 685 |
+
active kernel version. planes = (bp, bn, br) mx.uint8 [rows, rs]."""
|
| 686 |
+
if cols is not None and cols % 8 == 0 and planes[0].shape[1] > cols // 8:
|
| 687 |
+
planes = tuple(mx.contiguous(p[:, :cols // 8]) for p in planes)
|
| 688 |
+
if KERNEL_VERSION >= 5: # v5/v6 interleaved layout
|
| 689 |
+
return tuple(pack_planes_v5(p) for p in planes)
|
| 690 |
+
return planes
|
| 691 |
+
|
| 692 |
+
|
| 693 |
+
def pack_planes_v5(plane_mx):
|
| 694 |
+
"""[rows, rs] uint8 -> v5 interleaved layout (same byte count):
|
| 695 |
+
uint32 words reordered to [row_block(8), word, row_in_block]."""
|
| 696 |
+
rows, rs = plane_mx.shape
|
| 697 |
+
assert rows % 8 == 0 and rs % 4 == 0
|
| 698 |
+
w = plane_mx.view(mx.uint32).reshape(rows // 8, 8, rs // 4)
|
| 699 |
+
return mx.contiguous(w.transpose(0, 2, 1)).view(mx.uint8).reshape(rows, rs)
|
| 700 |
+
|
| 701 |
+
|
| 702 |
+
def _rows_per_sg(rows, cols, rs, version, glu):
|
| 703 |
+
if version == 3 and rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0 and rows % RPSG == 0:
|
| 704 |
+
return RPSG
|
| 705 |
+
return 1
|
| 706 |
+
|
| 707 |
+
|
| 708 |
+
def get_trit_gemv(rows, cols, rs, has_bias=False, fuse_norm=False, add_resid=False,
|
| 709 |
+
glu=False, eps=1e-6, version=None):
|
| 710 |
+
version = KERNEL_VERSION if version is None else version
|
| 711 |
+
if version >= 2 and not (rs % 4 == 0 and rs * 8 == cols and cols % 32 == 0):
|
| 712 |
+
version = 1 # v2+ layout preconditions not met, fall back
|
| 713 |
+
if version >= 3 and rows % (8 if version >= 5 else RPSG) != 0:
|
| 714 |
+
version = 2
|
| 715 |
+
key = ("trit", rows, cols, rs, has_bias, fuse_norm, add_resid, glu, round(eps, 12), version)
|
| 716 |
+
if key in _CACHE:
|
| 717 |
+
return _CACHE[key]
|
| 718 |
+
inputs = ["bp", "bn", "br", "slo", "shi", "x"]
|
| 719 |
+
if has_bias:
|
| 720 |
+
inputs.append("bias")
|
| 721 |
+
if fuse_norm and not glu:
|
| 722 |
+
inputs.append("nw")
|
| 723 |
+
if add_resid:
|
| 724 |
+
inputs.append("resid")
|
| 725 |
+
gen = {1: _gemv_source, 2: _gemv_source_v2, 3: _gemv_source_v3,
|
| 726 |
+
4: _gemv_source_v4, 5: _gemv_source_v5, 6: _gemv_source_v6, 7: _gemv_source_v7}[version]
|
| 727 |
+
src = gen(rows, cols, rs, SG_PER_TG, has_bias, fuse_norm, add_resid, glu, eps)
|
| 728 |
+
name = "tritgemv%d_r%d_c%d_b%d_n%d_a%d_g%d" % (version, rows, cols, has_bias, fuse_norm,
|
| 729 |
+
add_resid, glu)
|
| 730 |
+
k = mx.fast.metal_kernel(name=name, input_names=inputs, output_names=["out"],
|
| 731 |
+
source=src, header=_HEADER)
|
| 732 |
+
_CACHE[key] = (k, inputs, version)
|
| 733 |
+
return _CACHE[key]
|
| 734 |
+
|
| 735 |
+
|
| 736 |
+
def _s8_source_v2(rows, cols, rs, sg_per_tg, fuse_norm, eps, rpsg=RPSG):
|
| 737 |
+
"""v2 int8 GEMV: rpsg rows per simdgroup, float4 x reuse across rows."""
|
| 738 |
+
assert rs % 4 == 0 and cols % 4 == 0 and rows % rpsg == 0
|
| 739 |
+
if fuse_norm:
|
| 740 |
+
xload = "float4 xv = x4[i] * nw4[i] * rms;"
|
| 741 |
+
norm_pre = _norm_pre(cols, eps)
|
| 742 |
+
xpre = """
|
| 743 |
+
const device float4* x4 = (const device float4*)x;
|
| 744 |
+
const device float4* nw4 = (const device float4*)nw;"""
|
| 745 |
+
else:
|
| 746 |
+
xload = "float4 xv = x4[i];"
|
| 747 |
+
norm_pre = ""
|
| 748 |
+
xpre = """
|
| 749 |
+
const device float4* x4 = (const device float4*)x;"""
|
| 750 |
+
return f"""
|
| 751 |
+
uint tid = thread_position_in_threadgroup.x;
|
| 752 |
+
uint lane = tid & 31u;
|
| 753 |
+
uint sg = tid >> 5u;
|
| 754 |
+
uint row0 = (threadgroup_position_in_grid.x * {sg_per_tg}u + sg) * {rpsg}u;
|
| 755 |
+
if (row0 >= {rows}u) return;
|
| 756 |
+
{norm_pre}{xpre}
|
| 757 |
+
float acc[{rpsg}];
|
| 758 |
+
for (uint r = 0u; r < {rpsg}u; ++r) acc[r] = 0.0f;
|
| 759 |
+
const float4 one4 = float4(1.0f);
|
| 760 |
+
for (uint i = lane; i < {cols // 4}u; i += 32u) {{
|
| 761 |
+
{xload}
|
| 762 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 763 |
+
char4 wv = *(const device char4*)((const device char*)W
|
| 764 |
+
+ (ulong)(row0 + r) * {rs}u + i * 4u);
|
| 765 |
+
acc[r] += metal::dot(float4(wv.x, wv.y, wv.z, wv.w) * xv, one4);
|
| 766 |
+
}}
|
| 767 |
+
}}
|
| 768 |
+
for (uint r = 0u; r < {rpsg}u; ++r) {{
|
| 769 |
+
float a = metal::simd_sum(acc[r]);
|
| 770 |
+
if (lane == 0u) out[row0 + r] = scale[row0 + r] * a;
|
| 771 |
+
}}
|
| 772 |
+
"""
|
| 773 |
+
|
| 774 |
+
|
| 775 |
+
def get_s8_gemv(rows, cols, rs, fuse_norm=False, eps=1e-6, version=None):
|
| 776 |
+
version = KERNEL_VERSION if version is None else version
|
| 777 |
+
v = 2 if (version >= 2 and rs % 4 == 0 and cols % 4 == 0 and rows % RPSG == 0) else 1
|
| 778 |
+
key = ("s8", rows, cols, rs, fuse_norm, round(eps, 12), v)
|
| 779 |
+
if key in _CACHE:
|
| 780 |
+
return _CACHE[key]
|
| 781 |
+
inputs = ["W", "scale", "x"]
|
| 782 |
+
if fuse_norm:
|
| 783 |
+
inputs.append("nw")
|
| 784 |
+
src = (_s8_source_v2 if v == 2 else _s8_source)(rows, cols, rs, SG_PER_TG, fuse_norm, eps)
|
| 785 |
+
assert src is not None, "cols must be multiple of 4"
|
| 786 |
+
name = "s8gemv%d_r%d_c%d_n%d" % (v, rows, cols, fuse_norm)
|
| 787 |
+
k = mx.fast.metal_kernel(name=name, input_names=inputs, output_names=["out"],
|
| 788 |
+
source=src, header=_HEADER)
|
| 789 |
+
_CACHE[key] = (k, inputs, v)
|
| 790 |
+
return _CACHE[key]
|
| 791 |
+
|
| 792 |
+
|
| 793 |
+
def _grid_for(rows, rpsg=1):
|
| 794 |
+
nsg = (rows + rpsg - 1) // rpsg # one simdgroup per rpsg rows
|
| 795 |
+
ntg = (nsg + SG_PER_TG - 1) // SG_PER_TG
|
| 796 |
+
return (ntg * SG_PER_TG * 32, 1, 1), (SG_PER_TG * 32, 1, 1)
|
| 797 |
+
|
| 798 |
+
|
| 799 |
+
def trit_gemv(planes, slo, shi, x, bias=None, norm_w=None, resid=None, glu=False,
|
| 800 |
+
out_rows=None, eps=1e-6, stream=None):
|
| 801 |
+
"""planes = (bp, bn, br) mx.uint8 [rows, rs]. x fp32. Returns fp32 [out_rows]."""
|
| 802 |
+
bp, bn, br = planes
|
| 803 |
+
rows, rs = bp.shape
|
| 804 |
+
out_rows = out_rows if out_rows is not None else rows
|
| 805 |
+
cols = x.shape[0] if not glu else x.shape[0] // 2
|
| 806 |
+
k, names, version = get_trit_gemv(rows, cols, rs, bias is not None, norm_w is not None,
|
| 807 |
+
resid is not None, glu, eps)
|
| 808 |
+
vals = {"bp": bp, "bn": bn, "br": br, "slo": slo, "shi": shi, "x": x,
|
| 809 |
+
"bias": bias, "nw": norm_w, "resid": resid}
|
| 810 |
+
grid, tg = _grid_for(out_rows, 8 if version >= 5 else (RPSG if version >= 3 else 1))
|
| 811 |
+
return k(inputs=[vals[n] for n in names], grid=grid, threadgroup=tg,
|
| 812 |
+
output_shapes=[(out_rows,)], output_dtypes=[mx.float32], stream=stream)[0]
|
| 813 |
+
|
| 814 |
+
|
| 815 |
+
def s8_gemv(W, scale, x, norm_w=None, eps=1e-6, stream=None):
|
| 816 |
+
rows, rs = W.shape
|
| 817 |
+
cols = x.shape[0]
|
| 818 |
+
k, names, v = get_s8_gemv(rows, cols, rs, norm_w is not None, eps)
|
| 819 |
+
vals = {"W": W, "scale": scale, "x": x, "nw": norm_w}
|
| 820 |
+
grid, tg = _grid_for(rows, RPSG if v == 2 else 1)
|
| 821 |
+
return k(inputs=[vals[n] for n in names], grid=grid, threadgroup=tg,
|
| 822 |
+
output_shapes=[(rows,)], output_dtypes=[mx.float32], stream=stream)[0]
|
| 823 |
+
|
| 824 |
+
|
| 825 |
+
# ---------------- fused rope + KV cache append ----------------
|
| 826 |
+
|
| 827 |
+
def get_rope_append(n_heads, n_kv, hd, maxseq):
|
| 828 |
+
"""One dispatch: rope q (n_heads*hd) and k (n_kv*hd) from the fused qkv
|
| 829 |
+
vector, write roped k and raw v in-place into the preallocated caches at
|
| 830 |
+
position pos (device scalar), output roped q. NeoX rotate-half, full hd.
|
| 831 |
+
kc/vc passed as inputs and written in place (stream-serial ordering with
|
| 832 |
+
the consumer sdpa; correctness enforced by the e2e gate)."""
|
| 833 |
+
key = ("rope_append", n_heads, n_kv, hd, maxseq)
|
| 834 |
+
if key in _CACHE:
|
| 835 |
+
return _CACHE[key]
|
| 836 |
+
qd, kd = n_heads * hd, n_kv * hd
|
| 837 |
+
h2 = hd // 2
|
| 838 |
+
src = f"""
|
| 839 |
+
uint i = thread_position_in_grid.x; // one thread per q/k element
|
| 840 |
+
uint pos = (uint)posbuf[0];
|
| 841 |
+
device float* kc_w = (device float*)kc;
|
| 842 |
+
device float* vc_w = (device float*)vc;
|
| 843 |
+
if (i == 0u) {{ ((device float*)mask)[pos] = 0.0f; }}
|
| 844 |
+
if (i < {qd}u + {kd}u) {{
|
| 845 |
+
uint hidx = i / {hd}u, d = i % {hd}u;
|
| 846 |
+
float c = ct[pos * {h2}u + (d % {h2}u)];
|
| 847 |
+
float sn = st[pos * {h2}u + (d % {h2}u)];
|
| 848 |
+
float v = qkv[i];
|
| 849 |
+
float vrot = (d < {h2}u) ? -qkv[hidx * {hd}u + d + {h2}u]
|
| 850 |
+
: qkv[hidx * {hd}u + d - {h2}u];
|
| 851 |
+
float r = metal::fma(v, c, vrot * sn);
|
| 852 |
+
if (i < {qd}u) {{
|
| 853 |
+
qout[i] = r;
|
| 854 |
+
}} else {{
|
| 855 |
+
uint kh = (i - {qd}u) / {hd}u;
|
| 856 |
+
kc_w[(ulong)(kh * {maxseq}u + pos) * {hd}u + d] = r;
|
| 857 |
+
}}
|
| 858 |
+
}} else if (i < {qd}u + 2u * {kd}u) {{
|
| 859 |
+
uint j = i - {qd}u - {kd}u;
|
| 860 |
+
uint vh = j / {hd}u, d = j % {hd}u;
|
| 861 |
+
vc_w[(ulong)(vh * {maxseq}u + pos) * {hd}u + d] = qkv[{qd}u + {kd}u + j];
|
| 862 |
+
}}
|
| 863 |
+
"""
|
| 864 |
+
k = mx.fast.metal_kernel(name="rope_append_%d_%d_%d" % (n_heads, n_kv, hd),
|
| 865 |
+
input_names=["qkv", "kc", "vc", "ct", "st", "posbuf", "mask"],
|
| 866 |
+
output_names=["qout"], source=src)
|
| 867 |
+
_CACHE[key] = k
|
| 868 |
+
return k
|
| 869 |
+
|
| 870 |
+
|
| 871 |
+
def rope_append(qkv, kc, vc, ct, st, posbuf, mask, n_heads, n_kv, hd, maxseq, stream=None):
|
| 872 |
+
k = get_rope_append(n_heads, n_kv, hd, maxseq)
|
| 873 |
+
n = (n_heads + 2 * n_kv) * hd
|
| 874 |
+
nthr = ((n + 255) // 256) * 256
|
| 875 |
+
return k(inputs=[qkv, kc, vc, ct, st, posbuf, mask], grid=(nthr, 1, 1),
|
| 876 |
+
threadgroup=(256, 1, 1),
|
| 877 |
+
output_shapes=[(n_heads * hd,)], output_dtypes=[mx.float32], stream=stream)[0]
|
mlx/merges.txt
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
mlx/receipts/bench_8b_wbase_m5_16gb_memcapped.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"mode": "bench",
|
| 3 |
+
"model": "artifacts/wbase_v4.bin",
|
| 4 |
+
"prompt_ids": [
|
| 5 |
+
785,
|
| 6 |
+
1376,
|
| 7 |
+
4522,
|
| 8 |
+
4815,
|
| 9 |
+
71617,
|
| 10 |
+
658,
|
| 11 |
+
29728,
|
| 12 |
+
14155,
|
| 13 |
+
374
|
| 14 |
+
],
|
| 15 |
+
"gen_tokens": 128,
|
| 16 |
+
"runs": [
|
| 17 |
+
{
|
| 18 |
+
"run": 0,
|
| 19 |
+
"tokens": 128,
|
| 20 |
+
"seconds": 5.003734500001883,
|
| 21 |
+
"tok_s": 25.580893630537716
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"run": 1,
|
| 25 |
+
"tokens": 128,
|
| 26 |
+
"seconds": 5.117534165998222,
|
| 27 |
+
"tok_s": 25.012046006542374
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"run": 2,
|
| 31 |
+
"tokens": 128,
|
| 32 |
+
"seconds": 5.271790290993522,
|
| 33 |
+
"tok_s": 24.280176739708118
|
| 34 |
+
}
|
| 35 |
+
],
|
| 36 |
+
"median_tok_s": 25.012046006542374,
|
| 37 |
+
"peak_mem_gib": 3.671223647892475,
|
| 38 |
+
"utc": "2026-07-25T09:38:39Z"
|
| 39 |
+
}
|
mlx/receipts/pathgate_8b_cons3b25.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"n": 32,
|
| 3 |
+
"identical": true,
|
| 4 |
+
"first_divergence": null,
|
| 5 |
+
"fused": [
|
| 6 |
+
429,
|
| 7 |
+
807,
|
| 8 |
+
646,
|
| 9 |
+
4009,
|
| 10 |
+
264,
|
| 11 |
+
729,
|
| 12 |
+
448,
|
| 13 |
+
264,
|
| 14 |
+
2613,
|
| 15 |
+
1372,
|
| 16 |
+
315,
|
| 17 |
+
5029,
|
| 18 |
+
11,
|
| 19 |
+
892,
|
| 20 |
+
3643,
|
| 21 |
+
1105,
|
| 22 |
+
11050,
|
| 23 |
+
369,
|
| 24 |
+
3654,
|
| 25 |
+
9079,
|
| 26 |
+
13,
|
| 27 |
+
4354,
|
| 28 |
+
11,
|
| 29 |
+
279,
|
| 30 |
+
3491,
|
| 31 |
+
374,
|
| 32 |
+
429,
|
| 33 |
+
279,
|
| 34 |
+
1372,
|
| 35 |
+
315,
|
| 36 |
+
5029,
|
| 37 |
+
374
|
| 38 |
+
],
|
| 39 |
+
"mxops": [
|
| 40 |
+
429,
|
| 41 |
+
807,
|
| 42 |
+
646,
|
| 43 |
+
4009,
|
| 44 |
+
264,
|
| 45 |
+
729,
|
| 46 |
+
448,
|
| 47 |
+
264,
|
| 48 |
+
2613,
|
| 49 |
+
1372,
|
| 50 |
+
315,
|
| 51 |
+
5029,
|
| 52 |
+
11,
|
| 53 |
+
892,
|
| 54 |
+
3643,
|
| 55 |
+
1105,
|
| 56 |
+
11050,
|
| 57 |
+
369,
|
| 58 |
+
3654,
|
| 59 |
+
9079,
|
| 60 |
+
13,
|
| 61 |
+
4354,
|
| 62 |
+
11,
|
| 63 |
+
279,
|
| 64 |
+
3491,
|
| 65 |
+
374,
|
| 66 |
+
429,
|
| 67 |
+
279,
|
| 68 |
+
1372,
|
| 69 |
+
315,
|
| 70 |
+
5029,
|
| 71 |
+
374
|
| 72 |
+
],
|
| 73 |
+
"mode": "pathgate",
|
| 74 |
+
"model": "<staging>/Neutrino-8B/neutrino-8b_v4.bin"
|
| 75 |
+
}
|
mlx/receipts/pathgate_8b_d25.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"n": 32,
|
| 3 |
+
"identical": true,
|
| 4 |
+
"first_divergence": null,
|
| 5 |
+
"fused": [
|
| 6 |
+
429,
|
| 7 |
+
807,
|
| 8 |
+
646,
|
| 9 |
+
387,
|
| 10 |
+
1483,
|
| 11 |
+
311,
|
| 12 |
+
4009,
|
| 13 |
+
279,
|
| 14 |
+
5480,
|
| 15 |
+
22711,
|
| 16 |
+
315,
|
| 17 |
+
264,
|
| 18 |
+
33365,
|
| 19 |
+
13,
|
| 20 |
+
576,
|
| 21 |
+
5480,
|
| 22 |
+
22711,
|
| 23 |
+
525,
|
| 24 |
+
279,
|
| 25 |
+
5128,
|
| 26 |
+
429,
|
| 27 |
+
8651,
|
| 28 |
+
2155,
|
| 29 |
+
6846,
|
| 30 |
+
304,
|
| 31 |
+
279,
|
| 32 |
+
1946,
|
| 33 |
+
3550,
|
| 34 |
+
13,
|
| 35 |
+
758,
|
| 36 |
+
264,
|
| 37 |
+
71617
|
| 38 |
+
],
|
| 39 |
+
"mxops": [
|
| 40 |
+
429,
|
| 41 |
+
807,
|
| 42 |
+
646,
|
| 43 |
+
387,
|
| 44 |
+
1483,
|
| 45 |
+
311,
|
| 46 |
+
4009,
|
| 47 |
+
279,
|
| 48 |
+
5480,
|
| 49 |
+
22711,
|
| 50 |
+
315,
|
| 51 |
+
264,
|
| 52 |
+
33365,
|
| 53 |
+
13,
|
| 54 |
+
576,
|
| 55 |
+
5480,
|
| 56 |
+
22711,
|
| 57 |
+
525,
|
| 58 |
+
279,
|
| 59 |
+
5128,
|
| 60 |
+
429,
|
| 61 |
+
8651,
|
| 62 |
+
2155,
|
| 63 |
+
6846,
|
| 64 |
+
304,
|
| 65 |
+
279,
|
| 66 |
+
1946,
|
| 67 |
+
3550,
|
| 68 |
+
13,
|
| 69 |
+
758,
|
| 70 |
+
264,
|
| 71 |
+
71617
|
| 72 |
+
],
|
| 73 |
+
"mode": "pathgate",
|
| 74 |
+
"model": "/tmp/neutrino-8b_v4_d25.bin"
|
| 75 |
+
}
|
mlx/receipts/pathgate_8b_product.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"n": 32,
|
| 3 |
+
"identical": true,
|
| 4 |
+
"first_divergence": null,
|
| 5 |
+
"fused": [
|
| 6 |
+
429,
|
| 7 |
+
807,
|
| 8 |
+
525,
|
| 9 |
+
264,
|
| 10 |
+
943,
|
| 11 |
+
315,
|
| 12 |
+
29728,
|
| 13 |
+
3922,
|
| 14 |
+
429,
|
| 15 |
+
5711,
|
| 16 |
+
2326,
|
| 17 |
+
2155,
|
| 18 |
+
5302,
|
| 19 |
+
369,
|
| 20 |
+
1817,
|
| 21 |
+
48284,
|
| 22 |
+
13,
|
| 23 |
+
1096,
|
| 24 |
+
374,
|
| 25 |
+
2155,
|
| 26 |
+
504,
|
| 27 |
+
8606,
|
| 28 |
+
7868,
|
| 29 |
+
29728,
|
| 30 |
+
14155,
|
| 31 |
+
11,
|
| 32 |
+
892,
|
| 33 |
+
1172,
|
| 34 |
+
990,
|
| 35 |
+
1378,
|
| 36 |
+
5302,
|
| 37 |
+
320
|
| 38 |
+
],
|
| 39 |
+
"mxops": [
|
| 40 |
+
429,
|
| 41 |
+
807,
|
| 42 |
+
525,
|
| 43 |
+
264,
|
| 44 |
+
943,
|
| 45 |
+
315,
|
| 46 |
+
29728,
|
| 47 |
+
3922,
|
| 48 |
+
429,
|
| 49 |
+
5711,
|
| 50 |
+
2326,
|
| 51 |
+
2155,
|
| 52 |
+
5302,
|
| 53 |
+
369,
|
| 54 |
+
1817,
|
| 55 |
+
48284,
|
| 56 |
+
13,
|
| 57 |
+
1096,
|
| 58 |
+
374,
|
| 59 |
+
2155,
|
| 60 |
+
504,
|
| 61 |
+
8606,
|
| 62 |
+
7868,
|
| 63 |
+
29728,
|
| 64 |
+
14155,
|
| 65 |
+
11,
|
| 66 |
+
892,
|
| 67 |
+
1172,
|
| 68 |
+
990,
|
| 69 |
+
1378,
|
| 70 |
+
5302,
|
| 71 |
+
320
|
| 72 |
+
],
|
| 73 |
+
"mode": "pathgate",
|
| 74 |
+
"model": "/tmp/neutrino-8b_v4.bin"
|
| 75 |
+
}
|
mlx/receipts/pathgate_8b_wbase.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"n": 32,
|
| 3 |
+
"identical": true,
|
| 4 |
+
"first_divergence": null,
|
| 5 |
+
"fused": [
|
| 6 |
+
429,
|
| 7 |
+
807,
|
| 8 |
+
646,
|
| 9 |
+
4009,
|
| 10 |
+
894,
|
| 11 |
+
729,
|
| 12 |
+
448,
|
| 13 |
+
264,
|
| 14 |
+
3654,
|
| 15 |
+
1372,
|
| 16 |
+
315,
|
| 17 |
+
5029,
|
| 18 |
+
11,
|
| 19 |
+
323,
|
| 20 |
+
279,
|
| 21 |
+
1372,
|
| 22 |
+
315,
|
| 23 |
+
5029,
|
| 24 |
+
374,
|
| 25 |
+
5435,
|
| 26 |
+
311,
|
| 27 |
+
279,
|
| 28 |
+
1372,
|
| 29 |
+
315,
|
| 30 |
+
7798,
|
| 31 |
+
304,
|
| 32 |
+
279,
|
| 33 |
+
3922,
|
| 34 |
+
13,
|
| 35 |
+
576,
|
| 36 |
+
1372,
|
| 37 |
+
315
|
| 38 |
+
],
|
| 39 |
+
"mxops": [
|
| 40 |
+
429,
|
| 41 |
+
807,
|
| 42 |
+
646,
|
| 43 |
+
4009,
|
| 44 |
+
894,
|
| 45 |
+
729,
|
| 46 |
+
448,
|
| 47 |
+
264,
|
| 48 |
+
3654,
|
| 49 |
+
1372,
|
| 50 |
+
315,
|
| 51 |
+
5029,
|
| 52 |
+
11,
|
| 53 |
+
323,
|
| 54 |
+
279,
|
| 55 |
+
1372,
|
| 56 |
+
315,
|
| 57 |
+
5029,
|
| 58 |
+
374,
|
| 59 |
+
5435,
|
| 60 |
+
311,
|
| 61 |
+
279,
|
| 62 |
+
1372,
|
| 63 |
+
315,
|
| 64 |
+
7798,
|
| 65 |
+
304,
|
| 66 |
+
279,
|
| 67 |
+
3922,
|
| 68 |
+
13,
|
| 69 |
+
576,
|
| 70 |
+
1372,
|
| 71 |
+
315
|
| 72 |
+
],
|
| 73 |
+
"mode": "pathgate",
|
| 74 |
+
"model": "artifacts/wbase_v4.bin"
|
| 75 |
+
}
|
mlx/receipts/refgate_8b_cons3b25.json
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ref": "/tmp/c3gate/cons3b25_ref.npz",
|
| 3 |
+
"meta": {
|
| 4 |
+
"tag": "cons3b25",
|
| 5 |
+
"donor": "Qwen/Qwen3-8B",
|
| 6 |
+
"dtype": "float32",
|
| 7 |
+
"attn": "eager",
|
| 8 |
+
"prompt": "The key idea behind ternary neural networks is",
|
| 9 |
+
"made": "2026-07-27T15:05:30Z"
|
| 10 |
+
},
|
| 11 |
+
"tf": [
|
| 12 |
+
{
|
| 13 |
+
"pos": 0,
|
| 14 |
+
"rel": 2.6224000711845977e-06,
|
| 15 |
+
"cos": 0.999999999999292,
|
| 16 |
+
"argmax_match": true
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"pos": 1,
|
| 20 |
+
"rel": 1.9424399919302914e-06,
|
| 21 |
+
"cos": 0.9999999999994499,
|
| 22 |
+
"argmax_match": true
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"pos": 2,
|
| 26 |
+
"rel": 1.9770419300937743e-06,
|
| 27 |
+
"cos": 0.9999999999993497,
|
| 28 |
+
"argmax_match": true
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"pos": 3,
|
| 32 |
+
"rel": 2.279632526743816e-06,
|
| 33 |
+
"cos": 0.9999999999994535,
|
| 34 |
+
"argmax_match": true
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"pos": 4,
|
| 38 |
+
"rel": 1.9337612922869644e-06,
|
| 39 |
+
"cos": 0.9999999999990218,
|
| 40 |
+
"argmax_match": true
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"pos": 5,
|
| 44 |
+
"rel": 2.324533691836032e-06,
|
| 45 |
+
"cos": 0.9999999999990492,
|
| 46 |
+
"argmax_match": true
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"pos": 6,
|
| 50 |
+
"rel": 2.616610329592598e-06,
|
| 51 |
+
"cos": 0.9999999999988447,
|
| 52 |
+
"argmax_match": true
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"pos": 7,
|
| 56 |
+
"rel": 1.6640498515280545e-06,
|
| 57 |
+
"cos": 0.9999999999993727,
|
| 58 |
+
"argmax_match": true
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"pos": 8,
|
| 62 |
+
"rel": 1.8894559618594419e-06,
|
| 63 |
+
"cos": 0.9999999999993779,
|
| 64 |
+
"argmax_match": true
|
| 65 |
+
}
|
| 66 |
+
],
|
| 67 |
+
"greedy_flips": [],
|
| 68 |
+
"tie_tol": 0.04,
|
| 69 |
+
"max_flips": 8,
|
| 70 |
+
"tf_summary": {
|
| 71 |
+
"rel_max": 2.6224000711845977e-06,
|
| 72 |
+
"rel_mean": 2.1388806274506187e-06,
|
| 73 |
+
"argmax": "9/9",
|
| 74 |
+
"near_tie": 0
|
| 75 |
+
},
|
| 76 |
+
"flips": 0,
|
| 77 |
+
"worst_gap": 0.0,
|
| 78 |
+
"ok": true,
|
| 79 |
+
"mlx_greedy": [
|
| 80 |
+
429,
|
| 81 |
+
807,
|
| 82 |
+
646,
|
| 83 |
+
4009,
|
| 84 |
+
264,
|
| 85 |
+
729,
|
| 86 |
+
448,
|
| 87 |
+
264,
|
| 88 |
+
2613,
|
| 89 |
+
1372,
|
| 90 |
+
315,
|
| 91 |
+
5029,
|
| 92 |
+
11,
|
| 93 |
+
892,
|
| 94 |
+
3643,
|
| 95 |
+
1105,
|
| 96 |
+
11050,
|
| 97 |
+
369,
|
| 98 |
+
3654,
|
| 99 |
+
9079,
|
| 100 |
+
13,
|
| 101 |
+
4354,
|
| 102 |
+
11,
|
| 103 |
+
279
|
| 104 |
+
],
|
| 105 |
+
"ref_greedy": [
|
| 106 |
+
429,
|
| 107 |
+
807,
|
| 108 |
+
646,
|
| 109 |
+
4009,
|
| 110 |
+
264,
|
| 111 |
+
729,
|
| 112 |
+
448,
|
| 113 |
+
264,
|
| 114 |
+
2613,
|
| 115 |
+
1372,
|
| 116 |
+
315,
|
| 117 |
+
5029,
|
| 118 |
+
11,
|
| 119 |
+
892,
|
| 120 |
+
3643,
|
| 121 |
+
1105,
|
| 122 |
+
11050,
|
| 123 |
+
369,
|
| 124 |
+
3654,
|
| 125 |
+
9079,
|
| 126 |
+
13,
|
| 127 |
+
4354,
|
| 128 |
+
11,
|
| 129 |
+
279
|
| 130 |
+
],
|
| 131 |
+
"mode": "refgate",
|
| 132 |
+
"model": "neutrino-8b_v4.bin"
|
| 133 |
+
}
|
mlx/receipts/refgate_8b_d25.json
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ref": "/tmp/d25_ref.npz",
|
| 3 |
+
"meta": {
|
| 4 |
+
"tag": "d25",
|
| 5 |
+
"donor": "Qwen/Qwen3-8B",
|
| 6 |
+
"dtype": "float32",
|
| 7 |
+
"attn": "eager",
|
| 8 |
+
"prompt": "The key idea behind ternary neural networks is",
|
| 9 |
+
"made": "2026-07-26T09:41:51Z"
|
| 10 |
+
},
|
| 11 |
+
"tf": [
|
| 12 |
+
{
|
| 13 |
+
"pos": 0,
|
| 14 |
+
"rel": 3.7357846722178054e-06,
|
| 15 |
+
"cos": 0.9999999999992958,
|
| 16 |
+
"argmax_match": true
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"pos": 1,
|
| 20 |
+
"rel": 1.9618753776610103e-06,
|
| 21 |
+
"cos": 0.9999999999994419,
|
| 22 |
+
"argmax_match": true
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"pos": 2,
|
| 26 |
+
"rel": 1.8195449731555086e-06,
|
| 27 |
+
"cos": 0.999999999999342,
|
| 28 |
+
"argmax_match": true
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"pos": 3,
|
| 32 |
+
"rel": 2.111957507414951e-06,
|
| 33 |
+
"cos": 0.999999999999451,
|
| 34 |
+
"argmax_match": true
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"pos": 4,
|
| 38 |
+
"rel": 1.8725257620352456e-06,
|
| 39 |
+
"cos": 0.9999999999988299,
|
| 40 |
+
"argmax_match": true
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"pos": 5,
|
| 44 |
+
"rel": 1.8537546715748062e-06,
|
| 45 |
+
"cos": 0.9999999999990721,
|
| 46 |
+
"argmax_match": true
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"pos": 6,
|
| 50 |
+
"rel": 1.8210546698822445e-06,
|
| 51 |
+
"cos": 0.9999999999988646,
|
| 52 |
+
"argmax_match": true
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"pos": 7,
|
| 56 |
+
"rel": 1.8684000228879002e-06,
|
| 57 |
+
"cos": 0.9999999999994065,
|
| 58 |
+
"argmax_match": true
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"pos": 8,
|
| 62 |
+
"rel": 1.9054552787661224e-06,
|
| 63 |
+
"cos": 0.9999999999993701,
|
| 64 |
+
"argmax_match": true
|
| 65 |
+
}
|
| 66 |
+
],
|
| 67 |
+
"greedy_flips": [],
|
| 68 |
+
"tie_tol": 0.04,
|
| 69 |
+
"max_flips": 8,
|
| 70 |
+
"tf_summary": {
|
| 71 |
+
"rel_max": 3.7357846722178054e-06,
|
| 72 |
+
"rel_mean": 2.105594770621733e-06,
|
| 73 |
+
"argmax": "9/9",
|
| 74 |
+
"near_tie": 0
|
| 75 |
+
},
|
| 76 |
+
"flips": 0,
|
| 77 |
+
"worst_gap": 0.0,
|
| 78 |
+
"ok": true,
|
| 79 |
+
"mlx_greedy": [
|
| 80 |
+
429,
|
| 81 |
+
807,
|
| 82 |
+
646,
|
| 83 |
+
387,
|
| 84 |
+
1483,
|
| 85 |
+
311,
|
| 86 |
+
4009,
|
| 87 |
+
279,
|
| 88 |
+
5480,
|
| 89 |
+
22711,
|
| 90 |
+
315,
|
| 91 |
+
264,
|
| 92 |
+
33365,
|
| 93 |
+
13,
|
| 94 |
+
576,
|
| 95 |
+
5480,
|
| 96 |
+
22711,
|
| 97 |
+
525,
|
| 98 |
+
279,
|
| 99 |
+
5128,
|
| 100 |
+
429,
|
| 101 |
+
8651,
|
| 102 |
+
2155,
|
| 103 |
+
6846
|
| 104 |
+
],
|
| 105 |
+
"ref_greedy": [
|
| 106 |
+
429,
|
| 107 |
+
807,
|
| 108 |
+
646,
|
| 109 |
+
387,
|
| 110 |
+
1483,
|
| 111 |
+
311,
|
| 112 |
+
4009,
|
| 113 |
+
279,
|
| 114 |
+
5480,
|
| 115 |
+
22711,
|
| 116 |
+
315,
|
| 117 |
+
264,
|
| 118 |
+
33365,
|
| 119 |
+
13,
|
| 120 |
+
576,
|
| 121 |
+
5480,
|
| 122 |
+
22711,
|
| 123 |
+
525,
|
| 124 |
+
279,
|
| 125 |
+
5128,
|
| 126 |
+
429,
|
| 127 |
+
8651,
|
| 128 |
+
2155,
|
| 129 |
+
6846
|
| 130 |
+
],
|
| 131 |
+
"mode": "refgate",
|
| 132 |
+
"model": "/tmp/neutrino-8b_v4_d25.bin"
|
| 133 |
+
}
|
mlx/receipts/refgate_8b_product.json
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ref": "/tmp/s50_ref.npz",
|
| 3 |
+
"meta": {
|
| 4 |
+
"tag": "s50",
|
| 5 |
+
"donor": "Qwen/Qwen3-8B",
|
| 6 |
+
"dtype": "float32",
|
| 7 |
+
"attn": "eager",
|
| 8 |
+
"prompt": "The key idea behind ternary neural networks is",
|
| 9 |
+
"made": "2026-07-26T00:09:27Z"
|
| 10 |
+
},
|
| 11 |
+
"tf": [
|
| 12 |
+
{
|
| 13 |
+
"pos": 0,
|
| 14 |
+
"rel": 2.7188532122724473e-06,
|
| 15 |
+
"cos": 0.9999999999992888,
|
| 16 |
+
"argmax_match": true
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"pos": 1,
|
| 20 |
+
"rel": 2.1881101921352207e-06,
|
| 21 |
+
"cos": 0.9999999999994555,
|
| 22 |
+
"argmax_match": true
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"pos": 2,
|
| 26 |
+
"rel": 2.319768487104987e-06,
|
| 27 |
+
"cos": 0.999999999999356,
|
| 28 |
+
"argmax_match": true
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"pos": 3,
|
| 32 |
+
"rel": 1.8684499152657963e-06,
|
| 33 |
+
"cos": 0.9999999999994532,
|
| 34 |
+
"argmax_match": true
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"pos": 4,
|
| 38 |
+
"rel": 1.8324882362981365e-06,
|
| 39 |
+
"cos": 0.9999999999979642,
|
| 40 |
+
"argmax_match": true
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"pos": 5,
|
| 44 |
+
"rel": 2.1302047849215587e-06,
|
| 45 |
+
"cos": 0.9999999999989747,
|
| 46 |
+
"argmax_match": true
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"pos": 6,
|
| 50 |
+
"rel": 2.7137296248146236e-06,
|
| 51 |
+
"cos": 0.9999999999988849,
|
| 52 |
+
"argmax_match": true
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"pos": 7,
|
| 56 |
+
"rel": 1.7059300688086893e-06,
|
| 57 |
+
"cos": 0.9999999999993904,
|
| 58 |
+
"argmax_match": true
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"pos": 8,
|
| 62 |
+
"rel": 1.9090595614651556e-06,
|
| 63 |
+
"cos": 0.9999999999993883,
|
| 64 |
+
"argmax_match": true
|
| 65 |
+
}
|
| 66 |
+
],
|
| 67 |
+
"greedy_flips": [],
|
| 68 |
+
"tie_tol": 0.04,
|
| 69 |
+
"max_flips": 8,
|
| 70 |
+
"tf_summary": {
|
| 71 |
+
"rel_max": 2.7188532122724473e-06,
|
| 72 |
+
"rel_mean": 2.154066009231846e-06,
|
| 73 |
+
"argmax": "9/9",
|
| 74 |
+
"near_tie": 0
|
| 75 |
+
},
|
| 76 |
+
"flips": 0,
|
| 77 |
+
"worst_gap": 0.0,
|
| 78 |
+
"ok": true,
|
| 79 |
+
"mlx_greedy": [
|
| 80 |
+
429,
|
| 81 |
+
807,
|
| 82 |
+
525,
|
| 83 |
+
264,
|
| 84 |
+
943,
|
| 85 |
+
315,
|
| 86 |
+
29728,
|
| 87 |
+
3922,
|
| 88 |
+
429,
|
| 89 |
+
5711,
|
| 90 |
+
2326,
|
| 91 |
+
2155,
|
| 92 |
+
5302,
|
| 93 |
+
369,
|
| 94 |
+
1817,
|
| 95 |
+
48284,
|
| 96 |
+
13,
|
| 97 |
+
1096,
|
| 98 |
+
374,
|
| 99 |
+
2155,
|
| 100 |
+
504,
|
| 101 |
+
8606,
|
| 102 |
+
7868,
|
| 103 |
+
29728
|
| 104 |
+
],
|
| 105 |
+
"ref_greedy": [
|
| 106 |
+
429,
|
| 107 |
+
807,
|
| 108 |
+
525,
|
| 109 |
+
264,
|
| 110 |
+
943,
|
| 111 |
+
315,
|
| 112 |
+
29728,
|
| 113 |
+
3922,
|
| 114 |
+
429,
|
| 115 |
+
5711,
|
| 116 |
+
2326,
|
| 117 |
+
2155,
|
| 118 |
+
5302,
|
| 119 |
+
369,
|
| 120 |
+
1817,
|
| 121 |
+
48284,
|
| 122 |
+
13,
|
| 123 |
+
1096,
|
| 124 |
+
374,
|
| 125 |
+
2155,
|
| 126 |
+
504,
|
| 127 |
+
8606,
|
| 128 |
+
7868,
|
| 129 |
+
29728
|
| 130 |
+
],
|
| 131 |
+
"mode": "refgate",
|
| 132 |
+
"model": "/tmp/neutrino-8b_v4.bin"
|
| 133 |
+
}
|
mlx/receipts/refgate_8b_wbase.json
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ref": "artifacts/wbase_ref.npz",
|
| 3 |
+
"meta": {
|
| 4 |
+
"tag": "wbase",
|
| 5 |
+
"donor": "Qwen/Qwen3-8B",
|
| 6 |
+
"dtype": "float32",
|
| 7 |
+
"attn": "eager",
|
| 8 |
+
"prompt": "The key idea behind ternary neural networks is",
|
| 9 |
+
"made": "2026-07-25T05:16:03Z"
|
| 10 |
+
},
|
| 11 |
+
"tf": [
|
| 12 |
+
{
|
| 13 |
+
"pos": 0,
|
| 14 |
+
"rel": 2.5888214527867327e-06,
|
| 15 |
+
"cos": 0.9999999999993819,
|
| 16 |
+
"argmax_match": true
|
| 17 |
+
},
|
| 18 |
+
{
|
| 19 |
+
"pos": 1,
|
| 20 |
+
"rel": 2.474946032917843e-06,
|
| 21 |
+
"cos": 0.9999999999994814,
|
| 22 |
+
"argmax_match": true
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"pos": 2,
|
| 26 |
+
"rel": 2.3891665631361155e-06,
|
| 27 |
+
"cos": 0.9999999999993758,
|
| 28 |
+
"argmax_match": true
|
| 29 |
+
},
|
| 30 |
+
{
|
| 31 |
+
"pos": 3,
|
| 32 |
+
"rel": 2.329101721715938e-06,
|
| 33 |
+
"cos": 0.9999999999994569,
|
| 34 |
+
"argmax_match": true
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"pos": 4,
|
| 38 |
+
"rel": 1.6647379290477042e-06,
|
| 39 |
+
"cos": 0.9999999999984548,
|
| 40 |
+
"argmax_match": true
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"pos": 5,
|
| 44 |
+
"rel": 2.36762622893542e-06,
|
| 45 |
+
"cos": 0.9999999999989674,
|
| 46 |
+
"argmax_match": true
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"pos": 6,
|
| 50 |
+
"rel": 1.7604135798303548e-06,
|
| 51 |
+
"cos": 0.9999999999987743,
|
| 52 |
+
"argmax_match": true
|
| 53 |
+
},
|
| 54 |
+
{
|
| 55 |
+
"pos": 7,
|
| 56 |
+
"rel": 1.7215424255002515e-06,
|
| 57 |
+
"cos": 0.9999999999994331,
|
| 58 |
+
"argmax_match": true
|
| 59 |
+
},
|
| 60 |
+
{
|
| 61 |
+
"pos": 8,
|
| 62 |
+
"rel": 1.7872186699375681e-06,
|
| 63 |
+
"cos": 0.9999999999993865,
|
| 64 |
+
"argmax_match": true
|
| 65 |
+
}
|
| 66 |
+
],
|
| 67 |
+
"greedy_flips": [],
|
| 68 |
+
"tie_tol": 0.04,
|
| 69 |
+
"max_flips": 8,
|
| 70 |
+
"tf_summary": {
|
| 71 |
+
"rel_max": 2.5888214527867327e-06,
|
| 72 |
+
"rel_mean": 2.120397178200881e-06,
|
| 73 |
+
"argmax": "9/9",
|
| 74 |
+
"near_tie": 0
|
| 75 |
+
},
|
| 76 |
+
"flips": 0,
|
| 77 |
+
"worst_gap": 0.0,
|
| 78 |
+
"ok": true,
|
| 79 |
+
"mlx_greedy": [
|
| 80 |
+
429,
|
| 81 |
+
807,
|
| 82 |
+
646,
|
| 83 |
+
4009,
|
| 84 |
+
894,
|
| 85 |
+
729,
|
| 86 |
+
448,
|
| 87 |
+
264,
|
| 88 |
+
3654,
|
| 89 |
+
1372,
|
| 90 |
+
315,
|
| 91 |
+
5029,
|
| 92 |
+
11,
|
| 93 |
+
323,
|
| 94 |
+
279,
|
| 95 |
+
1372,
|
| 96 |
+
315,
|
| 97 |
+
5029,
|
| 98 |
+
374,
|
| 99 |
+
5435,
|
| 100 |
+
311,
|
| 101 |
+
279,
|
| 102 |
+
1372,
|
| 103 |
+
315
|
| 104 |
+
],
|
| 105 |
+
"ref_greedy": [
|
| 106 |
+
429,
|
| 107 |
+
807,
|
| 108 |
+
646,
|
| 109 |
+
4009,
|
| 110 |
+
894,
|
| 111 |
+
729,
|
| 112 |
+
448,
|
| 113 |
+
264,
|
| 114 |
+
3654,
|
| 115 |
+
1372,
|
| 116 |
+
315,
|
| 117 |
+
5029,
|
| 118 |
+
11,
|
| 119 |
+
323,
|
| 120 |
+
279,
|
| 121 |
+
1372,
|
| 122 |
+
315,
|
| 123 |
+
5029,
|
| 124 |
+
374,
|
| 125 |
+
5435,
|
| 126 |
+
311,
|
| 127 |
+
279,
|
| 128 |
+
1372,
|
| 129 |
+
315
|
| 130 |
+
],
|
| 131 |
+
"mode": "refgate",
|
| 132 |
+
"model": "artifacts/wbase_v4.bin"
|
| 133 |
+
}
|
mlx/receipts/specbench_8b_0p6b_m5.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bench": "specbench",
|
| 3 |
+
"k": 4,
|
| 4 |
+
"n": 512,
|
| 5 |
+
"runs": 3,
|
| 6 |
+
"types": {
|
| 7 |
+
"facts": {
|
| 8 |
+
"plain_median_tok_s": 22.0,
|
| 9 |
+
"spec_median_tok_s": 25.71,
|
| 10 |
+
"speedup": 1.168,
|
| 11 |
+
"acceptance_median": 0.7442,
|
| 12 |
+
"plain_runs": [
|
| 13 |
+
23.52,
|
| 14 |
+
22.0,
|
| 15 |
+
21.08
|
| 16 |
+
],
|
| 17 |
+
"spec_runs": [
|
| 18 |
+
26.3,
|
| 19 |
+
25.71,
|
| 20 |
+
23.76
|
| 21 |
+
]
|
| 22 |
+
},
|
| 23 |
+
"prose": {
|
| 24 |
+
"plain_median_tok_s": 17.86,
|
| 25 |
+
"spec_median_tok_s": 12.15,
|
| 26 |
+
"speedup": 0.68,
|
| 27 |
+
"acceptance_median": 0.2994,
|
| 28 |
+
"plain_runs": [
|
| 29 |
+
19.12,
|
| 30 |
+
17.86,
|
| 31 |
+
17.84
|
| 32 |
+
],
|
| 33 |
+
"spec_runs": [
|
| 34 |
+
12.48,
|
| 35 |
+
12.1,
|
| 36 |
+
12.15
|
| 37 |
+
]
|
| 38 |
+
},
|
| 39 |
+
"chat": {
|
| 40 |
+
"plain_median_tok_s": 17.53,
|
| 41 |
+
"spec_median_tok_s": 14.68,
|
| 42 |
+
"speedup": 0.837,
|
| 43 |
+
"acceptance_median": 0.4395,
|
| 44 |
+
"plain_runs": [
|
| 45 |
+
17.72,
|
| 46 |
+
17.53,
|
| 47 |
+
17.35
|
| 48 |
+
],
|
| 49 |
+
"spec_runs": [
|
| 50 |
+
15.01,
|
| 51 |
+
14.67,
|
| 52 |
+
14.68
|
| 53 |
+
]
|
| 54 |
+
}
|
| 55 |
+
},
|
| 56 |
+
"peak_mem_gib": 4.3
|
| 57 |
+
}
|
mlx/receipts/specgate_8b_0p6b.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"gate": "specgate",
|
| 3 |
+
"k": 4,
|
| 4 |
+
"lens": [
|
| 5 |
+
32,
|
| 6 |
+
256
|
| 7 |
+
],
|
| 8 |
+
"prompts": {
|
| 9 |
+
"facts": {
|
| 10 |
+
"32": {
|
| 11 |
+
"identical": true,
|
| 12 |
+
"first_divergence": -1,
|
| 13 |
+
"acceptance": 0.3214
|
| 14 |
+
},
|
| 15 |
+
"256": {
|
| 16 |
+
"identical": true,
|
| 17 |
+
"first_divergence": -1,
|
| 18 |
+
"acceptance": 0.6714
|
| 19 |
+
}
|
| 20 |
+
},
|
| 21 |
+
"prose": {
|
| 22 |
+
"32": {
|
| 23 |
+
"identical": true,
|
| 24 |
+
"first_divergence": -1,
|
| 25 |
+
"acceptance": 0.4038
|
| 26 |
+
},
|
| 27 |
+
"256": {
|
| 28 |
+
"identical": true,
|
| 29 |
+
"first_divergence": -1,
|
| 30 |
+
"acceptance": 0.2896
|
| 31 |
+
}
|
| 32 |
+
},
|
| 33 |
+
"chat": {
|
| 34 |
+
"32": {
|
| 35 |
+
"identical": true,
|
| 36 |
+
"first_divergence": -1,
|
| 37 |
+
"acceptance": 0.55
|
| 38 |
+
},
|
| 39 |
+
"256": {
|
| 40 |
+
"identical": true,
|
| 41 |
+
"first_divergence": -1,
|
| 42 |
+
"acceptance": 0.4289
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
},
|
| 46 |
+
"verdict": "PASS"
|
| 47 |
+
}
|