# RWKV-X Architecture RWKV-X is a hybrid model architecture that interleaves RWKV-7 recurrent blocks (~75% of layers) with sparse attention blocks (~25%). It is fundamentally different from transformer models and uses safetensors format instead of GGUF. ## Source The RWKV-X engine in Pantheon (`src/models/rwkvx.c`, ~2,500 LOC) is copied from the battle-tested Foundry implementation (`foundry/rwkvx/rwkvx.c`), with Metal, CUDA, HybridMem, training, batch, instrumentation, and OpenMP support stripped. A thin adapter layer wraps it into Pantheon's `pn_model`/`pn_state`/`pn_forward_fn` interface. ## Model Architecture For the 0.2B model (12 RWKV + 4 attention layers), block execution order is: ``` R0, R1, R2, A0, R3, R4, R5, A1, R6, R7, R8, A2, R9, R10, R11, A3 ``` Where `R` = RWKV-7 recurrent block, `A` = sparse attention block. ### Forward Pass ``` embed(token) → for each block in interleaved order: if RWKV-7 block: LayerNorm → TimeMix (WKV-7 kernel) → residual → LayerNorm → ChannelMix FFN → residual if attention block: LayerNorm → Multi-Head Attention (with KV cache) → residual → LayerNorm → ChannelMix FFN → residual → LayerNorm → LM head → logits ``` ### WKV-7 Kernel (Generalized Delta Rule) The core recurrent kernel, computed per-head per-timestep: ``` for each timestep t, for each head h: state *= diag(w[t]) // decay state += (a[t] @ state) * k^T // delta rule bonus state += v[t] @ k[t]^T // key-value update output[t] = r[t] @ state // readout ``` State is a `[head_size, head_size]` matrix per head per RWKV-7 layer — fixed size regardless of sequence length. ### Attention Blocks Standard multi-head attention with KV cache. Uses cached keys and values up to a configurable max length (default 2048). No RoPE — position information comes from the surrounding RWKV-7 blocks. ## Weight Format RWKV-X uses safetensors (not GGUF). Weights are loaded via Foundry's `fs_open()` (zero-copy mmap) and routed by prefix: | Prefix | Block Type | |--------|-----------| | `rwkv.blocks.N.*` | RWKV-7 recurrent block N | | `moba.blocks.N.*` | Sparse attention block N | | `emb.weight` | Token embeddings | | `head.weight` | LM head | | `ln_out.*` | Final layer norm | Weight transformations at load time: - Linear weights are transposed (safetensors stores `[out, in]`, inference needs `[in, out]`) - Singleton dimensions are squeezed - `att.r_k` is flattened from `[n_head, head_size]` to `[n_embd]` - Embeddings are pre-normalized (L2 norm per row) ## INT8 Quantization When `--quantize` is passed, all linear projection weights are quantized to INT8 at load time using per-row symmetric quantization: ``` scale[row] = max(abs(row)) / 127 quantized[row][col] = round(float[row][col] / scale[row]) ``` Inference uses a NEON-accelerated INT8 dot product kernel (`fast_matvec_q8`) that pre-quantizes the input vector once, then computes INT8 dot products for all output rows. This gives ~3x speedup on ARM. ## Tokenizer RWKV-X uses a **trie-based World tokenizer** with 65,536 tokens. The tokenizer operates on raw UTF-8 bytes with greedy longest-match — fundamentally different from BPE or SentencePiece. The vocab is loaded from a text file (`rwkv_vocab_v20230424.txt`) via the `--vocab` CLI flag. Each line contains a token ID, token length, and the token's byte representation as a Python byte literal. The tokenizer is stored inside the RWKV-X model struct (not in `pn_model->tokenizer`). The engine routes to it via `pn_rwkvx_encode()`/`pn_rwkvx_decode()` when `config.arch == PN_ARCH_RWKVX`. ## Dual State Each inference session maintains two state types in `pn_state->kv_cache`: - **Recurrent state**: Fixed-size per-layer matrix `[n_heads, head_size, head_size]` for RWKV-7 blocks - **KV cache**: Key-value store for sparse attention blocks (capped at `max_cache_len`) - **Workspace**: Pre-allocated scratch buffers (16 buffers + ping-pong pair + INT8 buffer) All state is encapsulated in a single opaque struct. `pn_state_reset()` zeros both the recurrent state and KV cache. ## Adapter API The adapter layer in `rwkvx.c` maps Pantheon's interface to internal RWKV-X functions: | Pantheon API | Internal Function | |-------------|-------------------| | `pn_rwkvx_load()` | `rx_model_load_internal()` | | `pn_rwkvx_free()` | `rx_model_free_internal()` + tokenizer free | | `pn_rwkvx_forward()` | `rx_forward_internal()` | | `pn_rwkvx_state_new()` | `rx_state_new_internal()` | | `pn_rwkvx_state_free()` | `rx_state_free_internal()` | | `pn_rwkvx_state_reset()` | `rx_state_reset_internal()` | | `pn_rwkvx_encode()` | `rx_tokenizer_encode()` | | `pn_rwkvx_decode()` | `rx_tokenizer_decode()` | ## Usage ```bash # FP32 inference ./pantheon --model ../models/base/RWKV-X-0.2B-64k-Base.safetensors \ --vocab foundry/rwkvx/data/rwkv_vocab_v20230424.txt \ --prompt "Hello world" --max-tokens 32 # INT8 quantized inference (~3x faster decode on ARM) ./pantheon --model ../models/base/RWKV-X-0.2B-64k-Base.safetensors \ --vocab foundry/rwkvx/data/rwkv_vocab_v20230424.txt \ --prompt "Hello world" --max-tokens 32 --quantize ``` ## What Was Stripped The Foundry RWKV-X engine has additional features not yet in Pantheon: | Feature | Why Stripped | |---------|-------------| | Metal GPU (`fm_*`) | Pantheon doesn't have Metal model integration yet | | CUDA GPU (`fc_*`) | Pantheon doesn't have CUDA model integration yet | | HybridMem (`hm_*`) | Separate feature, not part of base inference | | Training (`rx_forward_ex`, callbacks) | Training not in Pantheon scope yet | | Batch forward (`rx_forward_batch`) | Prefill optimization, add later | | Instrumentation (attn_log, embed_capture) | Debug tooling, add later | | OpenMP (`#pragma omp`) | Pantheon uses threadpool, not OpenMP | These can be added incrementally in future phases.