nx_attention.nx
buildroot/runtime/nx_attention.nx
about
nx_attention.nx -- scaled dot-product attention kernel.
THE kernel that activates everything we've shipped on the
sovereign-ML stack. Composes:
nx_tensor -- typed shape contract
nx_blas_i64 -- gemm-shape Q @ K^T
nx_kv_cache -- O(n) autoregressive incremental
nx_sparse_tensor -- skip zero entries post-softmax
nx_quant_block -- 4-bit Q / K / V at storage layer (queued)
nx_numeric_oracle -- bit-exact vs reference verification
Per Vaswani 2017:
Attention(Q, K, V) = softmax(Q @ K^T / sqrt(d_k)) @ V
On the i64 + Q10 substrate:
* Q, K, V are i64 tensors with shape [n_tokens, head_dim]
* Scores = Q @ K^T (raw integer dot products)
* Scale = 1 / sqrt(d_k) approximated via Q10 lookup or
constant the caller pre-computes
* Softmax in Q10: per-row, find max, subtract, exp via
piecewise approximation, normalise
* Output = weighted @ V
For v1 we expose:
nx_attn_score_matrix Q @ K^T raw (i64)
nx_attn_scale_q10 apply Q10 scaling
nx_attn_softmax_row_q10 per-row softmax in Q10
nx_attn_apply_to_v scores @ V
nx_attn_forward full pass (compose the four above)
Per the min-hardware-floor cardinal: every step is a separate
primitive so the caller can swap in sparse / quant / fused
variants at any layer.
Softmax-in-Q10 honest accounting:
* exp(x) in Q10 uses a 16-entry lookup + linear interp; max
error ~3% relative for x in [-3, 0].
* Output rows sum to ~1024 (Q10 unity); rounding pads by up
dependencies 4 imports · 8 importers
imports: nx_syscalls.nxnx_tier.nxnx_tensor.nxnx_blas_i64.nx
imported by: _attn_exp_hoist_gate.nxnx_attention_test.nxnx_attn_window.nxnx_flash_attention.nxnx_flash_attention_test.nxnx_llm_run.nxnx_llm_run_v2.nxnx_transformer_block.nx
structs
| none |
consts
| 73 | const NX_MAGIC_1024: i64 = 1024 |
| 74 | const NX_MAGIC_7680: i64 = 7680 |
| 76 | const NX_ATTN_Q10: nx_int = 1024 |
| 80 | const NX_ATTN_OK: nx_int = 0 |
| 81 | const NX_ATTN_ERR_BAD_DTYPE: nx_int = 1 |
| 82 | const NX_ATTN_ERR_BAD_NDIM: nx_int = 2 |
| 83 | const NX_ATTN_ERR_SHAPE_MISMATCH: nx_int = 3 |
| 84 | const NX_ATTN_ERR_NOT_CONTIGUOUS: nx_int = 4 |
| 85 | const NX_ATTN_N_VERDICTS: nx_int = 5 |
functions
| 87 | func nx_attn_verdict_is_valid(v: nx_int) -> nx_int called by 1: main |
| 102 | func nx_attn_score_matrix(q: *NxTensor, k: *NxTensor, scores: *NxTensor) -> nx_int |
| 146 | func nx_attn_scale_q10(scores: *NxTensor, scale_q10: nx_int) -> nx_int |
| 185 | func _attn_exp_lut_fill(lo_table: *i64) -> i64 |
| 197 | func _attn_exp_q10_lut(x_q10: nx_int, lo_table: *i64) -> nx_int |
| 212 | func _attn_exp_q10(x_q10: nx_int) -> nx_int |
| 227 | func nx_attn_softmax_row_q10(scores: *NxTensor) -> nx_int |
| 282 | func nx_attn_apply_to_v(weights: *NxTensor, v: *NxTensor, out: *NxTensor) -> nx_int |
| 331 | func nx_attn_forward(q: *NxTensor, k: *NxTensor, v: *NxTensor, |