nx_quant_block.nx
buildroot/runtime/nx_quant_block.nx
about
nx_quant_block.nx -- 4-bit block quantization on i64 substrate.
Algo-led memory win per the min-hardware-floor cardinal.
Modern LLMs and diffusion models DO NOT fit on small hardware at
f32 precision; q4_K-style quantization is what makes Llama-3 70B
run on a 24 GB consumer GPU and what makes Z-Image fit on a 16 GB
card. On a $50 SBC with 4-8 GB total RAM, quantization is the
difference between "model loads" and "model OOMs."
v1 shape: q4_0-style symmetric quantization on i64 substrate.
* Block size: 32 values per block (standard)
* Per-block scale: one i64 (Q-format multiplier)
* Values: 4-bit signed nibbles in [-7, 7]; packed two per byte
* Storage per block: 8 bytes (scale) + 16 bytes (nibbles) = 24 bytes
* Dense storage: 32 i64 = 256 bytes
* Compression: 256 / 24 ~ 10.67x
When the compiler-I2 f-types land, this slots in q4_K (k-means per-
group scales) + q5_K + q8_0 without API change. The block layout
is the contract; the per-block calibration is the dtype.
Algorithmic precision note (the audit-honest answer):
* Per-value max rounding error = scale/2 = max_abs/14
* Relative error per value ≤ 1/14 ≈ 7.1% in the worst case
* Mean error across a uniform-distribution block is ~1/28 ≈ 3.6%
* Oracle round-trip uses eps_q10 = 102 (10%) -- well above mean
error, below worst-case, so failures here are real corruption
not normal quantization noise.
genealogy_id: q4_0_ggml_2023 + q4_K_ggml_2024 + lloyd_max_1957 +
jacob_kligys_2018_8bit_quant
lineage_id: substrate_quant_block_v1
dependencies 3 imports · 4 importers
imports: nx_syscalls.nxnx_tier.nxnx_tensor.nx
imported by: nx_embedding.nxnx_quant_block_test.nxnx_quant_q4k.nxnx_quant_q4k_test.nx
structs
| 67 | struct NxQuantBlock |
consts
| 44 | const NX_QB_BLOCK_SIZE: nx_int = 32 // values per block |
| 45 | const NX_QB_NIBBLE_MAX: nx_int = 7 // [-7..+7] |
| 46 | const NX_QB_NIBBLES_PER_BYTE: nx_int = 2 |
| 49 | const NX_QB_BYTES_PER_BLOCK: nx_int = 24 |
| 53 | const NX_QB_OK: nx_int = 0 |
| 54 | const NX_QB_ERR_BAD_LEN: nx_int = 1 // n not multiple of BLOCK_SIZE |
| 55 | const NX_QB_ERR_BAD_DTYPE: nx_int = 2 // input not i64 |
| 56 | const NX_QB_ERR_SHAPE_MISMATCH: nx_int = 3 // dequant target wrong shape |
| 57 | const NX_QB_N_VERDICTS: nx_int = 4 |
| 74 | const NX_QB_STRUCT_BYTES: nx_int = 32 // 4 fields * 8 |
functions
| 59 | func nx_qb_verdict_is_valid(v: nx_int) -> nx_int called by 1: main |
| 76 | func nx_qb_alloc(n_values: nx_int) -> *NxQuantBlock |
| 105 | func _qb_pack_nibble(qb: *NxQuantBlock, value_idx: nx_int, signed_nib: nx_int) -> nx_int called by 1: nx_qb_quantize |
| 126 | func _qb_unpack_nibble(qb: *NxQuantBlock, value_idx: nx_int) -> nx_int called by 1: nx_qb_dequantize |
| 152 | func _qb_abs(x: nx_int) -> nx_int called by 1: nx_qb_quantize |
| 157 | func nx_qb_quantize(values: *i64, n: nx_int, qb: *NxQuantBlock) -> nx_int |
| 211 | func nx_qb_dequantize(qb: *NxQuantBlock, values_out: *i64, n: nx_int) -> nx_int |
| 229 | func nx_qb_compression_ratio_q10(qb: *NxQuantBlock) -> nx_int called by 1: main |
| 240 | func nx_qb_quantize_tensor(t: *NxTensor) -> *NxQuantBlock |
| 251 | func nx_qb_dequantize_tensor(qb: *NxQuantBlock, t: *NxTensor) -> nx_int |