Taykhoom commited on
Commit
113fb34
·
0 Parent(s):

Initial Evo2-20B-1M Hugging Face port

Browse files
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ library_name: transformers
3
+ tags:
4
+ - DNA
5
+ - language-model
6
+ - StripedHyena2
7
+ - Evo2
8
+ license: apache-2.0
9
+ ---
10
+
11
+ # Evo2-20B-1M
12
+
13
+ A clean, minimal HuggingFace port of [Evo 2 20B](https://huggingface.co/arcinstitute/evo2_20b), with 40B-class quality at half the FLOPs. Provides native support for layer-by-layer hidden state extraction, attention-weight extraction, and a runtime-switchable attention backend.
14
+
15
+ > **NVIDIA Transformer Engine required.** This variant uses FP8 input
16
+ > projections (`use_fp8_input_projections=True`) which require
17
+ > [TransformerEngine](https://github.com/NVIDIA/TransformerEngine) and a
18
+ > Hopper-class GPU (H100 / H200). Install with:
19
+ >
20
+ > ```bash
21
+ > pip install transformer-engine[pytorch]>=2.3.0
22
+ > ```
23
+
24
+
25
+
26
+ ## Why this port?
27
+
28
+ [arcinstitute/evo2_20b](https://huggingface.co/arcinstitute/evo2_20b) ships a `.pt` checkpoint that requires the [evo2](https://github.com/ArcInstitute/evo2) and [vortex](https://github.com/Zymrael/vortex) Python packages just to instantiate the model. Even with both installed, common pain points remain:
29
+
30
+ 1. **Not a HuggingFace model.** No `from_pretrained`, no `AutoModel`, no `AutoModelForCausalLM` - the original ships a thin Python wrapper around a custom `nn.Module`.
31
+ 2. **No public attention-weight API.** The default reference path uses Flash Attention and discards the `(B, H, T, T)` attention matrix.
32
+ 3. **`evo2` + `vortex` packages mandatory** even for inference.
33
+
34
+ This repo fixes all three. Its converted **bf16 SDPA path** is bit-exact with a matching bf16 SDPA vortex reference configuration (`max_abs_diff = 0.000e+00` at every layer; see [Parity Verification](#parity-verification)). Loads with `from_pretrained` and `trust_remote_code=True` - no `evo2` / `vortex` install needed.
35
+
36
+ ## Architecture
37
+
38
+ | Parameter | Value |
39
+ |---|---|
40
+ | Total parameters | ~18.5B |
41
+ | Architecture | StripedHyena 2 (interleaved Hyena cascade + MHA blocks) |
42
+ | Layers | 24 |
43
+ | Attention heads | 64 |
44
+ | Embedding dimension | 8192 |
45
+ | Inner MLP size | 22 528 |
46
+ | Vocabulary size | 512 (UTF-8 byte-level) |
47
+ | Attention block indices | 3, 10, 17 (3 blocks total) |
48
+ | Hyena block indices | all others (21 blocks: hcs / hcm / hcl pattern) |
49
+ | Positional encoding | RoPE (base = 1 000 000), linearly scaled by 128x |
50
+ | Max sequence length | 1 048 576 |
51
+ | Training dtype | bfloat16 (Hyena modal-form `log_poles` / `residues` and rotary `inv_freq` kept in fp32) |
52
+ | FP8 input projections | yes (TransformerEngine required) |
53
+ | Weight format | `model.safetensors` (18.5B params, 8 files) |
54
+
55
+ ## Pretraining
56
+
57
+ - **Objective:** causal byte-level next-token prediction.
58
+ - **Data:** [OpenGenome2](https://huggingface.co/datasets/arcinstitute/opengenome2), 8.8 trillion tokens spanning all domains of life.
59
+ - **Source checkpoint:** `arcinstitute/evo2_20b` ([`evo2_20b.pt`](https://huggingface.co/arcinstitute/evo2_20b/blob/main/evo2_20b.pt)).
60
+
61
+ ## Parity Verification
62
+
63
+ Hidden-state representations verified **bit-exact** (`max_abs_diff = 0.000e+00`) to the vortex reference at every block output after reconstructing the published checkpoint's bf16-rounded `inv_freq` from `base` and `dim` in fp32 on both sides. Parity uses `attn_implementation="sdpa"` in bf16, with FP8 input projections disabled on both sides where applicable. Logits from `Evo2ForCausalLM` were also bit-exact (top-1 agreement: 128/128 positions on a 128-byte ACGT input). This verifies conversion fidelity under that controlled configuration; it does not claim bit-exact output against the upstream default Flash Attention / FP8 execution path. Verified on H100 with PyTorch 2.7 / CUDA 12.
64
+
65
+ Two non-obvious correctness fixes were required versus a naive port (see Implementation Notes for details):
66
+
67
+ 1. **`inv_freq` recomputation.** A checkpoint-loaded rotary `inv_freq` can contain bf16-rounded values even when its destination buffer is fp32. The remote model code treats `inv_freq` as non-persistent runtime state and reconstructs it in fp32 from `base` and `dim` during construction, after checkpoint loading, and whenever the RoPE cache is rebuilt.
68
+ 2. **SDPA backend used for parity.** Vortex's reference `SelfAttention` (`use_flash_attn=False`) calls `F.scaled_dot_product_attention`, not a textbook softmax loop. Parity is measured with `attn_implementation="sdpa"` on our side. Using `"eager"` (textbook einsum + softmax) is mathematically equivalent but not bit-exact in bf16; using `"flash_attention_2"` (the recommended runtime backend) is also not bit-exact but agrees within bf16 noise.
69
+
70
+ ## Related Models
71
+
72
+ See the full Evo 2 collection on the [Arc Institute HF org](https://huggingface.co/arcinstitute) for the original weights, or the [`Taykhoom/Evo2-*` collection](https://huggingface.co/collections/Taykhoom/evo2-6a24b06c05955a295025a006) for our minimal HF ports.
73
+
74
+ | Model | Size | Context | Notes |
75
+ |---|---|---|---|
76
+ | [Taykhoom/Evo2-1B-8K](https://huggingface.co/Taykhoom/Evo2-1B-8K) | 1B | 8 192 | |
77
+ | [Taykhoom/Evo2-7B-8K](https://huggingface.co/Taykhoom/Evo2-7B-8K) | 7B | 8 192 | |
78
+ | [Taykhoom/Evo2-7B-262K](https://huggingface.co/Taykhoom/Evo2-7B-262K) | 7B | 262 144 | |
79
+ | [Taykhoom/Evo2-7B-1M](https://huggingface.co/Taykhoom/Evo2-7B-1M) | 7B | 1 048 576 | |
80
+ | **[Taykhoom/Evo2-20B-1M](https://huggingface.co/Taykhoom/Evo2-20B-1M)** | 20B | 1 048 576 | <- this model |
81
+ | [Taykhoom/Evo2-40B-8K](https://huggingface.co/Taykhoom/Evo2-40B-8K) | 40B | 8 192 | |
82
+ | [Taykhoom/Evo2-40B-1M](https://huggingface.co/Taykhoom/Evo2-40B-1M) | 40B | 1 048 576 | |
83
+
84
+ ## Usage
85
+
86
+ > **Note on dtype.** This port supports bfloat16 inference, with the Hyena `log_poles` / `residues` (modal-form filter parameters) and rotary `inv_freq` kept in fp32 for numerical stability. Checkpoint loading normalizes all other parameters to bfloat16 even if another `dtype` is requested. The bundled remote model code restores the fp32 invariants after loading; no manual repair call is needed.
87
+
88
+ > **Note on padding.** Use right padding, as shown below. The stateless path zeros padded activations but does not build an additive attention-key mask, so left padding changes logits at otherwise valid positions.
89
+
90
+ > **Note on attention backend.** By HuggingFace convention this model defaults to `attn_implementation="sdpa"` (`F.scaled_dot_product_attention`) since SDPA needs only `torch` and runs on any GPU. The original Arc Institute Evo 2 inference path uses **flash_attention_2**, which is faster on long sequences but requires a separate `flash-attn` install. All usage examples below opt in to `flash_attention_2` explicitly because most real users will want it. Drop the kwarg (or pass `"sdpa"` / `"eager"`) if you don't have `flash-attn` installed.
91
+
92
+ ### Embedding generation (no LM head)
93
+
94
+ ```python
95
+ import torch
96
+ from transformers import AutoTokenizer, AutoModel
97
+
98
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/Evo2-20B-1M", trust_remote_code=True)
99
+ model = AutoModel.from_pretrained(
100
+ "Taykhoom/Evo2-20B-1M",
101
+ trust_remote_code=True,
102
+ attn_implementation="flash_attention_2", # or "sdpa" (default) or "eager"
103
+ ).cuda().eval()
104
+
105
+ seqs = ["ACGTACGTACGT", "GGGTTTAAACCC"]
106
+ inputs = tokenizer(seqs, return_tensors="pt", padding=True).to(model.get_input_embeddings().weight.device)
107
+
108
+ with torch.no_grad():
109
+ out = model(**inputs, output_hidden_states=True)
110
+
111
+ last_hidden = out.last_hidden_state # (B, T, 8192)
112
+ all_layers = out.hidden_states # tuple of (B, T, 8192), len = 26
113
+ middle_layer = all_layers[12] # input to block 12 (= output of block 11)
114
+ ```
115
+
116
+ ### Middle-block pre-norm embedding
117
+
118
+ The Evo 2 paper reports that intermediate representations can outperform final-layer representations, but it does not prescribe this exact layer. This port uses the pre-norm output of the middle block as a simple convention; the upstream README instead demonstrates `blocks.28.mlp.l3` for the 7B model. For this variant, the middle-block value is `blocks[12].pre_norm(hidden_states[12])`:
119
+
120
+ ```python
121
+ import torch
122
+ from transformers import AutoTokenizer, AutoModel
123
+
124
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/Evo2-20B-1M", trust_remote_code=True)
125
+ model = AutoModel.from_pretrained(
126
+ "Taykhoom/Evo2-20B-1M",
127
+ trust_remote_code=True,
128
+ attn_implementation="flash_attention_2",
129
+ ).cuda().eval()
130
+
131
+ inputs = tokenizer(["ACGTACGTACGT"], return_tensors="pt").to(model.get_input_embeddings().weight.device)
132
+ with torch.no_grad():
133
+ out = model(**inputs, output_hidden_states=True)
134
+ pre_norm_middle = model.backbone.blocks[12].pre_norm(
135
+ out.hidden_states[12]
136
+ ) # (B, T, 8192)
137
+ ```
138
+
139
+ HF has no built-in API for sub-block intermediates like pre-norm outputs (only block outputs via `output_hidden_states`). The pattern above applies the block's `pre_norm` submodule directly to the corresponding `hidden_states` entry; this gives a bit-identical result to registering a forward hook on `backbone.blocks[i].pre_norm` and is simpler than using PyTorch hooks. Note that it does require running the full forward pass and then re-applying `pre_norm`, so a forward hook is more efficient if you only need this single intermediate.
140
+
141
+ ### LM logits
142
+
143
+ ```python
144
+ import torch
145
+ from transformers import AutoTokenizer, AutoModelForCausalLM
146
+
147
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/Evo2-20B-1M", trust_remote_code=True)
148
+ model = AutoModelForCausalLM.from_pretrained(
149
+ "Taykhoom/Evo2-20B-1M", trust_remote_code=True,
150
+ attn_implementation="flash_attention_2",
151
+ ).cuda().eval()
152
+
153
+ inputs = tokenizer(["ACGT"], return_tensors="pt").to(model.get_input_embeddings().weight.device)
154
+ with torch.no_grad():
155
+ logits = model(**inputs).logits # (1, T, 512)
156
+ ```
157
+
158
+ ### Generation
159
+
160
+ ```python
161
+ import torch
162
+ from transformers import AutoTokenizer, AutoModelForCausalLM
163
+
164
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/Evo2-20B-1M", trust_remote_code=True)
165
+ model = AutoModelForCausalLM.from_pretrained(
166
+ "Taykhoom/Evo2-20B-1M", trust_remote_code=True,
167
+ attn_implementation="flash_attention_2",
168
+ ).cuda().eval()
169
+
170
+ # The current generation hook drops a per-call use_cache=False kwarg.
171
+ model.config.use_cache = False
172
+
173
+ inputs = tokenizer(["ACGT"], return_tensors="pt").to(model.get_input_embeddings().weight.device)
174
+ out = model.generate(**inputs, max_new_tokens=128, do_sample=True, top_k=4, temperature=1.0)
175
+ print(tokenizer.decode(out[0]))
176
+ ```
177
+
178
+ `generation_config.json` ships with `eos_token_id = 0` (the EOD byte) and `pad_token_id = 1` so `model.generate()` stops naturally at the trained end-of-document token.
179
+
180
+ The current cache path ignores `attention_mask`, so cached batched generation requires equal-length prompts; disable caching for right-padded batches with unequal prompt lengths. The cache also preallocates each attention block to `max_seqlen`: this variant requires about 96.0 GiB of batch-one attention KV storage at its configured context before model weights. The 1M examples therefore disable caching through `model.config.use_cache = False`. A per-call `generate(..., use_cache=False)` is currently dropped by `prepare_inputs_for_generation` and is not sufficient. A cache-balanced two-H100 map made cached decoding feasible, but it diverged from full recomputation after one generated token in the 2026-08-25 audit.
181
+
182
+ ### Attention weights
183
+
184
+ ```python
185
+ import torch
186
+ from transformers import AutoTokenizer, AutoModel
187
+
188
+ tokenizer = AutoTokenizer.from_pretrained("Taykhoom/Evo2-20B-1M", trust_remote_code=True)
189
+ model = AutoModel.from_pretrained(
190
+ "Taykhoom/Evo2-20B-1M",
191
+ trust_remote_code=True,
192
+ attn_implementation="eager", # required for output_attentions to populate
193
+ ).cuda().eval()
194
+
195
+ inputs = tokenizer(["ACGTACGT"], return_tensors="pt").to(model.get_input_embeddings().weight.device)
196
+ with torch.no_grad():
197
+ out = model(**inputs, output_attentions=True)
198
+
199
+ # out.attentions is a tuple of length 24. Entries at indices not in
200
+ # [3, 10, 17] are None (Hyena blocks have no attention matrix).
201
+ # The 3 attention block(s) at those indices return a (B, num_heads, T, T) tensor.
202
+ attn_block_3 = out.attentions[3]
203
+ ```
204
+
205
+ ### Multi-GPU loading (optional)
206
+
207
+ For sharding across multiple GPUs (required for 40B, optional for smaller
208
+ variants), install [accelerate](https://github.com/huggingface/accelerate)
209
+ and pass `device_map="auto"`:
210
+
211
+ ```python
212
+ from transformers import AutoModelForCausalLM
213
+ # pip install accelerate
214
+ model = AutoModelForCausalLM.from_pretrained(
215
+ "Taykhoom/Evo2-20B-1M", trust_remote_code=True,
216
+ device_map="auto", # accelerate will shard across all visible GPUs
217
+ )
218
+ ```
219
+
220
+ ### Fine-tuning
221
+
222
+ This HuggingFace port has **not** been tested for fine-tuning - it's verified only for inference parity. For fine-tuning, follow the original Arc Institute guidance and use either [Savanna](https://github.com/Zymrael/savanna) (the framework Evo 2 was pretrained in) or [NVIDIA BioNeMo](https://github.com/NVIDIA/bionemo-framework), which provides an official [Evo 2 fine-tuning recipe](https://github.com/NVIDIA-BioNeMo/bionemo-recipes/tree/main/recipes/evo2_megatron#fine-tuning-from-an-existing-checkpoint).
223
+
224
+ ## Implementation Notes
225
+
226
+ - **HF base-model contract.** `base_model_prefix = "backbone"` exposes the raw `StripedHyena2` module through `.base_model`. Its public `forward` accepts standard HF arguments (`input_ids`, `attention_mask`, `past_key_values`, `use_cache`, `output_hidden_states`, `output_attentions`, `return_dict`) and returns `BaseModelOutputWithPast`; `AutoModel` delegates to this same path.
227
+ - **`inv_freq` kept exact in fp32 (critical for parity).** The geometric series `inv_freq[i] = 1 / base^(2i/dim)` loses ~7 bits of mantissa when rounded to bf16, which shifts the cos/sin tables and adds Q/K error at every attention layer. `inv_freq` is non-persistent runtime state: the remote code reconstructs it from `base` and `dim` in fp32 after loading and before rebuilding the RoPE cache. Rotary multiplication is also performed in fp32 before casting back to the activation dtype, matching Flash Attention 2.
228
+ - **`log_poles` / `residues` kept in fp32 (critical for stability).** The Hyena cascade long (hcl) blocks parameterize an IIR filter via `log_poles` and `residues`; bf16 quantisation makes the recurrence numerically unstable. Both are stored as fp32 in the safetensors, covered by `_keep_in_fp32_modules`, and explicitly restored to fp32 after `from_pretrained` completes.
229
+ - **`attn_implementation` switching (`attention.py`).** Three backends, selected via the standard HF `attn_implementation` kwarg to `from_pretrained` (default chosen by HF auto-detection - typically `"sdpa"`):
230
+ - `"sdpa"`: calls `F.scaled_dot_product_attention`. Bit-exact with vortex's reference path (when vortex uses `use_flash_attn=False`).
231
+ - `"flash_attention_2"`: calls `flash_attn.flash_attn_qkvpacked_func`, the same attention backend used by the default Arc Institute inference path; faster on long sequences; requires `flash-attn` installed.
232
+ - `"eager"`: textbook einsum + softmax(QK^T) + einsum. Slowest, used internally when `output_attentions=True` so the attention matrix is materialized.
233
+ - **Block dispatch (`hyena.py`).** StripedHyena 2 has 4 block types, dispatched by `layer_idx` membership in four config lists: `attn_layer_idxs` (MHA + RoPE), `hcl_layer_idxs` (modal-form IIR via FFT), `hcm_layer_idxs` (medium FIR cascade, inner length 128), `hcs_layer_idxs` (short FIR cascade, inner length 7). The disjoint union must equal `range(num_layers)`.
234
+ - **`TELinear` with pure-PyTorch fallback (`layers.py`).** Hyena cascade blocks use a TransformerEngine-backed input projection (3x hidden_size output) that supports FP8 quantisation. When TE is not installed, a `TELinear` fallback class with the same state_dict layout (`weight`, `bias`) is used - checkpoints are cross-loadable.
235
+ - **Custom cache (`cache.py`).** `Evo2Cache` wraps four block-type-specific dataclasses: `InferenceParams` for MHA KV cache, `HyenaCascadeIIRInferenceParams` for hcl, and two `HyenaCascadeFIRInferenceParams` for hcm / hcs. Passed through `model.generate()` as `past_key_values` (we set `_supports_cache_class = False` so HF treats it as an opaque dict rather than wrapping it in a `DynamicCache`).
236
+ - **Tokenizer (`tokenization_evo2.py`).** Byte-level UTF-8, vocab_size = 512. Pad token = byte `\x01`. EOS = byte `\x00` (set as `eos_token_id` in `generation_config.json`). Tokenizer does not add EOS at encoding time - matches the original Evo 2 inference pipeline.
237
+ - **Dependencies.** `torch`, `transformers`, `numpy`, `safetensors`, `huggingface_hub`. `transformer-engine[pytorch]` is required for this variant's FP8 input projections. `accelerate` is optional but recommended if you want to load with `device_map="auto"` for multi-GPU sharding. `flash_attn` is optional (only needed if you pass `attn_implementation="flash_attention_2"`).
238
+
239
+ ## Citation
240
+
241
+ ```bibtex
242
+ @article{brixi2026_evo2,
243
+ title = {Genome modelling and design across all domains of life with {Evo} 2},
244
+ author = {Brixi, Garyk and Durrant, Matthew G. and Ku, Jerome and Naghipourfar, Mohsen and Poli, Michael and Sun, Gwanggyu and Brockman, Greg and Chang, Daniel and Fanton, Alison and Gonzalez, Gabriel A. and King, Samuel H. and Li, David B. and Merchant, Aditi T. and Nguyen, Eric and Ricci-Tam, Chiara and Romero, David W. and Schmok, Jonathan C. and Taghibakhshi, Ali and Vorontsov, Anton and Yang, Brandon and Deng, Myra and Gorton, Liv and Nguyen, Nam and Wang, Nicholas K. and Pearce, Michael T. and Simon, Elana and Adams, Etowah and Amador, Zachary J. and Ashley, Euan A. and Baccus, Stephen A. and Dai, Haoyu and Dillmann, Steven and Ermon, Stefano and Guo, Daniel and Herschl, Michael H. and Ilango, Rajesh and Janik, Ken and Lu, Amy X. and Mehta, Reshma and Mofrad, Mohammad R. K. and Ng, Madelena Y. and Pannu, Jaspreet and {R{\'e}}, Christopher and St. John, John and Sullivan, Jeremy and Tey, Joseph and Viggiano, Ben and Zhu, Kevin and Zynda, Greg and Balsam, Daniel and Collison, Patrick and Costa, Anthony B. and Hernandez-Boussard, Tina and Ho, Eric and Liu, Ming-Yu and McGrath, Thomas and Powell, Kimberly and Pinglay, Sudarshan and Burke, Dave P. and Goodarzi, Hani and Hsu, Patrick D. and Hie, Brian L.},
245
+ journal = {Nature},
246
+ volume = {652},
247
+ number = {8112},
248
+ pages = {1349--1361},
249
+ year = {2026},
250
+ doi = {10.1038/s41586-026-10176-5}
251
+ }
252
+ ```
253
+
254
+ ## Credits
255
+
256
+ Original Evo 2 model and code by Brixi et al. ([arcinstitute/evo2](https://github.com/arcinstitute/evo2), [Zymrael/vortex](https://github.com/Zymrael/vortex)). Source checkpoint: [arcinstitute/evo2_20b](https://huggingface.co/arcinstitute/evo2_20b).
257
+
258
+ The HF conversion code was authored primarily by [Claude Code](https://claude.ai/code) and reviewed manually by Taykhoom Dalal.
259
+
260
+ ## License
261
+
262
+ Apache 2.0, following the original Evo 2 release.
attention.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Multi-head attention for Evo2.
2
+
3
+ Causal self-attention with RoPE. Three backends dispatchable via the standard
4
+ HF attn_implementation kwarg:
5
+
6
+ * "eager" -- pure F.softmax(QK^T)V; returns weights on demand
7
+ * "sdpa" -- F.scaled_dot_product_attention; no weights
8
+ * "flash_attention_2" -- flash_attn.flash_attn_qkvpacked_func; no weights
9
+
10
+ Constructor signature is a strict subset of vortex's MHA so the
11
+ AttentionBlock instantiation site only changes the attn_implementation kwarg.
12
+ KV cache (used by Evo2 generation) goes through an SDPA path that handles
13
+ arbitrary sequence lengths.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import math
19
+
20
+ import torch
21
+ import torch.nn as nn
22
+ import torch.nn.functional as F
23
+
24
+ from .rotary import RotaryEmbedding
25
+
26
+
27
+ def _flash_attn_required():
28
+ try:
29
+ from flash_attn import flash_attn_qkvpacked_func # noqa: F401
30
+ except ImportError as exc:
31
+ raise ImportError(
32
+ "attn_implementation='flash_attention_2' requires the flash-attn "
33
+ "package. Install with `pip install flash-attn --no-build-isolation`."
34
+ ) from exc
35
+
36
+
37
+ def _update_kv_cache(kv: torch.Tensor, inference_params, layer_idx: int) -> torch.Tensor:
38
+ num_heads, head_dim = kv.shape[-2:]
39
+ if layer_idx not in inference_params.key_value_memory_dict:
40
+ kv_cache = torch.empty(
41
+ inference_params.max_batch_size,
42
+ inference_params.max_seqlen,
43
+ 2,
44
+ num_heads,
45
+ head_dim,
46
+ dtype=kv.dtype,
47
+ device=kv.device,
48
+ )
49
+ inference_params.key_value_memory_dict[layer_idx] = kv_cache
50
+ else:
51
+ kv_cache = inference_params.key_value_memory_dict[layer_idx]
52
+ batch_start = inference_params.batch_size_offset
53
+ batch_end = batch_start + kv.shape[0]
54
+ sequence_start = inference_params.seqlen_offset
55
+ sequence_end = sequence_start + kv.shape[1]
56
+ kv_cache[batch_start:batch_end, sequence_start:sequence_end, ...] = kv
57
+ return kv_cache[batch_start:batch_end, :sequence_end, ...]
58
+
59
+
60
+ class MHA(nn.Module):
61
+ """Multi-head self-attention with backend-dispatch."""
62
+
63
+ def __init__(
64
+ self,
65
+ embed_dim: int,
66
+ num_heads: int,
67
+ num_heads_kv: int | None = None,
68
+ cross_attn: bool = False,
69
+ qkv_proj_bias: bool = True,
70
+ out_proj_bias: bool = True,
71
+ dropout: float = 0.0,
72
+ softmax_scale: float | None = None,
73
+ causal: bool = False,
74
+ layer_idx: int | None = None,
75
+ rotary_emb_dim: int = 0,
76
+ rotary_emb_base: float = 10000.0,
77
+ rotary_emb_scale_base: float | None = None,
78
+ rotary_emb_interleaved: bool = False,
79
+ use_flash_attn: bool = False, # legacy kwarg kept for ctor compat
80
+ attn_implementation: str = "eager",
81
+ device=None,
82
+ dtype=None,
83
+ ) -> None:
84
+ super().__init__()
85
+ if cross_attn:
86
+ raise NotImplementedError("Cross-attention is not supported.")
87
+
88
+ factory_kwargs = {"device": device, "dtype": dtype}
89
+ self.embed_dim = embed_dim
90
+ self.num_heads = num_heads
91
+ self.num_heads_kv = num_heads_kv if num_heads_kv is not None else num_heads
92
+ if self.embed_dim % num_heads != 0:
93
+ raise ValueError("embed_dim must be divisible by num_heads")
94
+ if self.num_heads % self.num_heads_kv != 0:
95
+ raise ValueError("num_heads must be divisible by num_heads_kv")
96
+ self.head_dim = self.embed_dim // num_heads
97
+ self.causal = causal
98
+ self.softmax_scale = softmax_scale
99
+ self.layer_idx = layer_idx
100
+ self.rotary_emb_dim = rotary_emb_dim
101
+ self.attn_implementation = attn_implementation
102
+ self.dropout_p = dropout
103
+ self.cross_attn = cross_attn
104
+
105
+ if self.rotary_emb_dim > 0:
106
+ self.rotary_emb = RotaryEmbedding(
107
+ self.rotary_emb_dim,
108
+ base=rotary_emb_base,
109
+ interleaved=rotary_emb_interleaved,
110
+ scale_base=rotary_emb_scale_base,
111
+ device=device,
112
+ )
113
+
114
+ qkv_dim = self.head_dim * (self.num_heads + 2 * self.num_heads_kv)
115
+ self.Wqkv = nn.Linear(embed_dim, qkv_dim, bias=qkv_proj_bias, **factory_kwargs)
116
+ self.out_proj = nn.Linear(embed_dim, embed_dim, bias=out_proj_bias, **factory_kwargs)
117
+
118
+ def allocate_inference_cache(self, batch_size, max_seqlen, dtype=None):
119
+ dtype = self.out_proj.weight.dtype if dtype is None else dtype
120
+ device = self.out_proj.weight.device
121
+ return torch.empty(
122
+ batch_size, max_seqlen, 2, self.num_heads_kv, self.head_dim,
123
+ dtype=dtype, device=device,
124
+ )
125
+
126
+ def _project_qkv(self, x: torch.Tensor) -> torch.Tensor:
127
+ qkv = self.Wqkv(x)
128
+ if self.num_heads_kv == self.num_heads:
129
+ return qkv.view(*qkv.shape[:-1], 3, self.num_heads, self.head_dim)
130
+ q = qkv[..., : self.num_heads * self.head_dim]
131
+ kv = qkv[..., self.num_heads * self.head_dim:]
132
+ q = q.view(*q.shape[:-1], self.num_heads, self.head_dim)
133
+ kv = kv.view(*kv.shape[:-1], 2, self.num_heads_kv, self.head_dim)
134
+ return q, kv
135
+
136
+ def _forward_eager(
137
+ self,
138
+ qkv: torch.Tensor,
139
+ output_attentions: bool,
140
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
141
+ """Bit-identical port of flash_attn.modules.mha.SelfAttention.forward.
142
+
143
+ Key choices that must match vortex (which delegates to this class
144
+ when use_flash_attn=False):
145
+
146
+ * einsum (not matmul) for QK^T and PV: bf16 matmul vs bf16 einsum
147
+ use different accumulator orders, ~3e-2 per attention layer.
148
+ * -10000.0 causal mask (not -inf): vortex uses a finite mask value.
149
+ * softmax(scores, dim=-1, dtype=v.dtype) (not F.softmax(.float())):
150
+ PyTorch promotes to fp32 internally then casts to v.dtype.
151
+ Explicit .float() then .to(v.dtype) cast rounds differently.
152
+ * scale applied to K (k * scale), not Q. Same math, different
153
+ bf16 multiply target.
154
+ """
155
+ q, k, v = qkv.unbind(dim=2) # each (B, T, H, D)
156
+ softmax_scale = (
157
+ self.softmax_scale if self.softmax_scale is not None
158
+ else 1.0 / math.sqrt(self.head_dim)
159
+ )
160
+ scores = torch.einsum("bthd,bshd->bhts", q, k * softmax_scale)
161
+ T = q.shape[1]
162
+ if self.causal:
163
+ causal_mask = torch.triu(
164
+ torch.full((T, T), -10000.0, device=scores.device), 1
165
+ )
166
+ scores = scores + causal_mask.to(dtype=scores.dtype)
167
+ attention = torch.softmax(scores, dim=-1, dtype=v.dtype)
168
+ if self.training and self.dropout_p > 0:
169
+ attention = F.dropout(attention, p=self.dropout_p)
170
+ out = torch.einsum("bhts,bshd->bthd", attention, v)
171
+ return out, (attention if output_attentions else None)
172
+
173
+ def _forward_sdpa(self, qkv: torch.Tensor) -> torch.Tensor:
174
+ q, k, v = qkv.unbind(dim=2)
175
+ q = q.permute(0, 2, 1, 3)
176
+ k = k.permute(0, 2, 1, 3)
177
+ v = v.permute(0, 2, 1, 3)
178
+ scale = self.softmax_scale if self.softmax_scale is not None else None
179
+ out = F.scaled_dot_product_attention(
180
+ q, k, v,
181
+ attn_mask=None,
182
+ dropout_p=self.dropout_p if self.training else 0.0,
183
+ is_causal=self.causal,
184
+ scale=scale,
185
+ )
186
+ return out.permute(0, 2, 1, 3)
187
+
188
+ def _forward_flash(self, qkv: torch.Tensor) -> torch.Tensor:
189
+ _flash_attn_required()
190
+ from flash_attn import flash_attn_qkvpacked_func
191
+ out = flash_attn_qkvpacked_func(
192
+ qkv,
193
+ dropout_p=self.dropout_p if self.training else 0.0,
194
+ softmax_scale=self.softmax_scale,
195
+ causal=self.causal,
196
+ )
197
+ return out
198
+
199
+ def _forward_with_cache(
200
+ self,
201
+ qkv: torch.Tensor,
202
+ inference_params,
203
+ ) -> torch.Tensor:
204
+ if self.rotary_emb_dim > 0:
205
+ qkv = self.rotary_emb(
206
+ qkv,
207
+ seqlen_offset=inference_params.seqlen_offset,
208
+ max_seqlen=inference_params.max_seqlen,
209
+ )
210
+ q, k, v = qkv.unbind(dim=2)
211
+ kv = torch.stack((k, v), dim=2)
212
+ kv = _update_kv_cache(kv, inference_params, self.layer_idx)
213
+ k_full, v_full = kv.unbind(dim=2)
214
+
215
+ q = q.permute(0, 2, 1, 3)
216
+ k_full = k_full.permute(0, 2, 1, 3)
217
+ v_full = v_full.permute(0, 2, 1, 3)
218
+ scale = self.softmax_scale if self.softmax_scale is not None else None
219
+ is_causal = self.causal and q.shape[-2] == k_full.shape[-2]
220
+ out = F.scaled_dot_product_attention(
221
+ q, k_full, v_full, is_causal=is_causal, scale=scale,
222
+ )
223
+ return out.permute(0, 2, 1, 3)
224
+
225
+ def forward(
226
+ self,
227
+ x: torch.Tensor,
228
+ inference_params=None,
229
+ output_attentions: bool = False,
230
+ **_unused,
231
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
232
+ if self.num_heads_kv != self.num_heads:
233
+ raise NotImplementedError("GQA is not exercised by Evo2.")
234
+
235
+ qkv = self._project_qkv(x)
236
+
237
+ if inference_params is not None:
238
+ out_btd = self._forward_with_cache(qkv, inference_params)
239
+ attn_weights = None
240
+ else:
241
+ if self.rotary_emb_dim > 0:
242
+ qkv = self.rotary_emb(qkv, seqlen_offset=0, max_seqlen=qkv.shape[1])
243
+
244
+ backend = self.attn_implementation
245
+ if output_attentions and backend != "eager":
246
+ backend = "eager"
247
+
248
+ if backend == "eager":
249
+ out_btd, attn_weights = self._forward_eager(qkv, output_attentions=output_attentions)
250
+ elif backend == "sdpa":
251
+ out_btd = self._forward_sdpa(qkv)
252
+ attn_weights = None
253
+ elif backend == "flash_attention_2":
254
+ out_btd = self._forward_flash(qkv)
255
+ attn_weights = None
256
+ else:
257
+ raise ValueError(f"Unknown attn_implementation: {backend!r}")
258
+
259
+ B, T, H, D = out_btd.shape
260
+ out_flat = out_btd.reshape(B, T, H * D)
261
+ return self.out_proj(out_flat), attn_weights
cache.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Inference-time caches for Evo2 blocks.
2
+
3
+ StripedHyena2 has four block types with different caching needs:
4
+
5
+ * `attn` blocks -> InferenceParams (standard KV cache)
6
+ * `hcl` blocks -> HyenaCascadeIIRInferenceParams (FIR window + IIR state)
7
+ * `hcm` blocks -> HyenaCascadeFIRInferenceParams (outer FIR + inner FIR)
8
+ * `hcs` blocks -> HyenaCascadeFIRInferenceParams (outer FIR + inner FIR)
9
+
10
+ Layer outputs of these caches are wrapped together inside an HF Cache subclass
11
+ (`Evo2Cache`) so model.generate() can drive autoregressive decoding without
12
+ the user having to instantiate four separate caches by hand.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from dataclasses import dataclass, field
18
+ from typing import Optional
19
+
20
+ import torch
21
+ from torch import Tensor
22
+
23
+
24
+ @dataclass
25
+ class InferenceParams:
26
+ """Standard KV cache for attention blocks."""
27
+
28
+ max_seqlen: int
29
+ max_batch_size: int
30
+ seqlen_offset: int = 0
31
+ batch_size_offset: int = 0
32
+ key_value_memory_dict: dict = field(default_factory=dict)
33
+ lengths_per_sample: Optional[Tensor] = None
34
+
35
+ def reset(self, max_seqlen, max_batch_size):
36
+ self.max_seqlen = max_seqlen
37
+ self.max_batch_size = max_batch_size
38
+ self.seqlen_offset = 0
39
+ if self.lengths_per_sample is not None:
40
+ self.lengths_per_sample.zero_()
41
+
42
+
43
+ @dataclass
44
+ class HyenaCascadeIIRInferenceParams:
45
+ """Cache for `hcl` blocks: short FIR window + IIR modal state."""
46
+
47
+ fir_filter_length: int = 3
48
+ state_dim: int = 16
49
+ seqlen_offset: int = 0
50
+ fir_state_dict: dict = field(default_factory=dict)
51
+ state_dict: dict = field(default_factory=dict)
52
+
53
+ def reset(self):
54
+ self.seqlen_offset = 0
55
+
56
+
57
+ @dataclass
58
+ class HyenaCascadeFIRInferenceParams:
59
+ """Cache for `hcm` and `hcs` blocks: outer short FIR + inner FIR cascade."""
60
+
61
+ fir_filter_length: int = 3
62
+ fir_inner_filter_length: int = 4
63
+ seqlen_offset: int = 0
64
+ fir_inner_state_dict: dict = field(default_factory=dict)
65
+ fir_state_dict: dict = field(default_factory=dict)
66
+ state_dict: dict = field(default_factory=dict)
67
+
68
+ def reset(self):
69
+ self.seqlen_offset = 0
70
+
71
+
72
+ class Evo2Cache:
73
+ """Container for per-block-type inference params.
74
+
75
+ Not a transformers.Cache subclass (the new Cache API requires per-layer
76
+ dataclasses, which doesn't fit StripedHyena 2's 4 block-type-specific
77
+ state structures). Instead we set Evo2PreTrainedModel._supports_cache_class
78
+ = False so HF's generate() treats this as an opaque past_key_values dict.
79
+ """
80
+
81
+ is_compileable = False
82
+
83
+ def __init__(
84
+ self,
85
+ max_seqlen: int,
86
+ max_batch_size: int,
87
+ short_filter_length: int,
88
+ hcm_filter_length: int,
89
+ hcs_filter_length: int,
90
+ state_size: int,
91
+ ):
92
+ self.mha = InferenceParams(
93
+ max_seqlen=max_seqlen,
94
+ max_batch_size=max_batch_size,
95
+ )
96
+ self.hcl = HyenaCascadeIIRInferenceParams(
97
+ fir_filter_length=short_filter_length,
98
+ state_dim=state_size,
99
+ )
100
+ self.hcm = HyenaCascadeFIRInferenceParams(
101
+ fir_filter_length=short_filter_length,
102
+ fir_inner_filter_length=hcm_filter_length,
103
+ )
104
+ self.hcs = HyenaCascadeFIRInferenceParams(
105
+ fir_filter_length=short_filter_length,
106
+ fir_inner_filter_length=hcs_filter_length,
107
+ )
108
+
109
+ @property
110
+ def seqlen_offset(self) -> int:
111
+ return self.mha.seqlen_offset
112
+
113
+ def get_seq_length(self, layer_idx: int = 0) -> int:
114
+ return self.mha.seqlen_offset
115
+
116
+ def get_max_cache_shape(self) -> int:
117
+ return self.mha.max_seqlen
118
+
119
+ def get_max_length(self) -> int:
120
+ return self.mha.max_seqlen
121
+
122
+ def advance(self, n: int = 1) -> None:
123
+ self.mha.seqlen_offset += n
124
+ self.hcl.seqlen_offset += n
125
+ self.hcm.seqlen_offset += n
126
+ self.hcs.seqlen_offset += n
127
+
128
+ def set_offset(self, offset: int) -> None:
129
+ self.mha.seqlen_offset = offset
130
+ self.hcl.seqlen_offset = offset
131
+ self.hcm.seqlen_offset = offset
132
+ self.hcs.seqlen_offset = offset
133
+
134
+ def reset(self) -> None:
135
+ self.mha.reset(self.mha.max_seqlen, self.mha.max_batch_size)
136
+ self.hcl.reset()
137
+ self.hcm.reset()
138
+ self.hcs.reset()
139
+
140
+ def by_block_name(self, name: str):
141
+ return getattr(self, name)
config.json ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Evo2ForCausalLM"
4
+ ],
5
+ "attn_block_dtype": "bfloat16",
6
+ "attn_layer_idxs": [
7
+ 3,
8
+ 10,
9
+ 17
10
+ ],
11
+ "auto_map": {
12
+ "AutoConfig": "configuration_evo2.Evo2Config",
13
+ "AutoModel": "modeling_evo2.Evo2Model",
14
+ "AutoModelForCausalLM": "modeling_evo2.Evo2ForCausalLM"
15
+ },
16
+ "column_split": true,
17
+ "column_split_hyena": false,
18
+ "dtype": "bfloat16",
19
+ "eps": 1e-06,
20
+ "evo2_style_activations": true,
21
+ "final_norm": true,
22
+ "hcl_filter_groups": 8192,
23
+ "hcl_layer_idxs": [
24
+ 2,
25
+ 6,
26
+ 9,
27
+ 13,
28
+ 16,
29
+ 20,
30
+ 23
31
+ ],
32
+ "hcm_filter_groups": 512,
33
+ "hcm_filter_length": 128,
34
+ "hcm_layer_idxs": [
35
+ 1,
36
+ 5,
37
+ 8,
38
+ 12,
39
+ 15,
40
+ 19,
41
+ 22
42
+ ],
43
+ "hcs_filter_groups": 512,
44
+ "hcs_filter_length": 7,
45
+ "hcs_layer_idxs": [
46
+ 0,
47
+ 4,
48
+ 7,
49
+ 11,
50
+ 14,
51
+ 18,
52
+ 21
53
+ ],
54
+ "hidden_size": 8192,
55
+ "hyena_block_dtype": "bfloat16",
56
+ "hyena_filter_groups": 1,
57
+ "hyena_flip_x1x2": false,
58
+ "hyena_out_proj_bias": true,
59
+ "inference_mode": true,
60
+ "inner_mlp_size": 22528,
61
+ "inner_size_multiple_of": 128,
62
+ "interleave": true,
63
+ "log_intermediate_values": false,
64
+ "make_vocab_size_divisible_by": 8,
65
+ "max_batch_size": 1,
66
+ "max_seqlen": 1048576,
67
+ "mha_out_proj_bias": true,
68
+ "mlp_activation": "gelu",
69
+ "mlp_dtype": "bfloat16",
70
+ "mlp_init_method": "torch.nn.init.zeros_",
71
+ "mlp_output_init_method": "torch.nn.init.zeros_",
72
+ "model_parallel_size": 1,
73
+ "model_type": "evo2",
74
+ "num_attention_heads": 64,
75
+ "num_filters": 8192,
76
+ "num_layers": 24,
77
+ "pipe_parallel_size": 1,
78
+ "prefill_style": "fft",
79
+ "print_activations": false,
80
+ "proj_groups": 1,
81
+ "qkv_proj_bias": false,
82
+ "rotary_emb_base": 1000000,
83
+ "rotary_emb_scaling_factor": 128,
84
+ "short_filter_bias": false,
85
+ "short_filter_length": 3,
86
+ "smeared_gqa": false,
87
+ "state_size": 16,
88
+ "tie_embeddings": true,
89
+ "transformers_version": "4.57.6",
90
+ "use_cache": true,
91
+ "use_flash_attn": false,
92
+ "use_flash_depthwise": false,
93
+ "use_flash_rmsnorm": false,
94
+ "use_flashfft": false,
95
+ "use_fp8_input_projections": true,
96
+ "use_interpolated_rotary_pos_emb": true,
97
+ "use_laughing_hyena": false,
98
+ "vocab_size": 512,
99
+ "model_max_length": 1048576
100
+ }
configuration_evo2.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Configuration for Evo2 (StripedHyena2)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import List
7
+
8
+ from transformers import PretrainedConfig
9
+
10
+
11
+ class Evo2Config(PretrainedConfig):
12
+ """Evo2 config.
13
+
14
+ Defaults match evo2_7b_base (32-layer, 4096-hidden). Per-variant overrides
15
+ are written into the config.json of each repo by convert_checkpoint.py.
16
+
17
+ Block dispatch is driven by four index lists:
18
+ attn_layer_idxs : transformer (MHA + RoPE) blocks
19
+ hcl_layer_idxs : Hyena Cascade Long (IIR via log_poles + residues)
20
+ hcm_layer_idxs : Hyena Cascade Medium (FIR, fir_inner_filter_length=128)
21
+ hcs_layer_idxs : Hyena Cascade Short (FIR, fir_inner_filter_length=7)
22
+ Their disjoint union must equal range(num_layers).
23
+ """
24
+
25
+ model_type = "evo2"
26
+
27
+ def __init__(
28
+ self,
29
+ # Architecture core
30
+ vocab_size: int = 512,
31
+ hidden_size: int = 4096,
32
+ num_filters: int = 4096,
33
+ inner_mlp_size: int = 11008,
34
+ num_layers: int = 32,
35
+ num_attention_heads: int = 32,
36
+ # Block dispatch
37
+ attn_layer_idxs: List[int] | None = None,
38
+ hcl_layer_idxs: List[int] | None = None,
39
+ hcm_layer_idxs: List[int] | None = None,
40
+ hcs_layer_idxs: List[int] | None = None,
41
+ # Filter geometry
42
+ hcm_filter_length: int = 128,
43
+ hcs_filter_length: int = 7,
44
+ hcl_filter_groups: int = 4096,
45
+ hcm_filter_groups: int = 256,
46
+ hcs_filter_groups: int = 256,
47
+ hyena_filter_groups: int = 1,
48
+ short_filter_length: int = 3,
49
+ short_filter_bias: bool = False,
50
+ state_size: int = 16,
51
+ # Channel-split conventions
52
+ column_split: bool = True,
53
+ column_split_hyena: bool = False,
54
+ interleave: bool = True,
55
+ hyena_flip_x1x2: bool = False,
56
+ # Norms / activations
57
+ eps: float = 1e-6,
58
+ final_norm: bool = True,
59
+ mlp_activation: str = "gelu",
60
+ evo2_style_activations: bool = True,
61
+ # Linear biases
62
+ mha_out_proj_bias: bool = True,
63
+ hyena_out_proj_bias: bool = True,
64
+ qkv_proj_bias: bool = False,
65
+ # MLP geometry
66
+ inner_size_multiple_of: int = 16,
67
+ make_vocab_size_divisible_by: int = 8,
68
+ # Embeddings
69
+ tie_embeddings: bool = True,
70
+ # Rotary / sequence
71
+ max_seqlen: int = 8192,
72
+ max_batch_size: int = 1,
73
+ rotary_emb_base: float = 10000.0,
74
+ use_interpolated_rotary_pos_emb: bool = False,
75
+ rotary_emb_scaling_factor: float = 1.0,
76
+ # Inference engine
77
+ prefill_style: str = "fft",
78
+ inference_mode: bool = True,
79
+ # Projection precision (TE FP8 - required for 1b / 20b / 40b)
80
+ use_fp8_input_projections: bool = False,
81
+ # Backend toggles
82
+ use_cache: bool = True,
83
+ # Vortex used this flag at runtime. Our HF port ignores it entirely
84
+ # (attention dispatch is driven by ``config._attn_implementation``,
85
+ # the standard HF mechanism). Default is False here so the config
86
+ # reflects what actually runs: SDPA by default unless the user passes
87
+ # ``attn_implementation="flash_attention_2"`` to from_pretrained.
88
+ use_flash_attn: bool = False,
89
+ use_flash_rmsnorm: bool = False,
90
+ use_flash_depthwise: bool = False,
91
+ use_flashfft: bool = False,
92
+ # Per-block dtypes (cast at module init)
93
+ attn_block_dtype: str = "bfloat16",
94
+ hyena_block_dtype: str = "bfloat16",
95
+ mlp_dtype: str = "bfloat16",
96
+ # Multi-tensor parallel knobs (kept for ParallelGatedMLP._compute_inner_size)
97
+ model_parallel_size: int = 1,
98
+ pipe_parallel_size: int = 1,
99
+ # GQA
100
+ proj_groups: int = 1,
101
+ smeared_gqa: bool = False,
102
+ **kwargs,
103
+ ):
104
+ if attn_layer_idxs is None:
105
+ attn_layer_idxs = [3, 10, 17, 24, 31]
106
+ if hcl_layer_idxs is None:
107
+ hcl_layer_idxs = [2, 6, 9, 13, 16, 20, 23, 27, 30]
108
+ if hcm_layer_idxs is None:
109
+ hcm_layer_idxs = [1, 5, 8, 12, 15, 19, 22, 26, 29]
110
+ if hcs_layer_idxs is None:
111
+ hcs_layer_idxs = [0, 4, 7, 11, 14, 18, 21, 25, 28]
112
+
113
+ self.vocab_size = vocab_size
114
+ self.hidden_size = hidden_size
115
+ self.num_filters = num_filters
116
+ self.inner_mlp_size = inner_mlp_size
117
+ self.num_layers = num_layers
118
+ self.num_attention_heads = num_attention_heads
119
+
120
+ self.attn_layer_idxs = attn_layer_idxs
121
+ self.hcl_layer_idxs = hcl_layer_idxs
122
+ self.hcm_layer_idxs = hcm_layer_idxs
123
+ self.hcs_layer_idxs = hcs_layer_idxs
124
+
125
+ self.hcm_filter_length = hcm_filter_length
126
+ self.hcs_filter_length = hcs_filter_length
127
+ self.hcl_filter_groups = hcl_filter_groups
128
+ self.hcm_filter_groups = hcm_filter_groups
129
+ self.hcs_filter_groups = hcs_filter_groups
130
+ self.hyena_filter_groups = hyena_filter_groups
131
+ self.short_filter_length = short_filter_length
132
+ self.short_filter_bias = short_filter_bias
133
+ self.state_size = state_size
134
+
135
+ self.column_split = column_split
136
+ self.column_split_hyena = column_split_hyena
137
+ self.interleave = interleave
138
+ self.hyena_flip_x1x2 = hyena_flip_x1x2
139
+
140
+ self.eps = eps
141
+ self.final_norm = final_norm
142
+ self.mlp_activation = mlp_activation
143
+ self.evo2_style_activations = evo2_style_activations
144
+
145
+ self.mha_out_proj_bias = mha_out_proj_bias
146
+ self.hyena_out_proj_bias = hyena_out_proj_bias
147
+ self.qkv_proj_bias = qkv_proj_bias
148
+
149
+ self.inner_size_multiple_of = inner_size_multiple_of
150
+ self.make_vocab_size_divisible_by = make_vocab_size_divisible_by
151
+
152
+ self.tie_embeddings = tie_embeddings
153
+
154
+ self.max_seqlen = max_seqlen
155
+ self.max_batch_size = max_batch_size
156
+ self.rotary_emb_base = rotary_emb_base
157
+ self.use_interpolated_rotary_pos_emb = use_interpolated_rotary_pos_emb
158
+ self.rotary_emb_scaling_factor = rotary_emb_scaling_factor
159
+
160
+ self.prefill_style = prefill_style
161
+ self.inference_mode = inference_mode
162
+
163
+ self.use_fp8_input_projections = use_fp8_input_projections
164
+
165
+ self.use_cache = use_cache
166
+ self.use_flash_attn = use_flash_attn
167
+ self.use_flash_rmsnorm = use_flash_rmsnorm
168
+ self.use_flash_depthwise = use_flash_depthwise
169
+ self.use_flashfft = use_flashfft
170
+
171
+ self.attn_block_dtype = attn_block_dtype
172
+ self.hyena_block_dtype = hyena_block_dtype
173
+ self.mlp_dtype = mlp_dtype
174
+
175
+ self.model_parallel_size = model_parallel_size
176
+ self.pipe_parallel_size = pipe_parallel_size
177
+
178
+ self.proj_groups = proj_groups
179
+ self.smeared_gqa = smeared_gqa
180
+
181
+ super().__init__(**kwargs)
182
+
183
+ # HF generation helpers expect `num_hidden_layers`.
184
+ @property
185
+ def num_hidden_layers(self) -> int:
186
+ return self.num_layers
187
+
188
+ # Internal blocks were originally written against a dotdict and call
189
+ # `config.get(key, default)` extensively; provide a dict-like getter.
190
+ def get(self, key, default=None):
191
+ return getattr(self, key, default)
192
+
193
+ @classmethod
194
+ def from_original_config(cls, config_path: str, **kwargs) -> "Evo2Config":
195
+ with open(config_path, "r") as f:
196
+ config = json.load(f)
197
+ return cls(**config, **kwargs)
engine.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Hyena inference engine for Evo2 (StripedHyena2).
2
+
3
+ Three operator families are exercised by Evo2 blocks:
4
+
5
+ * parallel_fir(gate=False) -- outer FIR used by all hyena blocks before any
6
+ channel split (input projection convolution).
7
+ * parallel_fir(gate=True) -- inner FIR cascade used by hcm/hcs blocks
8
+ (x1 * v gated, then convolved by `h`,
9
+ then multiplied by x2 postgate).
10
+ * parallel_iir -- modal-form IIR (long convolution via FFT)
11
+ used by hcl blocks; poles + residues parameterize
12
+ a stable, long-range linear filter.
13
+
14
+ Sequential step paths (step_fir / step_iir) are used during generation.
15
+
16
+ Layout conventions match vortex exactly so checkpoints are bit-identical.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import torch
22
+ import torch.nn.functional as F
23
+
24
+
25
+ IIR_PREFILL_MODES = ["recurrence", "modal-fft"]
26
+
27
+
28
+ def adjust_filter_shape_for_broadcast(u, h):
29
+ h = h.squeeze()
30
+ if len(u.shape) > len(h.shape):
31
+ h = h.unsqueeze(0)
32
+ if len(u.shape) > 3:
33
+ h = h.unsqueeze(1)
34
+ return h
35
+
36
+
37
+ def fftconv_func(u, k, D, dropout_mask, gelu=True, k_rev=None, bidirectional=False, **kwargs):
38
+ """FFT convolution for long FIR filters (length >= 128 path)."""
39
+ seqlen = u.shape[-1]
40
+ fft_size = 2 * seqlen
41
+
42
+ k_f = torch.fft.rfft(k, n=fft_size) / fft_size
43
+ k_f = adjust_filter_shape_for_broadcast(u, k_f)
44
+ k = k.squeeze()
45
+
46
+ if bidirectional:
47
+ u_f = torch.fft.rfft(u.to(dtype=k.dtype), n=fft_size)
48
+ k, k2 = k.split(k.shape[1] // 2, dim=1)
49
+ k2_f = torch.fft.rfft(k2, n=fft_size) / fft_size
50
+ y1 = u_f * k_f
51
+ y2 = u_f.conj() * k2_f.conj()
52
+ y = torch.fft.irfft(y1 + y2, n=fft_size, norm="forward")[..., :seqlen]
53
+ else:
54
+ if k_rev is not None:
55
+ k_rev_f = torch.fft.rfft(k_rev, n=fft_size) / fft_size
56
+ k_f = k_f + k_rev_f.conj()
57
+ u_f = torch.fft.rfft(u.to(dtype=k.dtype), n=fft_size)
58
+ y = torch.fft.irfft(u_f * k_f, n=fft_size, norm="forward")[..., :seqlen]
59
+
60
+ out = y + u * D.unsqueeze(-1)
61
+ return out.to(dtype=u.dtype)
62
+
63
+
64
+ def _column_split(x, num_heads, head_size):
65
+ """Compatibility helper for column_split_hyena=True (not used by Evo2)."""
66
+ x = x.reshape(x.shape[0], num_heads, 3 * head_size, x.shape[2])
67
+ x2 = x[:, :, :head_size].reshape(x.shape[0], -1, x.shape[-1])
68
+ x1 = x[:, :, head_size : 2 * head_size].reshape(x.shape[0], -1, x.shape[-1])
69
+ v = x[:, :, 2 * head_size :].reshape(x.shape[0], -1, x.shape[-1])
70
+ return x2, x1, v
71
+
72
+
73
+ class HyenaInferenceEngine:
74
+ def __init__(
75
+ self,
76
+ layer_idx: int | None = None,
77
+ iir_prefill_style: str = "modal-fft",
78
+ hyena_flip_x1x2: bool = False,
79
+ ) -> None:
80
+ assert iir_prefill_style in IIR_PREFILL_MODES, iir_prefill_style
81
+ self.iir_prefill_style = iir_prefill_style
82
+ self.layer_idx = layer_idx
83
+ self.low_mem_mode = False
84
+ self.hyena_flip_x1x2 = hyena_flip_x1x2
85
+
86
+ # ---------------------------------------------------------------- FIR
87
+ def parallel_fir(
88
+ self,
89
+ fir_fn,
90
+ u,
91
+ weight,
92
+ bias,
93
+ L,
94
+ dims,
95
+ groups=None,
96
+ gated_bias=False,
97
+ column_split_hyena=False,
98
+ dim_last=True,
99
+ fir_length=3,
100
+ gate=False,
101
+ inference_params=None,
102
+ padding_mask=None,
103
+ ):
104
+ L = u.shape[1] if dim_last else u.shape[2]
105
+ if gate:
106
+ hidden_size, num_attention_heads, hidden_size_per_attention_head, _, _ = dims
107
+ if column_split_hyena:
108
+ x2, x1, v = _column_split(u, num_attention_heads, hidden_size_per_attention_head)
109
+ else:
110
+ x2, x1, v = u.split([hidden_size, hidden_size, hidden_size], dim=1)
111
+ if self.hyena_flip_x1x2:
112
+ x1, x2 = x2, x1
113
+ u = x1 * v
114
+
115
+ if fir_length >= 128:
116
+ with torch.autocast("cuda"):
117
+ z = fftconv_func(
118
+ u.to(torch.float32),
119
+ weight[:, :, :L].to(torch.float32),
120
+ bias,
121
+ None,
122
+ gelu=False,
123
+ bidirectional=False,
124
+ groups=groups,
125
+ )
126
+ z = z.to(u.dtype)
127
+ else:
128
+ if dim_last:
129
+ u = u.permute(0, 2, 1) # B, D, L
130
+
131
+ z = fir_fn(
132
+ u.to(torch.float32),
133
+ weight.to(torch.float32),
134
+ bias=None,
135
+ stride=1,
136
+ padding=fir_length - 1,
137
+ groups=u.shape[1],
138
+ )[..., :L]
139
+
140
+ z = z.to(u.dtype)
141
+
142
+ if bias is not None:
143
+ if gated_bias:
144
+ z = z + bias[None, :, None] * u
145
+ else:
146
+ z = z + bias[None, :, None]
147
+
148
+ if isinstance(padding_mask, torch.Tensor):
149
+ z = z * padding_mask[:, None]
150
+
151
+ if gate:
152
+ z = x2 * z
153
+
154
+ if inference_params is not None:
155
+ fir_state = u[..., -fir_length + 1 :]
156
+ else:
157
+ fir_state = None
158
+
159
+ return z, fir_state
160
+
161
+ # ---------------------------------------------------------------- IIR
162
+ def parallel_iir(
163
+ self,
164
+ z_pre,
165
+ h,
166
+ D,
167
+ L,
168
+ poles,
169
+ residues,
170
+ t,
171
+ dims,
172
+ layer_idx,
173
+ inference_params=None,
174
+ prefill_style: str = "fft",
175
+ fftconv_fn=None,
176
+ padding_mask=None,
177
+ use_flashfft: bool = False,
178
+ column_split_hyena: bool = False,
179
+ long_fir_threshold: int | None = None,
180
+ ):
181
+ fft_size = 2 * L
182
+ hidden_size, num_attention_heads, hidden_size_per_attention_head, _, _ = dims
183
+ if column_split_hyena:
184
+ x2, x1, v = _column_split(z_pre, num_attention_heads, hidden_size_per_attention_head)
185
+ else:
186
+ x2, x1, v = z_pre.split([hidden_size, hidden_size, hidden_size], dim=1)
187
+
188
+ if self.hyena_flip_x1x2:
189
+ x1, x2 = x2, x1
190
+
191
+ x1v = x1 * v
192
+
193
+ X_s = None
194
+ if inference_params is not None and prefill_style == "recurrence":
195
+ y = self.prefill_via_direct_recurrence(
196
+ inference_params=inference_params, x1v=x1v, L=L,
197
+ poles=poles, residues=residues,
198
+ )
199
+ else:
200
+ if use_flashfft and (L % 2) == 0:
201
+ y = fftconv_fn(
202
+ x1v.to(dtype=torch.bfloat16).contiguous(),
203
+ h.to(dtype=torch.float32),
204
+ )
205
+ elif long_fir_threshold is None:
206
+ H = torch.fft.rfft(h.to(dtype=torch.float32), n=fft_size) / fft_size
207
+ X_s = torch.fft.fft(x1v.to(dtype=torch.float32), n=fft_size)
208
+ X = X_s[..., : H.shape[-1]]
209
+ if len(z_pre.shape) > 3:
210
+ H = H.unsqueeze(1)
211
+ y = torch.fft.irfft(X * H, n=fft_size, norm="forward")[..., :L]
212
+ else:
213
+ assert h.shape[0] == 1, "batch size must be 1 for long_fir_threshold"
214
+ h = h[0][:, None]
215
+ h = h[..., :long_fir_threshold]
216
+ y = F.conv1d(
217
+ x1v, h.to(dtype=x1v.dtype),
218
+ stride=1, groups=x1v.shape[1],
219
+ padding=h.shape[-1] - 1,
220
+ )[..., :L]
221
+
222
+ y = y.to(dtype=x1v.dtype)
223
+ y = (y + x1v * D.unsqueeze(-1)) * x2
224
+
225
+ if inference_params is not None and prefill_style == "fft":
226
+ self.prefill_via_modal_fft(
227
+ inference_params=inference_params, x1v=x1v, X_s=X_s, L=L,
228
+ t=t, poles=poles, dims=dims, layer_idx=layer_idx,
229
+ use_flashfft=use_flashfft, fftconv_fn=fftconv_fn,
230
+ )
231
+
232
+ return y.permute(0, 2, 1)
233
+
234
+ # --------------------------------------------------------- step (decode)
235
+ def step_fir(self, u, fir_state, weight, bias=None, gated_bias=False, flip_filter=False):
236
+ """Single-step FIR. fir_state holds the last (filter_len - 1) inputs."""
237
+ weight = weight.squeeze()
238
+ cache_size = fir_state.shape[-1]
239
+ filter_length = weight.shape[-1]
240
+ if flip_filter:
241
+ weight = weight.flip(-1)
242
+ weight = weight[..., -cache_size - 1 :].unsqueeze(0)
243
+ else:
244
+ weight = weight[..., : cache_size + 1].unsqueeze(0)
245
+
246
+ input_dtype = u.dtype
247
+ weight = weight.to(torch.float32)
248
+ u = u.to(torch.float32)
249
+ fir_state = fir_state.to(torch.float32)
250
+ bias = bias.to(torch.float32) if bias is not None else None
251
+
252
+ h0, h = weight[..., -1], weight[..., :-1]
253
+ y = h0 * u + torch.sum(fir_state * h, dim=-1)
254
+
255
+ if bias is not None:
256
+ if gated_bias:
257
+ y = y + bias * u
258
+ else:
259
+ y = y + bias
260
+
261
+ if cache_size < filter_length - 1:
262
+ fir_state = torch.cat([fir_state, u[..., None]], dim=-1)
263
+ else:
264
+ fir_state = torch.roll(fir_state, -1, dims=2)
265
+ fir_state[..., -1] = u
266
+
267
+ return y.to(input_dtype), fir_state
268
+
269
+ def step_iir(self, x2, x1, v, D, residues, poles, iir_state, iir_groups=1):
270
+ x1v = x1 * v
271
+ # `poles` arg contains log_poles (real, in modal form for evo2)
272
+ poles = torch.exp(poles)
273
+ poles = poles[..., 0][None]
274
+ residues = residues[None]
275
+ iir_state = poles * iir_state + x1v[..., None]
276
+ res_state = torch.sum(residues * iir_state, dim=-1)
277
+
278
+ if iir_groups > 1:
279
+ raise NotImplementedError
280
+ y = x2 * (res_state + D * x1v)
281
+ return y, iir_state
282
+
283
+ def prefill_via_direct_recurrence(self, inference_params, x1v, L, residues, poles, *args, **kwargs):
284
+ state_dim = poles.shape[1]
285
+ x1v_ = x1v[..., None, None]
286
+ x1v_ = x1v_.repeat(1, 1, 1, state_dim, 2)
287
+ x1v_[..., 1] = 0
288
+
289
+ state = 0 * x1v_[:, :, 0]
290
+ output = 0 * x1v_[:, :, :, 0, 0]
291
+
292
+ poles = poles[:, :, 0][None]
293
+ residues = residues[:, :, 0][None].repeat(x1v_.shape[0], 1, 1, 1)
294
+
295
+ for i in range(L):
296
+ state[..., 0] = poles[..., 0] * state[..., 0] - poles[..., 1] * state[..., 1] + x1v_[:, :, i, :, 0]
297
+ state[..., 1] = poles[..., 0] * state[..., 1] + poles[..., 1] * state[..., 0] + x1v_[:, :, i, :, 1]
298
+ output[:, :, i] = torch.sum(residues * state, dim=-2)[..., 0]
299
+
300
+ inference_params.state_dict[self.layer_idx] = state.to(dtype=torch.float32)
301
+ return output
302
+
303
+ def prefill_via_modal_fft(
304
+ self, inference_params, x1v, L, poles, t, dims, layer_idx,
305
+ X_s=None, use_flashfft=False, fftconv_fn=None,
306
+ state_dtype=torch.float32, *args, **kwargs,
307
+ ):
308
+ """Compute IIR state via a single FFT.
309
+
310
+ Evo2 uses *real* `log_poles` (not the complex view-as-real layout
311
+ that Evo1 uses), so the impulse-response IFFT is mathematically real;
312
+ the imaginary component is FFT round-off. We take ``.real`` explicitly
313
+ instead of relying on torch's lossy complex->real cast, which avoids
314
+ the "Casting complex values to real discards the imaginary part"
315
+ UserWarning at every decoded token.
316
+ """
317
+ hidden_size, _, _, state_size, hyena_filter_groups = dims
318
+
319
+ assert X_s is not None
320
+ bs = x1v.shape[0]
321
+ fft_size = 2 * L
322
+ state_s = (poles.to(torch.float32) * t).exp()
323
+ state_S = torch.fft.fft(state_s, n=fft_size).repeat(bs, 1, 1, 1)
324
+ if hyena_filter_groups > 1:
325
+ state_S = state_S.repeat_interleave(hidden_size // hyena_filter_groups, 1)
326
+ state = torch.fft.ifft(X_s[..., None, :] * state_S, n=fft_size)
327
+ inference_params.state_dict[layer_idx] = state[..., L - 1].real.to(dtype=state_dtype)
generation_config.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "eos_token_id": 0,
3
+ "pad_token_id": 1,
4
+ "_from_model_config": true
5
+ }
hyena.py ADDED
@@ -0,0 +1,514 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """StripedHyena2 block definitions for Evo2.
2
+
3
+ Block types:
4
+
5
+ * AttentionBlock -- standard pre-norm transformer block with MHA + RoPE.
6
+ * ParallelGatedConvBlock -- pre-norm hyena block; the inner `filter` is a
7
+ HyenaCascade configured per block-type idx:
8
+ hcl: IIR via log_poles + residues
9
+ hcm: FIR cascade, fir_inner_filter_length=128
10
+ hcs: FIR cascade, fir_inner_filter_length=7
11
+
12
+ Inside ParallelGatedConvBlock, `projections` is a TELinear (3x hidden_size
13
+ output) -- the FP8-capable input projection. For 7B variants without TE,
14
+ the pure-PyTorch fallback in layers.py is used.
15
+
16
+ The `interleave` config flag (True for all Evo2 variants) reorders the
17
+ channel dim after the outer FIR before the channel-split / cascade.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ from typing import Optional, Tuple
23
+
24
+ import torch
25
+ import torch.nn as nn
26
+ import torch.nn.functional as F
27
+
28
+ from .attention import MHA
29
+ from .engine import HyenaInferenceEngine
30
+ from .layers import ParallelGatedMLP, RMSNorm, TELinear
31
+ from .rotary import restore_rotary_inv_freq, swap_mha_rope
32
+
33
+
34
+ def _interleave(z_pre: torch.Tensor) -> torch.Tensor:
35
+ """Deinterleave a [x1_0,x2_0,v_0,x1_1,x2_1,v_1,...] channel layout into
36
+ blocks of [x1_all | x2_all | v_all]. Matches vortex.model.utils.interleave."""
37
+ if len(z_pre.shape) == 3: # (B, C, L) non-cached path
38
+ x1 = z_pre[:, 0::3, :]
39
+ x2 = z_pre[:, 1::3, :]
40
+ v = z_pre[:, 2::3, :]
41
+ return torch.cat([x1, x2, v], dim=1)
42
+ x1 = z_pre[..., 0::3]
43
+ x2 = z_pre[..., 1::3]
44
+ v = z_pre[..., 2::3]
45
+ return torch.cat([x1, x2, v], dim=-1)
46
+
47
+
48
+ def _column_split_step(z_pre, num_heads, head_size):
49
+ """Per-step variant of column_split for sequential_forward (cache step)."""
50
+ x = z_pre.reshape(z_pre.shape[0], num_heads, 3 * head_size)
51
+ x2 = x[..., :head_size].reshape(z_pre.shape[0], -1)
52
+ x1 = x[..., head_size : 2 * head_size].reshape(z_pre.shape[0], -1)
53
+ v = x[..., 2 * head_size :].reshape(z_pre.shape[0], -1)
54
+ return x2, x1, v
55
+
56
+
57
+ # =============================================================================
58
+ # Attention block
59
+ # =============================================================================
60
+
61
+
62
+ class AttentionBlock(nn.Module):
63
+ def __init__(self, config, layer_idx: int) -> None:
64
+ super().__init__()
65
+ self.config = config
66
+ self.layer_idx = layer_idx
67
+ self.pre_norm = RMSNorm(config)
68
+ self.post_norm = RMSNorm(config)
69
+ self.proj_groups = config.get("proj_groups", 1)
70
+ dtype = self._resolve_dtype(config.get("attn_block_dtype", "bfloat16"))
71
+ mlp_dtype = self._resolve_dtype(config.get("mlp_dtype", "bfloat16"))
72
+ self.num_attention_heads = config.num_attention_heads
73
+ self.hidden_size = config.hidden_size
74
+ self.hidden_size_per_attention_head = config.hidden_size // config.num_attention_heads
75
+
76
+ attn_impl = getattr(config, "_attn_implementation", "eager")
77
+
78
+ self.inner_mha_cls = MHA(
79
+ embed_dim=config.hidden_size,
80
+ num_heads=config.num_attention_heads,
81
+ num_heads_kv=config.num_attention_heads // self.proj_groups,
82
+ rotary_emb_dim=config.hidden_size // config.num_attention_heads,
83
+ qkv_proj_bias=config.get("qkv_proj_bias", False),
84
+ rotary_emb_base=config.get("rotary_emb_base", 1000000),
85
+ causal=True,
86
+ layer_idx=layer_idx,
87
+ out_proj_bias=config.get("mha_out_proj_bias", True),
88
+ attn_implementation=attn_impl,
89
+ ).to(dtype=dtype)
90
+
91
+ if config.get("use_interpolated_rotary_pos_emb", False):
92
+ swap_mha_rope(
93
+ mha=self.inner_mha_cls,
94
+ kwargs_new_rope={
95
+ "scaling_factor": config.get("rotary_emb_scaling_factor", 1.0)
96
+ },
97
+ )
98
+
99
+ if self.config.get("smeared_gqa", False):
100
+ self.inner_mha_cls.num_heads_kv = self.inner_mha_cls.num_heads
101
+ restore_rotary_inv_freq(self.inner_mha_cls.rotary_emb)
102
+
103
+ self.mlp = ParallelGatedMLP(config, layer_idx).to(dtype=mlp_dtype)
104
+
105
+ @staticmethod
106
+ def _resolve_dtype(name) -> torch.dtype:
107
+ if isinstance(name, torch.dtype):
108
+ return name
109
+ mapping = {
110
+ "bfloat16": torch.bfloat16,
111
+ "float16": torch.float16,
112
+ "float32": torch.float32,
113
+ }
114
+ return mapping.get(name, torch.bfloat16)
115
+
116
+ def forward(
117
+ self,
118
+ u: torch.Tensor,
119
+ inference_params=None,
120
+ padding_mask: Optional[torch.Tensor] = None,
121
+ output_attentions: bool = False,
122
+ *args,
123
+ **kwargs,
124
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
125
+ if isinstance(padding_mask, torch.Tensor):
126
+ # Zero attended values at pad positions (no qkv bias here).
127
+ u = u * padding_mask[..., None]
128
+
129
+ attn_out, attn_weights = self.inner_mha_cls(
130
+ self.pre_norm(u),
131
+ inference_params=inference_params,
132
+ output_attentions=output_attentions,
133
+ )
134
+ u = attn_out + u
135
+
136
+ if isinstance(padding_mask, torch.Tensor):
137
+ u = u * padding_mask[..., None]
138
+ u = self.mlp(self.post_norm(u)) + u
139
+ return u, attn_weights
140
+
141
+
142
+ # =============================================================================
143
+ # Hyena cascade (hcl / hcm / hcs)
144
+ # =============================================================================
145
+
146
+
147
+ class HyenaCascade(nn.Module):
148
+ """The inner mixer of a hyena block.
149
+
150
+ Two execution modes selected by `fir_inner_filter_length`:
151
+ * None -> hcl: outer FIR -> channel split -> parallel_iir
152
+ (modal-form long convolution)
153
+ * int (e.g. 7 or 128) -> hcm/hcs: outer FIR -> interleave -> inner FIR
154
+ cascade via parallel_fir(gate=True)
155
+ """
156
+
157
+ def __init__(
158
+ self,
159
+ config,
160
+ layer_idx: int,
161
+ hyena_filter_groups: int,
162
+ fir_inner_filter_length: int | None = None,
163
+ ) -> None:
164
+ super().__init__()
165
+ self.config = config
166
+ self.layer_idx = layer_idx
167
+ self.hyena_filter_groups = hyena_filter_groups
168
+
169
+ self.use_flashfft = config.get("use_flashfft", False)
170
+ self.state_size = config.state_size
171
+ self.hidden_size = config.hidden_size
172
+ self.num_filters = config.num_filters
173
+ self.inference_mode = config.get("inference_mode", True)
174
+ self.column_split_hyena = config.get("column_split_hyena", False)
175
+ self.hyena_flip_x1x2 = config.get("hyena_flip_x1x2", False)
176
+
177
+ assert self.hidden_size % self.num_filters == 0
178
+ assert self.num_filters <= self.hidden_size
179
+
180
+ self.num_attention_heads = config.num_attention_heads
181
+ self.hidden_size_per_attention_head = (
182
+ self.hidden_size // self.num_attention_heads
183
+ )
184
+
185
+ self.fir_inner_filter_length = fir_inner_filter_length
186
+ self.short_filter_length = config.short_filter_length
187
+ self.short_filter_weight = nn.Parameter(
188
+ torch.randn(3 * config.hidden_size, 1, config.short_filter_length)
189
+ )
190
+ self.short_filter_bias = (
191
+ nn.Parameter(torch.randn(3 * config.hidden_size))
192
+ if config.short_filter_bias else None
193
+ )
194
+
195
+ self.engine = HyenaInferenceEngine(
196
+ layer_idx=layer_idx,
197
+ hyena_flip_x1x2=config.get("hyena_flip_x1x2", False),
198
+ )
199
+ self.fir_fn = F.conv1d
200
+ self.fir_inner_fn = F.conv1d
201
+ self.fftconv_fn = None
202
+ self.long_fir_threshold = config.get("long_fir_threshold", None)
203
+ if self.long_fir_threshold is not None:
204
+ assert not self.use_flashfft, "long_fir_threshold incompatible with flashfft"
205
+
206
+ self.num_systems = self.hyena_filter_groups
207
+ self.channels_per_group = self.hidden_size // self.hyena_filter_groups
208
+
209
+ if self.fir_inner_filter_length:
210
+ # hcm / hcs: explicit FIR filter `h` of shape (groups, 1, inner_len)
211
+ self.h = nn.Parameter(
212
+ torch.randn(self.hyena_filter_groups, 1, fir_inner_filter_length)
213
+ )
214
+ if fir_inner_filter_length >= 128:
215
+ self.D = nn.Parameter(torch.zeros(self.hidden_size))
216
+ else:
217
+ self.D = None
218
+ else:
219
+ # hcl: modal-form IIR, log_poles in (num_systems, state_size, 1)
220
+ log_poles = torch.randn(self.num_systems, self.state_size, 1, dtype=torch.float32)
221
+ self.log_poles = nn.Parameter(log_poles)
222
+ self.residues = nn.Parameter(
223
+ torch.randn(self.num_systems, self.state_size, dtype=torch.float32)
224
+ )
225
+ self.D = nn.Parameter(torch.zeros(self.hidden_size))
226
+ self.h = None
227
+
228
+ self.t = None
229
+
230
+ def forward(self, u, inference_params=None, padding_mask=None, *args, **kwargs):
231
+ if (
232
+ inference_params is not None
233
+ and self.layer_idx in inference_params.fir_state_dict
234
+ ):
235
+ return self.sequential_forward(u, inference_params)
236
+ return self.parallel_forward(u, inference_params, padding_mask)
237
+
238
+ def parallel_forward(self, u, inference_params=None, padding_mask=None):
239
+ L = u.shape[1]
240
+ dims = (
241
+ self.hidden_size,
242
+ self.num_attention_heads,
243
+ self.hidden_size_per_attention_head,
244
+ self.state_size,
245
+ self.hyena_filter_groups,
246
+ )
247
+ z_pre, fir_state = self.engine.parallel_fir(
248
+ self.fir_fn, u,
249
+ self.short_filter_weight, self.short_filter_bias,
250
+ L, dims=dims, gate=False,
251
+ column_split_hyena=self.column_split_hyena,
252
+ fir_length=self.short_filter_length,
253
+ inference_params=inference_params,
254
+ padding_mask=padding_mask, dim_last=True,
255
+ )
256
+ if inference_params:
257
+ inference_params.fir_state_dict[self.layer_idx] = fir_state
258
+
259
+ if self.config.interleave:
260
+ z_pre = _interleave(z_pre)
261
+
262
+ if self.h is None:
263
+ # hcl path: compute IIR impulse response then convolve via FFT
264
+ h, _, _, _ = self.compute_filter(L, u.device)
265
+ else:
266
+ h = self.h
267
+
268
+ D = self.D
269
+ if self.hyena_filter_groups > 1:
270
+ h = h.repeat_interleave(self.hidden_size // self.hyena_filter_groups, 0)
271
+
272
+ if self.fir_inner_filter_length is not None:
273
+ # hcm / hcs: inner FIR cascade
274
+ y, fir_inner_state = self.engine.parallel_fir(
275
+ self.fir_inner_fn, z_pre, h, D, L,
276
+ dims=dims, gate=True,
277
+ gated_bias=self.fir_inner_filter_length >= 128,
278
+ dim_last=False,
279
+ column_split_hyena=self.column_split_hyena,
280
+ fir_length=self.fir_inner_filter_length,
281
+ inference_params=inference_params,
282
+ padding_mask=padding_mask,
283
+ groups=self.hyena_filter_groups,
284
+ )
285
+ y = y.permute(0, 2, 1)
286
+ if inference_params:
287
+ inference_params.fir_inner_state_dict[self.layer_idx] = fir_inner_state
288
+ else:
289
+ # hcl: parallel IIR via FFT
290
+ y = self.engine.parallel_iir(
291
+ z_pre, h, D, L, t=self.t,
292
+ poles=self.log_poles, residues=self.residues,
293
+ dims=dims, inference_params=inference_params,
294
+ layer_idx=self.layer_idx,
295
+ prefill_style=self.config.get("prefill_style", "fft"),
296
+ use_flashfft=self.use_flashfft,
297
+ fftconv_fn=self.fftconv_fn,
298
+ column_split_hyena=self.column_split_hyena,
299
+ long_fir_threshold=self.long_fir_threshold,
300
+ padding_mask=padding_mask,
301
+ )
302
+
303
+ return y, inference_params
304
+
305
+ def sequential_forward(self, u, inference_params):
306
+ if len(u.shape) > 2:
307
+ u = u[:, -1]
308
+ # Track input dtype so we can cast back at the end. step_iir produces
309
+ # fp32 output (log_poles / residues are fp32 for stability) which would
310
+ # otherwise dtype-mismatch the downstream bf16 Linear (out_filter_dense).
311
+ input_dtype = u.dtype
312
+
313
+ z_pre, fir_state = self.engine.step_fir(
314
+ u, inference_params.fir_state_dict[self.layer_idx],
315
+ weight=self.short_filter_weight, bias=self.short_filter_bias,
316
+ )
317
+ inference_params.fir_state_dict[self.layer_idx] = fir_state
318
+
319
+ if self.config.interleave:
320
+ # For a single step, dim-1 length is C and `_interleave` operates
321
+ # along the channel dim; reshape to (B, C, 1) so the dim==3 branch
322
+ # applies, then squeeze the last dim back.
323
+ z_pre = _interleave(z_pre.unsqueeze(-1)).squeeze(-1)
324
+
325
+ if self.column_split_hyena:
326
+ x2, x1, v = _column_split_step(
327
+ z_pre, self.num_attention_heads, self.hidden_size_per_attention_head
328
+ )
329
+ else:
330
+ x2, x1, v = z_pre.split([self.hidden_size, self.hidden_size, self.hidden_size], dim=1)
331
+
332
+ if self.hyena_flip_x1x2:
333
+ x1, x2 = x2, x1
334
+
335
+ if self.fir_inner_filter_length is not None:
336
+ if self.hyena_filter_groups > 1:
337
+ h = self.h.repeat_interleave(self.hidden_size // self.hyena_filter_groups, 0)
338
+ else:
339
+ h = self.h
340
+ y, fir_inner_state = self.engine.step_fir(
341
+ x1 * v,
342
+ inference_params.fir_inner_state_dict[self.layer_idx],
343
+ weight=h, bias=self.D,
344
+ flip_filter=self.fir_inner_filter_length >= 128,
345
+ gated_bias=self.fir_inner_filter_length >= 128,
346
+ )
347
+ y = y * x2
348
+ inference_params.fir_inner_state_dict[self.layer_idx] = fir_inner_state
349
+ else:
350
+ y, iir_state = self.engine.step_iir(
351
+ x2, x1, v, self.D,
352
+ self.residues, self.log_poles,
353
+ inference_params.state_dict[self.layer_idx],
354
+ iir_groups=1,
355
+ )
356
+ inference_params.state_dict[self.layer_idx] = iir_state
357
+
358
+ y = y.to(input_dtype)
359
+ return y[:, None], inference_params
360
+
361
+ def update_time(self, L, device):
362
+ if self.t is None:
363
+ self.t = torch.arange(L, device=device)[None, None]
364
+ elif self.t.shape[-1] < L:
365
+ self.t = torch.arange(L, device=device)[None, None]
366
+ else:
367
+ self.t = self.t[..., :L]
368
+
369
+ def compute_filter(self, L, device):
370
+ self.update_time(L, device)
371
+ filter_dtype = torch.float32
372
+ residues = self.residues.to(filter_dtype)
373
+ log_poles = self.log_poles.to(filter_dtype)
374
+ h = (residues[..., None] * (log_poles * self.t).exp()).sum(1)[None] # B, D, L
375
+ return h, filter_dtype, log_poles, residues
376
+
377
+
378
+ # =============================================================================
379
+ # Hyena block (wraps HyenaCascade with norms / projections / mlp)
380
+ # =============================================================================
381
+
382
+
383
+ class ParallelGatedConvBlock(nn.Module):
384
+ def __init__(
385
+ self,
386
+ config,
387
+ layer_idx: int,
388
+ hyena_filter_groups: int | None = None,
389
+ fir_inner_filter_length: int | None = None,
390
+ ) -> None:
391
+ super().__init__()
392
+ self.config = config
393
+ self.layer_idx = layer_idx
394
+ self.fir_inner_filter_length = fir_inner_filter_length
395
+ self.hyena_filter_groups = (
396
+ hyena_filter_groups if hyena_filter_groups is not None else config.hidden_size
397
+ )
398
+ dtype = AttentionBlock._resolve_dtype(config.get("hyena_block_dtype", "bfloat16"))
399
+ mlp_dtype = AttentionBlock._resolve_dtype(config.get("mlp_dtype", "bfloat16"))
400
+ self.pre_norm = RMSNorm(config).to(dtype=dtype)
401
+ self.post_norm = RMSNorm(config).to(dtype=dtype)
402
+ self.filter = HyenaCascade(
403
+ config, layer_idx,
404
+ hyena_filter_groups=self.hyena_filter_groups,
405
+ fir_inner_filter_length=fir_inner_filter_length,
406
+ ).to(dtype=dtype)
407
+
408
+ self.projections = TELinear(
409
+ config.hidden_size,
410
+ 3 * config.hidden_size,
411
+ bias=config.qkv_proj_bias,
412
+ init_method=torch.nn.init.xavier_uniform_,
413
+ use_fp8=config.get("use_fp8_input_projections", False),
414
+ )
415
+
416
+ self.out_filter_dense = nn.Linear(
417
+ config.hidden_size, config.hidden_size,
418
+ bias=config.hyena_out_proj_bias,
419
+ ).to(dtype)
420
+ self.mlp = ParallelGatedMLP(config, layer_idx).to(dtype=mlp_dtype)
421
+
422
+ def _pad_to_multiple(self, x: torch.Tensor, multiple: int = 16) -> torch.Tensor:
423
+ """Right-pad along seq dim to a multiple of `multiple` when FP8 input
424
+ projections are enabled. TE's FP8 path requires the product of all
425
+ dims except the last to be divisible by 8 (we use 16 to be safe).
426
+ No-op when FP8 is off."""
427
+ if not self.config.get("use_fp8_input_projections", False):
428
+ return x
429
+ seq_len = x.size(1)
430
+ pad_len = (multiple - (seq_len % multiple)) % multiple
431
+ if pad_len == 0:
432
+ return x
433
+ return F.pad(x, (0, 0, 0, pad_len))
434
+
435
+ def _proj_norm(self, x):
436
+ original_seq_len = x.size(1)
437
+ normalized = self.pre_norm(x)
438
+ normalized = self._pad_to_multiple(normalized)
439
+ with torch.cuda.device(x.device) if x.is_cuda else _nullctx():
440
+ projected = self.projections(normalized)
441
+ if isinstance(projected, tuple):
442
+ projected = projected[0]
443
+ # Slice back to original seq length if padding was added.
444
+ if projected.size(1) > original_seq_len:
445
+ projected = projected[:, :original_seq_len, :]
446
+ return projected
447
+
448
+ def forward(
449
+ self,
450
+ u: torch.Tensor,
451
+ inference_params=None,
452
+ padding_mask=None,
453
+ output_attentions: bool = False,
454
+ *args,
455
+ **kwargs,
456
+ ) -> Tuple[torch.Tensor, None]:
457
+ z = self._proj_norm(u)
458
+
459
+ if isinstance(padding_mask, torch.Tensor):
460
+ z = z * padding_mask[..., None]
461
+
462
+ z, inference_params = self.filter(
463
+ z, inference_params=inference_params, padding_mask=padding_mask,
464
+ )
465
+ z_in = self.out_filter_dense(z) + u
466
+
467
+ if isinstance(padding_mask, torch.Tensor):
468
+ z_in = z_in * padding_mask[..., None]
469
+
470
+ y = self.mlp(self.post_norm(z_in)) + z_in
471
+ # Hyena blocks have no attention matrix.
472
+ return y, None
473
+
474
+
475
+ class _nullctx:
476
+ def __enter__(self): return None
477
+ def __exit__(self, *a): return False
478
+
479
+
480
+ # =============================================================================
481
+ # Block dispatch
482
+ # =============================================================================
483
+
484
+
485
+ def get_block(config, layer_idx: int):
486
+ if layer_idx in config.attn_layer_idxs:
487
+ return AttentionBlock(config, layer_idx)
488
+ if layer_idx in config.hcl_layer_idxs:
489
+ return ParallelGatedConvBlock(
490
+ config, layer_idx,
491
+ hyena_filter_groups=config.hcl_filter_groups,
492
+ fir_inner_filter_length=None,
493
+ )
494
+ if layer_idx in config.hcm_layer_idxs:
495
+ return ParallelGatedConvBlock(
496
+ config, layer_idx,
497
+ hyena_filter_groups=config.hcm_filter_groups,
498
+ fir_inner_filter_length=config.hcm_filter_length,
499
+ )
500
+ if layer_idx in config.hcs_layer_idxs:
501
+ return ParallelGatedConvBlock(
502
+ config, layer_idx,
503
+ hyena_filter_groups=config.hcs_filter_groups,
504
+ fir_inner_filter_length=config.hcs_filter_length,
505
+ )
506
+ raise NotImplementedError(f"layer_idx {layer_idx} not in any block-type idxs")
507
+
508
+
509
+ def block_type_for_idx(config, layer_idx: int) -> str:
510
+ if layer_idx in config.attn_layer_idxs: return "mha"
511
+ if layer_idx in config.hcl_layer_idxs: return "hcl"
512
+ if layer_idx in config.hcm_layer_idxs: return "hcm"
513
+ if layer_idx in config.hcs_layer_idxs: return "hcs"
514
+ raise ValueError(f"block idx {layer_idx} not classified")
layers.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Basic layers for Evo2 (StripedHyena2).
2
+
3
+ * RMSNorm -- pre/post norm in each block
4
+ * ParallelGatedMLP -- GLU(act(l1(x)) * l2(x)) -> l3(...); evo2_style
5
+ activations replace `act` with Identity for layer > 0
6
+ * VocabParallelEmbedding -- single-process variant of vortex's
7
+ VocabParallelEmbedding so checkpoint keys match
8
+ * TELinear -- TransformerEngine FP8-capable Linear, with a
9
+ pure-PyTorch fallback when TE is not installed.
10
+ Used for the input QKV-like projections of
11
+ Hyena cascade blocks; required for 1B/20B/40B.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from typing import Callable
17
+
18
+ import torch
19
+ import torch.nn as nn
20
+ import torch.nn.functional as F
21
+ from torch import Tensor
22
+
23
+
24
+ try:
25
+ from transformer_engine.pytorch import Linear as _TELinearBase
26
+ from transformer_engine.common.recipe import Format, DelayedScaling
27
+ import transformer_engine.pytorch as te
28
+ HAS_TE = True
29
+ except ImportError:
30
+ HAS_TE = False
31
+
32
+
33
+ def grab_first_if_tuple(x):
34
+ if x.__class__.__name__ == "tuple":
35
+ return x[0]
36
+ return x
37
+
38
+
39
+ class RMSNorm(nn.Module):
40
+ def __init__(self, config):
41
+ super().__init__()
42
+ self.eps = config.eps
43
+ self.hidden_size = config.hidden_size
44
+ self.scale = nn.Parameter(torch.ones(self.hidden_size))
45
+ self.register_parameter("scale", self.scale)
46
+ self.use_flash_rmsnorm = config.get("use_flash_rmsnorm", False)
47
+ if self.use_flash_rmsnorm:
48
+ from flash_attn.ops.rms_norm import rms_norm as rmsnorm_func
49
+ self.rmsnorm_func = rmsnorm_func
50
+
51
+ def forward(self, x):
52
+ if self.use_flash_rmsnorm:
53
+ return self.rmsnorm_func(x, self.scale, self.eps)
54
+ y = x / (x.norm(2, dim=-1, keepdim=True) * self.hidden_size ** (-1.0 / 2) + self.eps)
55
+ return self.scale * y
56
+
57
+
58
+ class ParallelGatedMLP(nn.Module):
59
+ """GLU MLP. With evo2_style_activations=True, layer_idx > 0 uses Identity
60
+ in place of the gating activation; layer 0 keeps gelu."""
61
+
62
+ def __init__(self, config, layer_idx: int):
63
+ super().__init__()
64
+ self.layer_idx = layer_idx
65
+ multiple_of = config.get("inner_size_multiple_of", 64)
66
+ self.act_type = config.get("mlp_activation", "gelu")
67
+ if self.act_type == "gelu":
68
+ self.act = F.gelu
69
+ elif self.act_type == "silu":
70
+ self.act = F.silu
71
+ else:
72
+ raise NotImplementedError(f"Unknown mlp_activation: {self.act_type}")
73
+
74
+ if self.layer_idx > 0 and config.get("evo2_style_activations", False):
75
+ self.act = nn.Identity()
76
+
77
+ self.multiple_of = multiple_of * config.model_parallel_size
78
+ inner_size = int(2 * config.hidden_size * 4 / 3)
79
+ inner_size = self.multiple_of * ((inner_size + self.multiple_of - 1) // self.multiple_of)
80
+ if config.get("inner_mlp_size", None) is not None:
81
+ inner_size = config.inner_mlp_size
82
+
83
+ self.l1 = nn.Linear(config.hidden_size, inner_size, bias=False)
84
+ self.l2 = nn.Linear(config.hidden_size, inner_size, bias=False)
85
+ self.l3 = nn.Linear(inner_size, config.hidden_size, bias=False)
86
+
87
+ def forward(self, z):
88
+ z1, z2 = self.l1(z), self.l2(z)
89
+ z1, z2 = grab_first_if_tuple(z1), grab_first_if_tuple(z2)
90
+ y = self.l3(self.act(z1) * z2)
91
+ return grab_first_if_tuple(y)
92
+
93
+
94
+ class VocabParallelEmbedding(nn.Embedding):
95
+ """Single-process variant of vortex's VocabParallelEmbedding.
96
+
97
+ Drops distributed sharding so this minimal port runs on a single device
98
+ (HF/accelerate handles cross-device placement via device_map="auto").
99
+ """
100
+
101
+ def __init__(self, config):
102
+ vocab_size = config.vocab_size
103
+ padding_idx = config.get("padding_idx", None)
104
+ super().__init__(vocab_size, embedding_dim=config.hidden_size, padding_idx=padding_idx)
105
+
106
+ def embed(self, x: Tensor) -> Tensor:
107
+ return self.forward(x)
108
+
109
+ def unembed(self, u: Tensor) -> Tensor:
110
+ return u @ self.weight.T
111
+
112
+
113
+ if HAS_TE:
114
+
115
+ _TE_WORKSPACE_FIXUP_APPLIED = False
116
+
117
+ def fixup_te_workspace():
118
+ """Patch TE's Linear module to use per-device cuBLAS workspaces.
119
+
120
+ Vortex helper, ported verbatim. Without this, TE's Linear uses a single
121
+ workspace tensor allocated on whatever CUDA device was current at first
122
+ call, then fails ("cuBLAS Error: the function failed to launch on the
123
+ GPU") when called from layers on other devices -- which happens with
124
+ device_map="auto" sharding. Idempotent; safe to call multiple times.
125
+ No-op when TE is not installed (the import guard above gates it).
126
+ """
127
+ global _TE_WORKSPACE_FIXUP_APPLIED
128
+ if _TE_WORKSPACE_FIXUP_APPLIED:
129
+ return
130
+ from functools import lru_cache
131
+
132
+ @lru_cache
133
+ def te_cublas_get_workspace_per_device(device):
134
+ import transformer_engine.pytorch.module.base as tebase
135
+ with torch.cuda.device(device):
136
+ tebase._cublas_workspace = None # force get_workspace() to reallocate
137
+ return tebase.get_workspace()
138
+
139
+ def get_workspace():
140
+ return te_cublas_get_workspace_per_device(torch.cuda.current_device())
141
+
142
+ import transformer_engine.pytorch.module.linear as telinear
143
+ telinear.get_workspace = get_workspace
144
+ _TE_WORKSPACE_FIXUP_APPLIED = True
145
+
146
+ def set_format_recipe():
147
+ fp8_format = Format.HYBRID
148
+ fp8_recipe = DelayedScaling(fp8_format=fp8_format, amax_history_len=16, amax_compute_algo="max")
149
+ return fp8_format, fp8_recipe
150
+
151
+ class TELinear(_TELinearBase):
152
+ """Wrapper for Transformer-Engine's Linear, matching vortex's signature.
153
+
154
+ Returns (out, None) so callers can grab_first_if_tuple, matching vortex.
155
+ """
156
+
157
+ def __init__(
158
+ self,
159
+ input_size: int,
160
+ output_size: int,
161
+ init_method: Callable | None = None,
162
+ bias: bool = True,
163
+ skip_bias_add: bool = False,
164
+ use_fp8: bool = False,
165
+ **kwargs,
166
+ ):
167
+ params_dtype = torch.bfloat16
168
+ self.te_return_bias = skip_bias_add and bias
169
+ self.use_fp8_input_projections = use_fp8
170
+ if use_fp8:
171
+ self.fp8_format, self.fp8_recipe = set_format_recipe()
172
+
173
+ super().__init__(
174
+ in_features=input_size,
175
+ out_features=output_size,
176
+ sequence_parallel=False,
177
+ fuse_wgrad_accumulation=False,
178
+ tp_group=None,
179
+ tp_size=1,
180
+ init_method=init_method,
181
+ params_dtype=params_dtype,
182
+ parallel_mode=None,
183
+ bias=bias,
184
+ return_bias=self.te_return_bias,
185
+ **kwargs,
186
+ )
187
+
188
+ def forward(self, x):
189
+ if self.use_fp8_input_projections:
190
+ with te.fp8_autocast(enabled=True, fp8_recipe=self.fp8_recipe):
191
+ out = super().forward(x)
192
+ else:
193
+ out = super().forward(x)
194
+
195
+ if self.te_return_bias:
196
+ return out
197
+ return out, None
198
+
199
+ else:
200
+
201
+ def fixup_te_workspace():
202
+ """No-op when TransformerEngine is not installed."""
203
+ return
204
+
205
+ class TELinear(nn.Module):
206
+ """Pure-PyTorch fallback for TELinear (no FP8). Used by 7B variants
207
+ when TransformerEngine isn't installed.
208
+
209
+ Parameters are registered with names that match TE's state_dict layout
210
+ (`weight`, `bias`), so checkpoints saved from either path are
211
+ cross-loadable.
212
+ """
213
+
214
+ def __init__(
215
+ self,
216
+ input_size: int,
217
+ output_size: int,
218
+ init_method: Callable | None = None,
219
+ bias: bool = True,
220
+ skip_bias_add: bool = False,
221
+ use_fp8: bool = False,
222
+ **kwargs,
223
+ ):
224
+ super().__init__()
225
+ if use_fp8:
226
+ raise RuntimeError(
227
+ "FP8 requires Transformer Engine, which is not installed. "
228
+ "Install it with: pip install transformer_engine>=2.3.0"
229
+ )
230
+
231
+ self.te_return_bias = skip_bias_add and bias
232
+ self.use_fp8_input_projections = False
233
+ self.in_features = input_size
234
+ self.out_features = output_size
235
+ self.has_bias = bias
236
+
237
+ self.weight = nn.Parameter(torch.empty(output_size, input_size, dtype=torch.bfloat16))
238
+ if bias:
239
+ self.bias = nn.Parameter(torch.zeros(output_size, dtype=torch.bfloat16))
240
+ else:
241
+ self.register_parameter("bias", None)
242
+
243
+ if init_method is not None:
244
+ init_method(self.weight)
245
+ else:
246
+ nn.init.xavier_uniform_(self.weight)
247
+
248
+ def forward(self, x):
249
+ out = F.linear(x.to(self.weight.dtype), self.weight, self.bias)
250
+ if self.te_return_bias:
251
+ return out, self.bias
252
+ return out, None
model-00001-of-00008.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:43c2525175c10d90a7afe77140a65687c333550f41d1a8066dae6c8d14431cdf
3
+ size 4942736312
model-00002-of-00008.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fd1c81d89a45f7cf175ce98ff323c2406d194c77542a144ac16b9d22fc5bd8ee
3
+ size 4933102024
model-00003-of-00008.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:26367dc8f5aa79e833e751fb868179dc87902e4854b0ebca8b289a76ec4e77eb
3
+ size 4934314600
model-00004-of-00008.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:090f52f30d97f2117a77e071e7fcf532abb1947c36be844748126b1b514d5a9d
3
+ size 4934019656
model-00005-of-00008.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:aac8ff6809416a958fa61e7a331a64d33ff793d5c394f39dfc5cd2ed0a0bea78
3
+ size 4934314624
model-00006-of-00008.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f702c461953f8500b1e3b3dfb11422114791e4dc40b2b306c9b6c85d6414156e
3
+ size 4934160048
model-00007-of-00008.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4d7218c60984c30227c3afdc088f14e8a5c527cfe03b10520d81c9dc05bc9c0f
3
+ size 4934314632
model-00008-of-00008.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c959bcb67ea324aadc1e5edec231052dfe988d8a32f8225b96a4791c39a75bf2
3
+ size 4934298120
model.safetensors.index.json ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "total_parameters": 19738780160,
4
+ "total_size": 39481230720
5
+ },
6
+ "weight_map": {
7
+ "backbone.blocks.0.filter.h": "model-00001-of-00008.safetensors",
8
+ "backbone.blocks.0.filter.short_filter_weight": "model-00001-of-00008.safetensors",
9
+ "backbone.blocks.0.mlp.l1.weight": "model-00001-of-00008.safetensors",
10
+ "backbone.blocks.0.mlp.l2.weight": "model-00001-of-00008.safetensors",
11
+ "backbone.blocks.0.mlp.l3.weight": "model-00001-of-00008.safetensors",
12
+ "backbone.blocks.0.out_filter_dense.bias": "model-00001-of-00008.safetensors",
13
+ "backbone.blocks.0.out_filter_dense.weight": "model-00001-of-00008.safetensors",
14
+ "backbone.blocks.0.post_norm.scale": "model-00001-of-00008.safetensors",
15
+ "backbone.blocks.0.pre_norm.scale": "model-00001-of-00008.safetensors",
16
+ "backbone.blocks.0.projections.weight": "model-00001-of-00008.safetensors",
17
+ "backbone.blocks.1.filter.D": "model-00001-of-00008.safetensors",
18
+ "backbone.blocks.1.filter.h": "model-00001-of-00008.safetensors",
19
+ "backbone.blocks.1.filter.short_filter_weight": "model-00001-of-00008.safetensors",
20
+ "backbone.blocks.1.mlp.l1.weight": "model-00001-of-00008.safetensors",
21
+ "backbone.blocks.1.mlp.l2.weight": "model-00001-of-00008.safetensors",
22
+ "backbone.blocks.1.mlp.l3.weight": "model-00001-of-00008.safetensors",
23
+ "backbone.blocks.1.out_filter_dense.bias": "model-00001-of-00008.safetensors",
24
+ "backbone.blocks.1.out_filter_dense.weight": "model-00001-of-00008.safetensors",
25
+ "backbone.blocks.1.post_norm.scale": "model-00001-of-00008.safetensors",
26
+ "backbone.blocks.1.pre_norm.scale": "model-00001-of-00008.safetensors",
27
+ "backbone.blocks.1.projections.weight": "model-00001-of-00008.safetensors",
28
+ "backbone.blocks.10.inner_mha_cls.Wqkv.weight": "model-00004-of-00008.safetensors",
29
+ "backbone.blocks.10.inner_mha_cls.out_proj.bias": "model-00004-of-00008.safetensors",
30
+ "backbone.blocks.10.inner_mha_cls.out_proj.weight": "model-00004-of-00008.safetensors",
31
+ "backbone.blocks.10.inner_mha_cls.rotary_emb.inv_freq": "model-00004-of-00008.safetensors",
32
+ "backbone.blocks.10.mlp.l1.weight": "model-00004-of-00008.safetensors",
33
+ "backbone.blocks.10.mlp.l2.weight": "model-00004-of-00008.safetensors",
34
+ "backbone.blocks.10.mlp.l3.weight": "model-00004-of-00008.safetensors",
35
+ "backbone.blocks.10.post_norm.scale": "model-00004-of-00008.safetensors",
36
+ "backbone.blocks.10.pre_norm.scale": "model-00004-of-00008.safetensors",
37
+ "backbone.blocks.11.filter.h": "model-00004-of-00008.safetensors",
38
+ "backbone.blocks.11.filter.short_filter_weight": "model-00004-of-00008.safetensors",
39
+ "backbone.blocks.11.mlp.l1.weight": "model-00004-of-00008.safetensors",
40
+ "backbone.blocks.11.mlp.l2.weight": "model-00004-of-00008.safetensors",
41
+ "backbone.blocks.11.mlp.l3.weight": "model-00004-of-00008.safetensors",
42
+ "backbone.blocks.11.out_filter_dense.bias": "model-00004-of-00008.safetensors",
43
+ "backbone.blocks.11.out_filter_dense.weight": "model-00004-of-00008.safetensors",
44
+ "backbone.blocks.11.post_norm.scale": "model-00004-of-00008.safetensors",
45
+ "backbone.blocks.11.pre_norm.scale": "model-00004-of-00008.safetensors",
46
+ "backbone.blocks.11.projections.weight": "model-00004-of-00008.safetensors",
47
+ "backbone.blocks.12.filter.D": "model-00005-of-00008.safetensors",
48
+ "backbone.blocks.12.filter.h": "model-00005-of-00008.safetensors",
49
+ "backbone.blocks.12.filter.short_filter_weight": "model-00005-of-00008.safetensors",
50
+ "backbone.blocks.12.mlp.l1.weight": "model-00005-of-00008.safetensors",
51
+ "backbone.blocks.12.mlp.l2.weight": "model-00005-of-00008.safetensors",
52
+ "backbone.blocks.12.mlp.l3.weight": "model-00005-of-00008.safetensors",
53
+ "backbone.blocks.12.out_filter_dense.bias": "model-00004-of-00008.safetensors",
54
+ "backbone.blocks.12.out_filter_dense.weight": "model-00005-of-00008.safetensors",
55
+ "backbone.blocks.12.post_norm.scale": "model-00005-of-00008.safetensors",
56
+ "backbone.blocks.12.pre_norm.scale": "model-00004-of-00008.safetensors",
57
+ "backbone.blocks.12.projections.weight": "model-00005-of-00008.safetensors",
58
+ "backbone.blocks.13.filter.D": "model-00005-of-00008.safetensors",
59
+ "backbone.blocks.13.filter.log_poles": "model-00005-of-00008.safetensors",
60
+ "backbone.blocks.13.filter.residues": "model-00005-of-00008.safetensors",
61
+ "backbone.blocks.13.filter.short_filter_weight": "model-00005-of-00008.safetensors",
62
+ "backbone.blocks.13.mlp.l1.weight": "model-00005-of-00008.safetensors",
63
+ "backbone.blocks.13.mlp.l2.weight": "model-00005-of-00008.safetensors",
64
+ "backbone.blocks.13.mlp.l3.weight": "model-00005-of-00008.safetensors",
65
+ "backbone.blocks.13.out_filter_dense.bias": "model-00005-of-00008.safetensors",
66
+ "backbone.blocks.13.out_filter_dense.weight": "model-00005-of-00008.safetensors",
67
+ "backbone.blocks.13.post_norm.scale": "model-00005-of-00008.safetensors",
68
+ "backbone.blocks.13.pre_norm.scale": "model-00005-of-00008.safetensors",
69
+ "backbone.blocks.13.projections.weight": "model-00005-of-00008.safetensors",
70
+ "backbone.blocks.14.filter.h": "model-00005-of-00008.safetensors",
71
+ "backbone.blocks.14.filter.short_filter_weight": "model-00005-of-00008.safetensors",
72
+ "backbone.blocks.14.mlp.l1.weight": "model-00005-of-00008.safetensors",
73
+ "backbone.blocks.14.mlp.l2.weight": "model-00005-of-00008.safetensors",
74
+ "backbone.blocks.14.mlp.l3.weight": "model-00005-of-00008.safetensors",
75
+ "backbone.blocks.14.out_filter_dense.bias": "model-00005-of-00008.safetensors",
76
+ "backbone.blocks.14.out_filter_dense.weight": "model-00005-of-00008.safetensors",
77
+ "backbone.blocks.14.post_norm.scale": "model-00005-of-00008.safetensors",
78
+ "backbone.blocks.14.pre_norm.scale": "model-00005-of-00008.safetensors",
79
+ "backbone.blocks.14.projections.weight": "model-00005-of-00008.safetensors",
80
+ "backbone.blocks.15.filter.D": "model-00006-of-00008.safetensors",
81
+ "backbone.blocks.15.filter.h": "model-00006-of-00008.safetensors",
82
+ "backbone.blocks.15.filter.short_filter_weight": "model-00006-of-00008.safetensors",
83
+ "backbone.blocks.15.mlp.l1.weight": "model-00006-of-00008.safetensors",
84
+ "backbone.blocks.15.mlp.l2.weight": "model-00006-of-00008.safetensors",
85
+ "backbone.blocks.15.mlp.l3.weight": "model-00006-of-00008.safetensors",
86
+ "backbone.blocks.15.out_filter_dense.bias": "model-00005-of-00008.safetensors",
87
+ "backbone.blocks.15.out_filter_dense.weight": "model-00006-of-00008.safetensors",
88
+ "backbone.blocks.15.post_norm.scale": "model-00006-of-00008.safetensors",
89
+ "backbone.blocks.15.pre_norm.scale": "model-00005-of-00008.safetensors",
90
+ "backbone.blocks.15.projections.weight": "model-00006-of-00008.safetensors",
91
+ "backbone.blocks.16.filter.D": "model-00006-of-00008.safetensors",
92
+ "backbone.blocks.16.filter.log_poles": "model-00006-of-00008.safetensors",
93
+ "backbone.blocks.16.filter.residues": "model-00006-of-00008.safetensors",
94
+ "backbone.blocks.16.filter.short_filter_weight": "model-00006-of-00008.safetensors",
95
+ "backbone.blocks.16.mlp.l1.weight": "model-00006-of-00008.safetensors",
96
+ "backbone.blocks.16.mlp.l2.weight": "model-00006-of-00008.safetensors",
97
+ "backbone.blocks.16.mlp.l3.weight": "model-00006-of-00008.safetensors",
98
+ "backbone.blocks.16.out_filter_dense.bias": "model-00006-of-00008.safetensors",
99
+ "backbone.blocks.16.out_filter_dense.weight": "model-00006-of-00008.safetensors",
100
+ "backbone.blocks.16.post_norm.scale": "model-00006-of-00008.safetensors",
101
+ "backbone.blocks.16.pre_norm.scale": "model-00006-of-00008.safetensors",
102
+ "backbone.blocks.16.projections.weight": "model-00006-of-00008.safetensors",
103
+ "backbone.blocks.17.inner_mha_cls.Wqkv.weight": "model-00006-of-00008.safetensors",
104
+ "backbone.blocks.17.inner_mha_cls.out_proj.bias": "model-00006-of-00008.safetensors",
105
+ "backbone.blocks.17.inner_mha_cls.out_proj.weight": "model-00006-of-00008.safetensors",
106
+ "backbone.blocks.17.inner_mha_cls.rotary_emb.inv_freq": "model-00006-of-00008.safetensors",
107
+ "backbone.blocks.17.mlp.l1.weight": "model-00006-of-00008.safetensors",
108
+ "backbone.blocks.17.mlp.l2.weight": "model-00006-of-00008.safetensors",
109
+ "backbone.blocks.17.mlp.l3.weight": "model-00006-of-00008.safetensors",
110
+ "backbone.blocks.17.post_norm.scale": "model-00006-of-00008.safetensors",
111
+ "backbone.blocks.17.pre_norm.scale": "model-00006-of-00008.safetensors",
112
+ "backbone.blocks.18.filter.h": "model-00007-of-00008.safetensors",
113
+ "backbone.blocks.18.filter.short_filter_weight": "model-00007-of-00008.safetensors",
114
+ "backbone.blocks.18.mlp.l1.weight": "model-00007-of-00008.safetensors",
115
+ "backbone.blocks.18.mlp.l2.weight": "model-00007-of-00008.safetensors",
116
+ "backbone.blocks.18.mlp.l3.weight": "model-00007-of-00008.safetensors",
117
+ "backbone.blocks.18.out_filter_dense.bias": "model-00006-of-00008.safetensors",
118
+ "backbone.blocks.18.out_filter_dense.weight": "model-00007-of-00008.safetensors",
119
+ "backbone.blocks.18.post_norm.scale": "model-00007-of-00008.safetensors",
120
+ "backbone.blocks.18.pre_norm.scale": "model-00006-of-00008.safetensors",
121
+ "backbone.blocks.18.projections.weight": "model-00007-of-00008.safetensors",
122
+ "backbone.blocks.19.filter.D": "model-00007-of-00008.safetensors",
123
+ "backbone.blocks.19.filter.h": "model-00007-of-00008.safetensors",
124
+ "backbone.blocks.19.filter.short_filter_weight": "model-00007-of-00008.safetensors",
125
+ "backbone.blocks.19.mlp.l1.weight": "model-00007-of-00008.safetensors",
126
+ "backbone.blocks.19.mlp.l2.weight": "model-00007-of-00008.safetensors",
127
+ "backbone.blocks.19.mlp.l3.weight": "model-00007-of-00008.safetensors",
128
+ "backbone.blocks.19.out_filter_dense.bias": "model-00007-of-00008.safetensors",
129
+ "backbone.blocks.19.out_filter_dense.weight": "model-00007-of-00008.safetensors",
130
+ "backbone.blocks.19.post_norm.scale": "model-00007-of-00008.safetensors",
131
+ "backbone.blocks.19.pre_norm.scale": "model-00007-of-00008.safetensors",
132
+ "backbone.blocks.19.projections.weight": "model-00007-of-00008.safetensors",
133
+ "backbone.blocks.2.filter.D": "model-00001-of-00008.safetensors",
134
+ "backbone.blocks.2.filter.log_poles": "model-00001-of-00008.safetensors",
135
+ "backbone.blocks.2.filter.residues": "model-00001-of-00008.safetensors",
136
+ "backbone.blocks.2.filter.short_filter_weight": "model-00001-of-00008.safetensors",
137
+ "backbone.blocks.2.mlp.l1.weight": "model-00001-of-00008.safetensors",
138
+ "backbone.blocks.2.mlp.l2.weight": "model-00001-of-00008.safetensors",
139
+ "backbone.blocks.2.mlp.l3.weight": "model-00001-of-00008.safetensors",
140
+ "backbone.blocks.2.out_filter_dense.bias": "model-00001-of-00008.safetensors",
141
+ "backbone.blocks.2.out_filter_dense.weight": "model-00001-of-00008.safetensors",
142
+ "backbone.blocks.2.post_norm.scale": "model-00001-of-00008.safetensors",
143
+ "backbone.blocks.2.pre_norm.scale": "model-00001-of-00008.safetensors",
144
+ "backbone.blocks.2.projections.weight": "model-00001-of-00008.safetensors",
145
+ "backbone.blocks.20.filter.D": "model-00007-of-00008.safetensors",
146
+ "backbone.blocks.20.filter.log_poles": "model-00007-of-00008.safetensors",
147
+ "backbone.blocks.20.filter.residues": "model-00007-of-00008.safetensors",
148
+ "backbone.blocks.20.filter.short_filter_weight": "model-00007-of-00008.safetensors",
149
+ "backbone.blocks.20.mlp.l1.weight": "model-00007-of-00008.safetensors",
150
+ "backbone.blocks.20.mlp.l2.weight": "model-00007-of-00008.safetensors",
151
+ "backbone.blocks.20.mlp.l3.weight": "model-00007-of-00008.safetensors",
152
+ "backbone.blocks.20.out_filter_dense.bias": "model-00007-of-00008.safetensors",
153
+ "backbone.blocks.20.out_filter_dense.weight": "model-00007-of-00008.safetensors",
154
+ "backbone.blocks.20.post_norm.scale": "model-00007-of-00008.safetensors",
155
+ "backbone.blocks.20.pre_norm.scale": "model-00007-of-00008.safetensors",
156
+ "backbone.blocks.20.projections.weight": "model-00007-of-00008.safetensors",
157
+ "backbone.blocks.21.filter.h": "model-00008-of-00008.safetensors",
158
+ "backbone.blocks.21.filter.short_filter_weight": "model-00008-of-00008.safetensors",
159
+ "backbone.blocks.21.mlp.l1.weight": "model-00008-of-00008.safetensors",
160
+ "backbone.blocks.21.mlp.l2.weight": "model-00008-of-00008.safetensors",
161
+ "backbone.blocks.21.mlp.l3.weight": "model-00008-of-00008.safetensors",
162
+ "backbone.blocks.21.out_filter_dense.bias": "model-00007-of-00008.safetensors",
163
+ "backbone.blocks.21.out_filter_dense.weight": "model-00008-of-00008.safetensors",
164
+ "backbone.blocks.21.post_norm.scale": "model-00008-of-00008.safetensors",
165
+ "backbone.blocks.21.pre_norm.scale": "model-00007-of-00008.safetensors",
166
+ "backbone.blocks.21.projections.weight": "model-00008-of-00008.safetensors",
167
+ "backbone.blocks.22.filter.D": "model-00008-of-00008.safetensors",
168
+ "backbone.blocks.22.filter.h": "model-00008-of-00008.safetensors",
169
+ "backbone.blocks.22.filter.short_filter_weight": "model-00008-of-00008.safetensors",
170
+ "backbone.blocks.22.mlp.l1.weight": "model-00008-of-00008.safetensors",
171
+ "backbone.blocks.22.mlp.l2.weight": "model-00008-of-00008.safetensors",
172
+ "backbone.blocks.22.mlp.l3.weight": "model-00008-of-00008.safetensors",
173
+ "backbone.blocks.22.out_filter_dense.bias": "model-00008-of-00008.safetensors",
174
+ "backbone.blocks.22.out_filter_dense.weight": "model-00008-of-00008.safetensors",
175
+ "backbone.blocks.22.post_norm.scale": "model-00008-of-00008.safetensors",
176
+ "backbone.blocks.22.pre_norm.scale": "model-00008-of-00008.safetensors",
177
+ "backbone.blocks.22.projections.weight": "model-00008-of-00008.safetensors",
178
+ "backbone.blocks.23.filter.D": "model-00008-of-00008.safetensors",
179
+ "backbone.blocks.23.filter.log_poles": "model-00008-of-00008.safetensors",
180
+ "backbone.blocks.23.filter.residues": "model-00008-of-00008.safetensors",
181
+ "backbone.blocks.23.filter.short_filter_weight": "model-00008-of-00008.safetensors",
182
+ "backbone.blocks.23.mlp.l1.weight": "model-00008-of-00008.safetensors",
183
+ "backbone.blocks.23.mlp.l2.weight": "model-00008-of-00008.safetensors",
184
+ "backbone.blocks.23.mlp.l3.weight": "model-00008-of-00008.safetensors",
185
+ "backbone.blocks.23.out_filter_dense.bias": "model-00008-of-00008.safetensors",
186
+ "backbone.blocks.23.out_filter_dense.weight": "model-00008-of-00008.safetensors",
187
+ "backbone.blocks.23.post_norm.scale": "model-00008-of-00008.safetensors",
188
+ "backbone.blocks.23.pre_norm.scale": "model-00008-of-00008.safetensors",
189
+ "backbone.blocks.23.projections.weight": "model-00008-of-00008.safetensors",
190
+ "backbone.blocks.3.inner_mha_cls.Wqkv.weight": "model-00002-of-00008.safetensors",
191
+ "backbone.blocks.3.inner_mha_cls.out_proj.bias": "model-00001-of-00008.safetensors",
192
+ "backbone.blocks.3.inner_mha_cls.out_proj.weight": "model-00002-of-00008.safetensors",
193
+ "backbone.blocks.3.inner_mha_cls.rotary_emb.inv_freq": "model-00002-of-00008.safetensors",
194
+ "backbone.blocks.3.mlp.l1.weight": "model-00002-of-00008.safetensors",
195
+ "backbone.blocks.3.mlp.l2.weight": "model-00002-of-00008.safetensors",
196
+ "backbone.blocks.3.mlp.l3.weight": "model-00002-of-00008.safetensors",
197
+ "backbone.blocks.3.post_norm.scale": "model-00002-of-00008.safetensors",
198
+ "backbone.blocks.3.pre_norm.scale": "model-00001-of-00008.safetensors",
199
+ "backbone.blocks.4.filter.h": "model-00002-of-00008.safetensors",
200
+ "backbone.blocks.4.filter.short_filter_weight": "model-00002-of-00008.safetensors",
201
+ "backbone.blocks.4.mlp.l1.weight": "model-00002-of-00008.safetensors",
202
+ "backbone.blocks.4.mlp.l2.weight": "model-00002-of-00008.safetensors",
203
+ "backbone.blocks.4.mlp.l3.weight": "model-00002-of-00008.safetensors",
204
+ "backbone.blocks.4.out_filter_dense.bias": "model-00002-of-00008.safetensors",
205
+ "backbone.blocks.4.out_filter_dense.weight": "model-00002-of-00008.safetensors",
206
+ "backbone.blocks.4.post_norm.scale": "model-00002-of-00008.safetensors",
207
+ "backbone.blocks.4.pre_norm.scale": "model-00002-of-00008.safetensors",
208
+ "backbone.blocks.4.projections.weight": "model-00002-of-00008.safetensors",
209
+ "backbone.blocks.5.filter.D": "model-00002-of-00008.safetensors",
210
+ "backbone.blocks.5.filter.h": "model-00002-of-00008.safetensors",
211
+ "backbone.blocks.5.filter.short_filter_weight": "model-00002-of-00008.safetensors",
212
+ "backbone.blocks.5.mlp.l1.weight": "model-00002-of-00008.safetensors",
213
+ "backbone.blocks.5.mlp.l2.weight": "model-00002-of-00008.safetensors",
214
+ "backbone.blocks.5.mlp.l3.weight": "model-00002-of-00008.safetensors",
215
+ "backbone.blocks.5.out_filter_dense.bias": "model-00002-of-00008.safetensors",
216
+ "backbone.blocks.5.out_filter_dense.weight": "model-00002-of-00008.safetensors",
217
+ "backbone.blocks.5.post_norm.scale": "model-00002-of-00008.safetensors",
218
+ "backbone.blocks.5.pre_norm.scale": "model-00002-of-00008.safetensors",
219
+ "backbone.blocks.5.projections.weight": "model-00002-of-00008.safetensors",
220
+ "backbone.blocks.6.filter.D": "model-00003-of-00008.safetensors",
221
+ "backbone.blocks.6.filter.log_poles": "model-00003-of-00008.safetensors",
222
+ "backbone.blocks.6.filter.residues": "model-00003-of-00008.safetensors",
223
+ "backbone.blocks.6.filter.short_filter_weight": "model-00003-of-00008.safetensors",
224
+ "backbone.blocks.6.mlp.l1.weight": "model-00003-of-00008.safetensors",
225
+ "backbone.blocks.6.mlp.l2.weight": "model-00003-of-00008.safetensors",
226
+ "backbone.blocks.6.mlp.l3.weight": "model-00003-of-00008.safetensors",
227
+ "backbone.blocks.6.out_filter_dense.bias": "model-00002-of-00008.safetensors",
228
+ "backbone.blocks.6.out_filter_dense.weight": "model-00003-of-00008.safetensors",
229
+ "backbone.blocks.6.post_norm.scale": "model-00003-of-00008.safetensors",
230
+ "backbone.blocks.6.pre_norm.scale": "model-00002-of-00008.safetensors",
231
+ "backbone.blocks.6.projections.weight": "model-00003-of-00008.safetensors",
232
+ "backbone.blocks.7.filter.h": "model-00003-of-00008.safetensors",
233
+ "backbone.blocks.7.filter.short_filter_weight": "model-00003-of-00008.safetensors",
234
+ "backbone.blocks.7.mlp.l1.weight": "model-00003-of-00008.safetensors",
235
+ "backbone.blocks.7.mlp.l2.weight": "model-00003-of-00008.safetensors",
236
+ "backbone.blocks.7.mlp.l3.weight": "model-00003-of-00008.safetensors",
237
+ "backbone.blocks.7.out_filter_dense.bias": "model-00003-of-00008.safetensors",
238
+ "backbone.blocks.7.out_filter_dense.weight": "model-00003-of-00008.safetensors",
239
+ "backbone.blocks.7.post_norm.scale": "model-00003-of-00008.safetensors",
240
+ "backbone.blocks.7.pre_norm.scale": "model-00003-of-00008.safetensors",
241
+ "backbone.blocks.7.projections.weight": "model-00003-of-00008.safetensors",
242
+ "backbone.blocks.8.filter.D": "model-00003-of-00008.safetensors",
243
+ "backbone.blocks.8.filter.h": "model-00003-of-00008.safetensors",
244
+ "backbone.blocks.8.filter.short_filter_weight": "model-00003-of-00008.safetensors",
245
+ "backbone.blocks.8.mlp.l1.weight": "model-00003-of-00008.safetensors",
246
+ "backbone.blocks.8.mlp.l2.weight": "model-00003-of-00008.safetensors",
247
+ "backbone.blocks.8.mlp.l3.weight": "model-00003-of-00008.safetensors",
248
+ "backbone.blocks.8.out_filter_dense.bias": "model-00003-of-00008.safetensors",
249
+ "backbone.blocks.8.out_filter_dense.weight": "model-00003-of-00008.safetensors",
250
+ "backbone.blocks.8.post_norm.scale": "model-00003-of-00008.safetensors",
251
+ "backbone.blocks.8.pre_norm.scale": "model-00003-of-00008.safetensors",
252
+ "backbone.blocks.8.projections.weight": "model-00003-of-00008.safetensors",
253
+ "backbone.blocks.9.filter.D": "model-00004-of-00008.safetensors",
254
+ "backbone.blocks.9.filter.log_poles": "model-00004-of-00008.safetensors",
255
+ "backbone.blocks.9.filter.residues": "model-00004-of-00008.safetensors",
256
+ "backbone.blocks.9.filter.short_filter_weight": "model-00004-of-00008.safetensors",
257
+ "backbone.blocks.9.mlp.l1.weight": "model-00004-of-00008.safetensors",
258
+ "backbone.blocks.9.mlp.l2.weight": "model-00004-of-00008.safetensors",
259
+ "backbone.blocks.9.mlp.l3.weight": "model-00004-of-00008.safetensors",
260
+ "backbone.blocks.9.out_filter_dense.bias": "model-00003-of-00008.safetensors",
261
+ "backbone.blocks.9.out_filter_dense.weight": "model-00004-of-00008.safetensors",
262
+ "backbone.blocks.9.post_norm.scale": "model-00004-of-00008.safetensors",
263
+ "backbone.blocks.9.pre_norm.scale": "model-00003-of-00008.safetensors",
264
+ "backbone.blocks.9.projections.weight": "model-00004-of-00008.safetensors",
265
+ "backbone.embedding_layer.weight": "model-00001-of-00008.safetensors",
266
+ "backbone.norm.scale": "model-00008-of-00008.safetensors"
267
+ }
268
+ }
modeling_evo2.py ADDED
@@ -0,0 +1,482 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HuggingFace wrappers for Evo2 (StripedHyena2).
2
+
3
+ Two top-level model classes:
4
+
5
+ * Evo2Model -- bare backbone returning BaseModelOutputWithPast
6
+ (no LM head); the post-RMSNorm hidden state is the
7
+ last_hidden_state.
8
+ * Evo2ForCausalLM -- with the tied LM head + GenerationMixin so
9
+ model.generate() works out of the box.
10
+
11
+ Caching for autoregressive generation uses the custom Evo2Cache (see
12
+ cache.py): a dict-like container of four block-type-specific param objects
13
+ (mha, hcl, hcm, hcs) so all four StripedHyena2 block types can decode in
14
+ constant time per new token.
15
+
16
+ Per-block dtype: bfloat16 for everything except the modal-form filter
17
+ parameters (`log_poles`, `residues`) which must stay fp32 for stability.
18
+ This is enforced both at convert time (the safetensors store these as fp32)
19
+ and automatically after checkpoint loading.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ from typing import Optional, Tuple, Union
25
+
26
+ import torch
27
+ import torch.nn as nn
28
+ import torch.nn.functional as F
29
+ from transformers import PreTrainedModel
30
+ from transformers.generation import GenerationMixin
31
+ from transformers.modeling_outputs import (
32
+ BaseModelOutputWithPast,
33
+ CausalLMOutputWithPast,
34
+ )
35
+ from transformers.utils import logging
36
+
37
+ from .cache import Evo2Cache
38
+ from .configuration_evo2 import Evo2Config
39
+ from .hyena import (
40
+ AttentionBlock,
41
+ ParallelGatedConvBlock,
42
+ block_type_for_idx,
43
+ get_block,
44
+ )
45
+ from .layers import RMSNorm, VocabParallelEmbedding
46
+ # Bundle the tokenizer file via trust_remote_code:
47
+ from .tokenization_evo2 import ByteTokenizer # noqa: F401
48
+ # Force HF's trust_remote_code loader to copy these transitive deps into its
49
+ # dynamic-module cache (it only walks top-level `from .X import Y` patterns
50
+ # of modeling_evo2.py, not `from . import X`, so we use the explicit form).
51
+ from .attention import MHA as _MHA # noqa: F401
52
+ from .engine import HyenaInferenceEngine as _HyenaInferenceEngine # noqa: F401
53
+ from .rotary import ( # noqa: F401
54
+ RotaryEmbedding as _RotaryEmbedding,
55
+ restore_rotary_inv_freq,
56
+ )
57
+
58
+ logger = logging.get_logger(__name__)
59
+
60
+
61
+ # =============================================================================
62
+ # Backbone
63
+ # =============================================================================
64
+
65
+
66
+ class StripedHyena2(nn.Module):
67
+ """Pure backbone: token embedding -> N blocks -> final RMSNorm."""
68
+
69
+ def __init__(self, config: Evo2Config):
70
+ super().__init__()
71
+ # Patch TE's cuBLAS workspace handling so multi-GPU device_map="auto"
72
+ # works (TE otherwise allocates a single workspace on the first device
73
+ # it sees, then fails on layers placed on other devices). No-op when
74
+ # TE isn't installed.
75
+ from .layers import fixup_te_workspace
76
+ fixup_te_workspace()
77
+
78
+ self.config = config
79
+ self.embedding_layer = VocabParallelEmbedding(config)
80
+ self.norm = RMSNorm(config) if config.get("final_norm", True) else None
81
+
82
+ if config.get("use_flashfft", False):
83
+ import importlib
84
+ FlashFFTConv = importlib.import_module("flashfftconv").FlashFFTConv
85
+ self.flash_fft = FlashFFTConv(2 * config.max_seqlen, dtype=torch.bfloat16)
86
+ else:
87
+ self.flash_fft = None
88
+
89
+ self.blocks = nn.ModuleList(
90
+ get_block(config, i) for i in range(config.num_layers)
91
+ )
92
+ # Wire fftconv_fn into the hcl filters when flashfft is on.
93
+ if self.flash_fft is not None:
94
+ for block in self.blocks:
95
+ if isinstance(block, ParallelGatedConvBlock) and block.filter.h is None:
96
+ block.filter.fftconv_fn = self.flash_fft
97
+
98
+ def forward(
99
+ self,
100
+ input_ids: torch.LongTensor = None,
101
+ attention_mask: Optional[torch.LongTensor] = None,
102
+ past_key_values: Optional[Evo2Cache] = None,
103
+ use_cache: Optional[bool] = None,
104
+ output_attentions: Optional[bool] = None,
105
+ output_hidden_states: Optional[bool] = None,
106
+ return_dict: Optional[bool] = None,
107
+ ) -> Union[Tuple, BaseModelOutputWithPast]:
108
+ """Run the backbone using the standard HuggingFace base-model API."""
109
+ if input_ids is None:
110
+ raise ValueError("input_ids must be provided.")
111
+
112
+ return_dict = (
113
+ return_dict if return_dict is not None else self.config.use_return_dict
114
+ )
115
+ output_attentions = (
116
+ output_attentions
117
+ if output_attentions is not None
118
+ else self.config.output_attentions
119
+ )
120
+ output_hidden_states = (
121
+ output_hidden_states
122
+ if output_hidden_states is not None
123
+ else self.config.output_hidden_states
124
+ )
125
+ # The bare backbone defaults to no cache because embedding extraction
126
+ # does not benefit from autoregressive state.
127
+ use_cache = use_cache if use_cache is not None else False
128
+ if use_cache and self.training:
129
+ use_cache = False
130
+ if use_cache and past_key_values is None:
131
+ past_key_values = self.initialize_inference_params(
132
+ max_batch_size=input_ids.shape[0],
133
+ )
134
+
135
+ last_hidden, past_kv, hidden_states, attentions = self._forward_features(
136
+ input_ids,
137
+ inference_params_dict=past_key_values if use_cache else None,
138
+ attention_mask=attention_mask,
139
+ output_hidden_states=output_hidden_states,
140
+ output_attentions=output_attentions,
141
+ )
142
+ if use_cache:
143
+ past_kv.advance(input_ids.shape[-1])
144
+
145
+ if not return_dict:
146
+ outputs = (last_hidden,)
147
+ if use_cache:
148
+ outputs += (past_kv,)
149
+ if output_hidden_states:
150
+ outputs += (tuple(hidden_states),)
151
+ if output_attentions:
152
+ outputs += (tuple(attentions),)
153
+ return outputs
154
+
155
+ return BaseModelOutputWithPast(
156
+ last_hidden_state=last_hidden,
157
+ past_key_values=past_kv if use_cache else None,
158
+ hidden_states=tuple(hidden_states) if output_hidden_states else None,
159
+ attentions=tuple(attentions) if output_attentions else None,
160
+ )
161
+
162
+ def _forward_features(
163
+ self,
164
+ input_ids: torch.LongTensor,
165
+ inference_params_dict: Optional[Evo2Cache] = None,
166
+ attention_mask: Optional[torch.Tensor] = None,
167
+ output_hidden_states: bool = False,
168
+ output_attentions: bool = False,
169
+ ):
170
+ x = self.embedding_layer.embed(input_ids)
171
+
172
+ all_hidden_states: list[torch.Tensor] = []
173
+ all_attentions: list[Optional[torch.Tensor]] = []
174
+ if output_hidden_states:
175
+ all_hidden_states.append(x)
176
+
177
+ if inference_params_dict is not None:
178
+ x, params_out = self._stateful_forward(
179
+ x, inference_params_dict,
180
+ all_hidden_states=all_hidden_states,
181
+ all_attentions=all_attentions,
182
+ output_hidden_states=output_hidden_states,
183
+ output_attentions=output_attentions,
184
+ )
185
+ else:
186
+ x, params_out = self._stateless_forward(
187
+ x, padding_mask=attention_mask,
188
+ all_hidden_states=all_hidden_states,
189
+ all_attentions=all_attentions,
190
+ output_hidden_states=output_hidden_states,
191
+ output_attentions=output_attentions,
192
+ )
193
+
194
+ if self.norm is not None:
195
+ x = self.norm(x)
196
+ if output_hidden_states:
197
+ all_hidden_states.append(x)
198
+
199
+ return x, params_out, all_hidden_states, all_attentions
200
+
201
+ def get_input_embeddings(self):
202
+ return self.embedding_layer
203
+
204
+ def set_input_embeddings(self, value):
205
+ self.embedding_layer = value
206
+
207
+ def _stateful_forward(
208
+ self, x, cache: Evo2Cache,
209
+ all_hidden_states, all_attentions,
210
+ output_hidden_states, output_attentions,
211
+ ):
212
+ for block_idx, block in enumerate(self.blocks):
213
+ block_name = block_type_for_idx(self.config, block_idx)
214
+ inference_params = cache.by_block_name(block_name)
215
+ x, attn = block(
216
+ x, inference_params=inference_params,
217
+ output_attentions=output_attentions,
218
+ )
219
+ if output_hidden_states:
220
+ all_hidden_states.append(x)
221
+ if output_attentions:
222
+ all_attentions.append(attn)
223
+ return x, cache
224
+
225
+ def _stateless_forward(
226
+ self, x, padding_mask,
227
+ all_hidden_states, all_attentions,
228
+ output_hidden_states, output_attentions,
229
+ ):
230
+ if isinstance(padding_mask, torch.Tensor):
231
+ x = x * padding_mask[..., None]
232
+ for block in self.blocks:
233
+ x, attn = block(
234
+ x, inference_params=None, padding_mask=padding_mask,
235
+ output_attentions=output_attentions,
236
+ )
237
+ if output_hidden_states:
238
+ all_hidden_states.append(x)
239
+ if output_attentions:
240
+ all_attentions.append(attn)
241
+ return x, None
242
+
243
+ def initialize_inference_params(self, max_batch_size: int = 1) -> Evo2Cache:
244
+ return Evo2Cache(
245
+ max_seqlen=self.config.get("max_seqlen", 8192),
246
+ max_batch_size=max_batch_size,
247
+ short_filter_length=self.config.short_filter_length,
248
+ hcm_filter_length=self.config.hcm_filter_length,
249
+ hcs_filter_length=self.config.hcs_filter_length,
250
+ state_size=self.config.state_size,
251
+ )
252
+
253
+ def to_bfloat16_except_poles_residues(self):
254
+ """Cast params to bf16, restore fp32 invariants:
255
+
256
+ * log_poles / residues -- bf16 collapses the modal-form IIR (the
257
+ poles get rounded too aggressively, the recurrence blows up).
258
+ Mirrors vortex's ``to_bfloat16_except_pr_lc(to_float32=True)``
259
+ first-pass behaviour.
260
+
261
+ * rotary_emb.inv_freq -- HF ``from_pretrained(dtype=bf16)`` casts
262
+ ALL buffers to bf16. ``inv_freq = 1 / base^(2i/dim)`` loses ~7
263
+ bits of mantissa in bf16, which shifts cos/sin tables by ~5e-2
264
+ per cell at position 64+, causing each attention layer to add
265
+ ~4e-2 of noise on Q/K. We recompute inv_freq in fp32 here so
266
+ the cos/sin builder uses the precise stored value.
267
+ """
268
+ for k, p in self.named_parameters():
269
+ if "log_poles" not in k and "residues" not in k:
270
+ p.data = p.data.to(torch.bfloat16)
271
+ self.restore_precision_invariants()
272
+
273
+ def restore_precision_invariants(self):
274
+ """Restore exact RoPE frequencies and fp32 modal-filter parameters."""
275
+ for k, p in self.named_parameters():
276
+ if (
277
+ ("log_poles" in k or "residues" in k)
278
+ and p.dtype != torch.float32
279
+ ):
280
+ p.data = p.data.to(torch.float32)
281
+ for module in self.modules():
282
+ if hasattr(module, "inv_freq"):
283
+ restore_rotary_inv_freq(module)
284
+
285
+ def _apply(self, fn, recurse=True):
286
+ result = super()._apply(fn, recurse=recurse)
287
+ self.restore_precision_invariants()
288
+ return result
289
+
290
+
291
+ # =============================================================================
292
+ # HF wrappers
293
+ # =============================================================================
294
+
295
+
296
+ class Evo2PreTrainedModel(PreTrainedModel):
297
+ config_class = Evo2Config
298
+ base_model_prefix = "backbone"
299
+ supports_gradient_checkpointing = False
300
+ _no_split_modules = ["AttentionBlock", "ParallelGatedConvBlock"]
301
+ _skip_keys_device_placement = "past_key_values"
302
+ _keys_to_ignore_on_load_missing = [r"freq", r"\.t$", r"_extra_state"]
303
+ _keys_to_ignore_on_load_unexpected = [
304
+ r"fftconv",
305
+ r"twiddle_factors",
306
+ r"_extra_state$",
307
+ r"\.filter\.t$",
308
+ r"rotary_emb\.inv_freq",
309
+ ]
310
+ _supports_flash_attn_2 = True
311
+ _supports_sdpa = True
312
+ # Evo2 cache is a custom Evo2Cache (not a DynamicCache); tell HF not to wrap it.
313
+ _supports_cache_class = False
314
+ # log_poles / residues parameterize a modal long-range filter; bf16 collapses
315
+ # them. HF will keep these in fp32 even with dtype=bf16 at load time.
316
+ _keep_in_fp32_modules = ["log_poles", "residues"]
317
+
318
+ @classmethod
319
+ def from_pretrained(cls, *args, **kwargs):
320
+ if "dtype" not in kwargs and "torch_dtype" not in kwargs:
321
+ kwargs["dtype"] = torch.bfloat16
322
+ loaded = super().from_pretrained(*args, **kwargs)
323
+ model = loaded[0] if isinstance(loaded, tuple) else loaded
324
+ model.backbone.restore_precision_invariants()
325
+ return loaded
326
+
327
+
328
+ class Evo2Model(Evo2PreTrainedModel):
329
+ """Bare backbone returning BaseModelOutputWithPast."""
330
+
331
+ def __init__(self, config: Evo2Config):
332
+ super().__init__(config)
333
+ self.backbone = StripedHyena2(config)
334
+ self.config = config
335
+ self.post_init()
336
+ self.force_dtype()
337
+
338
+ def force_dtype(self):
339
+ self.backbone.to_bfloat16_except_poles_residues()
340
+
341
+ def get_input_embeddings(self):
342
+ return self.backbone.embedding_layer
343
+
344
+ def set_input_embeddings(self, value):
345
+ self.backbone.embedding_layer = value
346
+
347
+ def forward(
348
+ self,
349
+ input_ids: torch.LongTensor = None,
350
+ attention_mask: Optional[torch.LongTensor] = None,
351
+ past_key_values=None,
352
+ use_cache: Optional[bool] = None,
353
+ output_attentions: Optional[bool] = None,
354
+ output_hidden_states: Optional[bool] = None,
355
+ return_dict: Optional[bool] = None,
356
+ ) -> Union[Tuple, BaseModelOutputWithPast]:
357
+ return self.backbone(
358
+ input_ids=input_ids,
359
+ attention_mask=attention_mask,
360
+ past_key_values=past_key_values,
361
+ use_cache=use_cache,
362
+ output_attentions=output_attentions,
363
+ output_hidden_states=output_hidden_states,
364
+ return_dict=return_dict,
365
+ )
366
+
367
+
368
+ class Evo2ForCausalLM(Evo2PreTrainedModel, GenerationMixin):
369
+ """LM head wrapper. Tied to backbone.embedding_layer (Evo2 ties weights)."""
370
+
371
+ def __init__(self, config: Evo2Config, **kwargs):
372
+ super().__init__(config, **kwargs)
373
+ self.backbone = StripedHyena2(config)
374
+ self.config = config
375
+
376
+ vocab_size = config.vocab_size
377
+ if vocab_size % config.make_vocab_size_divisible_by != 0:
378
+ vocab_size += config.make_vocab_size_divisible_by - (
379
+ vocab_size % config.make_vocab_size_divisible_by
380
+ )
381
+ self.vocab_size = vocab_size
382
+ self.post_init()
383
+ self.force_dtype()
384
+
385
+ def force_dtype(self):
386
+ self.backbone.to_bfloat16_except_poles_residues()
387
+
388
+ def get_input_embeddings(self):
389
+ return self.backbone.embedding_layer
390
+
391
+ def set_input_embeddings(self, value):
392
+ self.backbone.embedding_layer = value
393
+
394
+ def get_output_embeddings(self):
395
+ return self.backbone.embedding_layer
396
+
397
+ def set_output_embeddings(self, value):
398
+ self.backbone.embedding_layer = value
399
+
400
+ def forward(
401
+ self,
402
+ input_ids: torch.LongTensor = None,
403
+ attention_mask: Optional[torch.LongTensor] = None,
404
+ labels: Optional[torch.LongTensor] = None,
405
+ past_key_values=None,
406
+ use_cache: Optional[bool] = None,
407
+ output_attentions: Optional[bool] = None,
408
+ output_hidden_states: Optional[bool] = None,
409
+ return_dict: Optional[bool] = None,
410
+ ) -> Union[Tuple, CausalLMOutputWithPast]:
411
+ return_dict = return_dict if return_dict is not None else self.config.use_return_dict
412
+ output_attentions = (
413
+ output_attentions if output_attentions is not None else self.config.output_attentions
414
+ )
415
+ output_hidden_states = (
416
+ output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
417
+ )
418
+ use_cache = use_cache if use_cache is not None else self.config.use_cache
419
+
420
+ if use_cache and labels is not None:
421
+ logger.warning_once("use_cache=True is incompatible with loss computation; disabling.")
422
+ use_cache = False
423
+
424
+ inputs = input_ids
425
+ if use_cache:
426
+ if isinstance(past_key_values, Evo2Cache):
427
+ inputs = input_ids[:, -1:]
428
+ else:
429
+ past_key_values = None
430
+
431
+ backbone_outputs = self.backbone(
432
+ input_ids=inputs,
433
+ attention_mask=attention_mask,
434
+ past_key_values=past_key_values,
435
+ use_cache=use_cache,
436
+ output_hidden_states=output_hidden_states,
437
+ output_attentions=output_attentions,
438
+ return_dict=True,
439
+ )
440
+ last_hidden = backbone_outputs.last_hidden_state
441
+ past_kv = backbone_outputs.past_key_values
442
+ hidden_states = backbone_outputs.hidden_states
443
+ attentions = backbone_outputs.attentions
444
+
445
+ logits = last_hidden @ self.backbone.embedding_layer.weight.T
446
+
447
+ loss = None
448
+ if labels is not None:
449
+ shift_logits = logits[..., :-1, :].contiguous()
450
+ shift_labels = labels[..., 1:].contiguous()
451
+ shift_logits = shift_logits.view(-1, self.config.vocab_size)
452
+ shift_labels = shift_labels.view(-1).to(shift_logits.device)
453
+ loss = F.cross_entropy(shift_logits, shift_labels)
454
+
455
+ if not return_dict:
456
+ outputs = (logits,)
457
+ if use_cache: outputs += (past_kv,)
458
+ if output_hidden_states: outputs += (hidden_states,)
459
+ if output_attentions: outputs += (attentions,)
460
+ if loss is not None: outputs = (loss,) + outputs
461
+ return outputs
462
+
463
+ return CausalLMOutputWithPast(
464
+ loss=loss,
465
+ logits=logits,
466
+ past_key_values=past_kv if use_cache else None,
467
+ hidden_states=hidden_states,
468
+ attentions=attentions,
469
+ )
470
+
471
+ @classmethod
472
+ def can_generate(cls) -> bool:
473
+ return True
474
+
475
+ def prepare_inputs_for_generation(
476
+ self, input_ids, attention_mask=None, past_key_values=None, **kwargs
477
+ ):
478
+ return {
479
+ "input_ids": input_ids,
480
+ "attention_mask": attention_mask,
481
+ "past_key_values": past_key_values,
482
+ }
rotary.py ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Rotary embeddings for Evo2.
2
+
3
+ Mirrors models/evo/hf/rotary.py (same RoPE math): flash_attn fast path when
4
+ available, pure-PyTorch fallback otherwise. LinearlyScaledRotaryEmbedding
5
+ implements linear position-index interpolation (`t = t / scaling_factor`)
6
+ used by all the 1M / 262K context variants.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import torch
12
+ import torch.nn as nn
13
+
14
+
15
+ try:
16
+ from flash_attn.layers.rotary import RotaryEmbedding as _FlashRotaryEmbedding
17
+ _HAS_FLASH_ROTARY = True
18
+ except ImportError:
19
+ _FlashRotaryEmbedding = None
20
+ _HAS_FLASH_ROTARY = False
21
+
22
+
23
+ def _rotate_half(x: torch.Tensor) -> torch.Tensor:
24
+ x1, x2 = x.chunk(2, dim=-1)
25
+ return torch.cat((-x2, x1), dim=-1)
26
+
27
+
28
+ @torch.no_grad()
29
+ def restore_rotary_inv_freq(
30
+ rotary_emb: nn.Module,
31
+ *,
32
+ device=None,
33
+ invalidate_cache: bool = True,
34
+ ) -> None:
35
+ """Rebuild ``inv_freq`` from RoPE metadata in fp32."""
36
+ if not hasattr(rotary_emb, "inv_freq"):
37
+ return
38
+ device = rotary_emb.inv_freq.device if device is None else device
39
+ if hasattr(rotary_emb, "_compute_inv_freq"):
40
+ inv_freq = rotary_emb._compute_inv_freq(device=device)
41
+ else:
42
+ inv_freq = 1.0 / (
43
+ float(rotary_emb.base)
44
+ ** (
45
+ torch.arange(
46
+ 0,
47
+ int(rotary_emb.dim),
48
+ 2,
49
+ device=device,
50
+ dtype=torch.float32,
51
+ )
52
+ / int(rotary_emb.dim)
53
+ )
54
+ )
55
+ rotary_emb.inv_freq = inv_freq.to(dtype=torch.float32)
56
+ if invalidate_cache:
57
+ rotary_emb._seq_len_cached = 0
58
+ for name in (
59
+ "_cos_cached",
60
+ "_sin_cached",
61
+ "_cos_k_cached",
62
+ "_sin_k_cached",
63
+ ):
64
+ if hasattr(rotary_emb, name):
65
+ setattr(rotary_emb, name, None)
66
+
67
+
68
+ def _apply_rotary(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
69
+ # Compute the multiply in fp32 then cast back to x.dtype to match
70
+ # flash_attn's Triton kernel bit-exactly. Doing the multiply in bf16
71
+ # directly compounds rounding error of ~3e-2 per layer.
72
+ rot_dim = cos.shape[-1] * 2
73
+ x_rot = x[..., :rot_dim]
74
+ x_pass = x[..., rot_dim:]
75
+ orig_dtype = x.dtype
76
+ cos_full = torch.cat((cos, cos), dim=-1).float()
77
+ sin_full = torch.cat((sin, sin), dim=-1).float()
78
+ x_rot_f = x_rot.float()
79
+ rotated = (x_rot_f * cos_full) + (_rotate_half(x_rot_f) * sin_full)
80
+ rotated = rotated.to(orig_dtype)
81
+ return torch.cat((rotated, x_pass), dim=-1)
82
+
83
+
84
+ class _PureRotaryEmbedding(nn.Module):
85
+ """Pure-PyTorch fallback RoPE. Mirrors flash_attn.layers.rotary.RotaryEmbedding's surface."""
86
+
87
+ def __init__(
88
+ self,
89
+ dim: int,
90
+ base: float = 10000.0,
91
+ interleaved: bool = False,
92
+ scale_base: float | None = None,
93
+ pos_idx_in_fp32: bool = True,
94
+ device=None,
95
+ ):
96
+ super().__init__()
97
+ if interleaved:
98
+ raise NotImplementedError("Interleaved RoPE is not implemented.")
99
+ if scale_base is not None:
100
+ raise NotImplementedError("xPos scale_base is not implemented.")
101
+ self.dim = dim
102
+ self.base = float(base)
103
+ self.interleaved = interleaved
104
+ self.scale_base = scale_base
105
+ self.pos_idx_in_fp32 = pos_idx_in_fp32
106
+
107
+ inv_freq = self._compute_inv_freq(device=device)
108
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
109
+ self.scale = None # xPos slot kept for swap_mha_rope compatibility
110
+
111
+ self._seq_len_cached = 0
112
+ self._cos_cached: torch.Tensor | None = None
113
+ self._sin_cached: torch.Tensor | None = None
114
+
115
+ def _compute_inv_freq(self, device=None) -> torch.Tensor:
116
+ return 1.0 / (
117
+ self.base
118
+ ** (torch.arange(0, self.dim, 2, device=device, dtype=torch.float32) / self.dim)
119
+ )
120
+
121
+ def _update_cos_sin_cache(self, seqlen: int, device=None, dtype=None):
122
+ if (
123
+ seqlen > self._seq_len_cached
124
+ or self._cos_cached is None
125
+ or self._cos_cached.device != device
126
+ or self._cos_cached.dtype != dtype
127
+ or (self.training and self._cos_cached.is_inference())
128
+ ):
129
+ self._seq_len_cached = seqlen
130
+ if self.pos_idx_in_fp32:
131
+ t = torch.arange(seqlen, device=device, dtype=torch.float32)
132
+ inv_freq = self._compute_inv_freq(device=device)
133
+ else:
134
+ t = torch.arange(seqlen, device=device, dtype=self.inv_freq.dtype)
135
+ inv_freq = self.inv_freq
136
+
137
+ freqs = torch.outer(t, inv_freq)
138
+ self._cos_cached = torch.cos(freqs).to(dtype)
139
+ self._sin_cached = torch.sin(freqs).to(dtype)
140
+
141
+ def forward(
142
+ self,
143
+ qkv: torch.Tensor,
144
+ seqlen_offset: int | torch.Tensor = 0,
145
+ max_seqlen: int | None = None,
146
+ ) -> torch.Tensor:
147
+ if isinstance(seqlen_offset, torch.Tensor):
148
+ seqlen_offset = int(seqlen_offset.max().item())
149
+ T = qkv.shape[1]
150
+ seqlen = max_seqlen if max_seqlen is not None else (T + seqlen_offset)
151
+ self._update_cos_sin_cache(seqlen, device=qkv.device, dtype=qkv.dtype)
152
+
153
+ cos = self._cos_cached[seqlen_offset : seqlen_offset + T]
154
+ sin = self._sin_cached[seqlen_offset : seqlen_offset + T]
155
+ q, k, v = qkv.unbind(dim=2)
156
+ cos_b = cos[None, :, None, :]
157
+ sin_b = sin[None, :, None, :]
158
+ q = _apply_rotary(q, cos_b, sin_b)
159
+ k = _apply_rotary(k, cos_b, sin_b)
160
+ return torch.stack((q, k, v), dim=2)
161
+
162
+
163
+ if _HAS_FLASH_ROTARY:
164
+ class RotaryEmbedding(_FlashRotaryEmbedding):
165
+ """Flash RoPE with exact fp32 frequency reconstruction."""
166
+
167
+ def _update_cos_sin_cache(self, seqlen, device=None, dtype=None):
168
+ needs_update = (
169
+ seqlen > self._seq_len_cached
170
+ or self._cos_cached is None
171
+ or self._cos_cached.device != device
172
+ or self._cos_cached.dtype != dtype
173
+ or (self.training and self._cos_cached.is_inference())
174
+ )
175
+ if needs_update and self.pos_idx_in_fp32:
176
+ restore_rotary_inv_freq(
177
+ self,
178
+ device=device,
179
+ invalidate_cache=False,
180
+ )
181
+ return super()._update_cos_sin_cache(
182
+ seqlen,
183
+ device=device,
184
+ dtype=dtype,
185
+ )
186
+ else:
187
+ RotaryEmbedding = _PureRotaryEmbedding
188
+
189
+
190
+ class LinearlyScaledRotaryEmbedding(RotaryEmbedding):
191
+ """RoPE with linear interpolation of position indices.
192
+
193
+ Used for evo2_7b (1M), evo2_7b_262k, evo2_20b, evo2_40b. Positions are
194
+ divided by ``scaling_factor`` before cos/sin tables are computed.
195
+ """
196
+
197
+ def __init__(self, dim: int, scaling_factor: float = 1.0, **kwargs):
198
+ super().__init__(dim=dim, **kwargs)
199
+ self._linear_scaling_factor = float(scaling_factor)
200
+
201
+ def _update_cos_sin_cache(self, seqlen, device=None, dtype=None):
202
+ if (
203
+ seqlen <= self._seq_len_cached
204
+ and self._cos_cached is not None
205
+ and self._cos_cached.device == device
206
+ and self._cos_cached.dtype == dtype
207
+ and not (self.training and self._cos_cached.is_inference())
208
+ ):
209
+ return
210
+
211
+ self._seq_len_cached = seqlen
212
+ if self.pos_idx_in_fp32:
213
+ t = torch.arange(seqlen, device=device, dtype=torch.float32)
214
+ t = t / self._linear_scaling_factor
215
+ inv_freq = self._compute_inv_freq(device=device)
216
+ else:
217
+ t = torch.arange(seqlen, device=device, dtype=self.inv_freq.dtype)
218
+ t = t / self._linear_scaling_factor
219
+ inv_freq = self.inv_freq
220
+
221
+ freqs = torch.outer(t, inv_freq)
222
+ if self.scale is None:
223
+ self._cos_cached = torch.cos(freqs).to(dtype)
224
+ self._sin_cached = torch.sin(freqs).to(dtype)
225
+ else:
226
+ from einops import rearrange
227
+ power = (
228
+ torch.arange(seqlen, dtype=self.scale.dtype, device=self.scale.device)
229
+ - seqlen // 2
230
+ ) / self.scale_base
231
+ scale = self.scale.to(device=power.device) ** rearrange(power, "s -> s 1")
232
+ self._cos_cached = (torch.cos(freqs) * scale).to(dtype)
233
+ self._sin_cached = (torch.sin(freqs) * scale).to(dtype)
234
+ self._cos_k_cached = (torch.cos(freqs) / scale).to(dtype)
235
+ self._sin_k_cached = (torch.sin(freqs) / scale).to(dtype)
236
+
237
+
238
+ def swap_mha_rope(mha, new_rope=LinearlyScaledRotaryEmbedding, kwargs_new_rope=None):
239
+ """Replace ``mha.rotary_emb`` with a freshly-constructed scaled RoPE."""
240
+ weight_attr = "Wq" if getattr(mha, "cross_attn", False) else "Wqkv"
241
+ weight = getattr(mha, weight_attr).weight
242
+ dtype = weight.dtype
243
+ kwargs_old_rope = dict(
244
+ dim=mha.rotary_emb.dim,
245
+ base=mha.rotary_emb.base,
246
+ interleaved=mha.rotary_emb.interleaved,
247
+ scale_base=mha.rotary_emb.scale_base,
248
+ pos_idx_in_fp32=mha.rotary_emb.pos_idx_in_fp32,
249
+ device=mha.rotary_emb.inv_freq.device,
250
+ )
251
+ del mha.rotary_emb
252
+ kwargs_new_rope = kwargs_new_rope or {"scaling_factor": 1.0}
253
+ scaled = new_rope(**kwargs_new_rope, **kwargs_old_rope).to(dtype)
254
+ restore_rotary_inv_freq(scaled)
255
+ mha.rotary_emb = scaled
256
+ return mha
special_tokens_map.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {}
tokenization_evo2.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ByteTokenizer for Evo2 (StripedHyena2).
2
+
3
+ Wraps raw UTF-8 byte values into a vocab of size 512 (only [0, 255] are real
4
+ bytes; the upper range pads out to match the model's vocab embedding size).
5
+ Mirrors vortex.model.tokenizer.CharLevelTokenizer exactly:
6
+
7
+ * eod / eos = byte 0 (chr(0))
8
+ * pad = byte 1 (chr(1))
9
+
10
+ No real special tokens are added at encoding time -- Evo2 is a pure byte-level
11
+ model and downstream pooling should treat every non-pad position as a real
12
+ token.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from os import PathLike
18
+ from typing import List, Tuple
19
+
20
+ import numpy as np
21
+
22
+ from transformers.tokenization_utils import PreTrainedTokenizer
23
+ from transformers.tokenization_utils_base import BatchEncoding, TruncationStrategy
24
+ from transformers.utils.generic import PaddingStrategy
25
+
26
+
27
+ EMPTY: str = ""
28
+
29
+
30
+ class ByteTokenizer(PreTrainedTokenizer):
31
+ """UTF-8 byte-level tokenizer for Evo2 (vocab_size = 512)."""
32
+
33
+ def __init__(self, byte_level: bool = True, **kwargs):
34
+ kwargs.setdefault("pad_token", chr(1))
35
+ super().__init__(byte_level=byte_level, **kwargs)
36
+ self.model_input_names = ["input_ids", "attention_mask"]
37
+
38
+ @property
39
+ def vocab_size(self) -> int:
40
+ return 512
41
+
42
+ @property
43
+ def byte_level(self) -> bool:
44
+ return self.init_kwargs.get("byte_level", True)
45
+
46
+ def get_vocab(self) -> dict:
47
+ return {chr(i): i for i in range(self.vocab_size)}
48
+
49
+ def __len__(self) -> int:
50
+ return self.vocab_size
51
+
52
+ def clamp(self, n: int) -> int:
53
+ # Matches vortex CharLevelTokenizer.clamp.
54
+ return max(32, min(n, self.vocab_size))
55
+
56
+ def _tokenize(self, text: str, **kwargs) -> List[str]:
57
+ return list(text)
58
+
59
+ def byte_tokenize(self, text: str) -> np.ndarray:
60
+ return np.frombuffer(text.encode("utf-8"), dtype=np.uint8)
61
+
62
+ def _convert_token_to_id(self, token: str) -> int:
63
+ return self.clamp(ord(token))
64
+
65
+ def _convert_id_to_token(self, index: int) -> str:
66
+ return chr(self.clamp(index))
67
+
68
+ def convert_tokens_to_string(self, tokens: List[str]) -> str:
69
+ return EMPTY.join(tokens)
70
+
71
+ def _decode(self, token_ids: List[int], **kwargs) -> str:
72
+ indices = np.asarray(token_ids, dtype=np.int16)
73
+ indices = indices.clip(min=32, max=255).astype(np.uint8)
74
+ return indices.tobytes().decode("utf-8", errors="replace")
75
+
76
+ def _encode_plus(self, text: str, **kwargs) -> BatchEncoding:
77
+ first_ids = self.byte_tokenize(text).tolist()
78
+ return self.prepare_for_model(
79
+ first_ids,
80
+ pair_ids=None,
81
+ add_special_tokens=kwargs.get("add_special_tokens", False),
82
+ padding=kwargs.get("padding_strategy", PaddingStrategy.DO_NOT_PAD).value,
83
+ truncation=kwargs.get("truncation_strategy", TruncationStrategy.DO_NOT_TRUNCATE).value,
84
+ max_length=kwargs.get("max_length"),
85
+ stride=kwargs.get("stride", 0),
86
+ pad_to_multiple_of=kwargs.get("pad_to_multiple_of"),
87
+ return_tensors=kwargs.get("return_tensors"),
88
+ prepend_batch_axis=True,
89
+ return_attention_mask=kwargs.get("return_attention_mask"),
90
+ return_token_type_ids=kwargs.get("return_token_type_ids"),
91
+ return_overflowing_tokens=kwargs.get("return_overflowing_tokens", False),
92
+ return_special_tokens_mask=kwargs.get("return_special_tokens_mask", False),
93
+ return_length=kwargs.get("return_length", False),
94
+ verbose=kwargs.get("verbose", True),
95
+ )
96
+
97
+ def _batch_encode_plus(self, batch_text_or_text_pairs, **kwargs) -> BatchEncoding:
98
+ input_ids = [(self.byte_tokenize(t).tolist(), None) for t in batch_text_or_text_pairs]
99
+ return self._batch_prepare_for_model(
100
+ input_ids,
101
+ add_special_tokens=kwargs.get("add_special_tokens", False),
102
+ padding_strategy=kwargs.get("padding_strategy", PaddingStrategy.DO_NOT_PAD),
103
+ truncation_strategy=kwargs.get("truncation_strategy", TruncationStrategy.DO_NOT_TRUNCATE),
104
+ max_length=kwargs.get("max_length"),
105
+ stride=kwargs.get("stride", 0),
106
+ pad_to_multiple_of=kwargs.get("pad_to_multiple_of"),
107
+ return_attention_mask=kwargs.get("return_attention_mask"),
108
+ return_token_type_ids=kwargs.get("return_token_type_ids"),
109
+ return_overflowing_tokens=kwargs.get("return_overflowing_tokens", False),
110
+ return_special_tokens_mask=kwargs.get("return_special_tokens_mask", False),
111
+ return_length=kwargs.get("return_length", False),
112
+ return_tensors=kwargs.get("return_tensors"),
113
+ verbose=kwargs.get("verbose", True),
114
+ )
115
+
116
+ def _save_pretrained(
117
+ self, save_directory: str | PathLike, file_names: Tuple[str], **kwargs
118
+ ) -> Tuple[str]:
119
+ return file_names
tokenizer_config.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "auto_map": {
3
+ "AutoTokenizer": [
4
+ "tokenization_evo2.ByteTokenizer",
5
+ null
6
+ ]
7
+ },
8
+ "tokenizer_class": "ByteTokenizer",
9
+ "model_max_length": 1048576,
10
+ "pad_token": "\u0001",
11
+ "byte_level": true
12
+ }