Text Generation
Safetensors
Rust
RWKV
English
oicio-rs
ternary
matmul-free
cpu-only
1.58-bit
bitnet
bonsai
infinite-context
em-llm
reattention
recursive-agent-harness
rlm
rah
edge-ai
needle
hadamard
mlgru
mamba
liquid-neural-networks
turbovec
turboquant
t-mac
vec-lut
axon
consumer-hardware
better-quality
intelligence-density
Instructions to use deeprcurs/OICIO with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- RWKV
How to use deeprcurs/OICIO with RWKV:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
| /*! | |
| BitLinear — Ternary Weights {-1,0,1} — No MatMul, Only Add/Sub | |
| Credits: deepRcurs Labs @deeprcurs / Mzed Imamkh @mzedimamkh | |
| Berdasarkan: | |
| - Microsoft BitNet b1.58: ternary weights, absmean quantization | |
| - MatMul-free LM 2406.02528: BitLinear eliminates MatMul in dense layers | |
| - T-MAC: LUT-based mpGEMM without dequantization, no multiplication | |
| Real BitNet 2B: 1.1GB vs 4.8GB FP16 (4.3x), 4.1x faster, 8.9x throughput | |
| Bonsai 8B: 1.75GB vs Qwen3 16.38GB (9.4x), 82 tok/s M4 Pro, 27 tok/s iPhone | |
| CPU-only: AVX2/NEON TBL/PSHUF for parallel LUT lookup, 32 indices with 1 instruction | |
| */ | |
| /// Ternary weight: -1, 0, +1 — 1.58-bit | |
| pub enum TernaryWeight { | |
| NegOne = -1, | |
| Zero = 0, | |
| PosOne = 1, | |
| } | |
| impl TernaryWeight { | |
| /// From 2-bit packed value: 00=-1, 01=0, 10=1, 11=0 (unused) | |
| pub fn from_2bit(val: u8) -> Self { | |
| match val & 0b11 { | |
| 0 => TernaryWeight::NegOne, | |
| 1 => TernaryWeight::Zero, | |
| 2 => TernaryWeight::PosOne, | |
| _ => TernaryWeight::Zero, // 11 -> 0 | |
| } | |
| } | |
| /// To f32 with scale | |
| pub fn to_f32(self, scale: f32) -> f32 { | |
| (self as i8 as f32) * scale | |
| } | |
| } | |
| /// BitLinear: replaces nn.Linear with ternary weights, no MatMul | |
| pub struct BitLinear { | |
| in_features: usize, | |
| out_features: usize, | |
| /// Packed ternary weights: 4 ternary per byte (2 bits each) | |
| /// Shape: [out_features, in_features/4] packed | |
| weight_packed: Vec<u8>, | |
| /// Scale per group of 128 weights (Bonsai style: group-wise quant + FP16 scale) | |
| weight_scale: Vec<f32>, | |
| /// Shadow full precision weights for training (QAT from step 0) | |
| weight_fp: Vec<f32>, | |
| } | |
| impl BitLinear { | |
| pub fn new(in_features: usize, out_features: usize) -> Self { | |
| // For 2-bit packing: 4 ternary per byte | |
| let packed_in = (in_features + 3) / 4; | |
| let weight_packed = vec![0u8; out_features * packed_in]; | |
| let num_groups = (in_features + 127) / 128; | |
| let weight_scale = vec![1.0; out_features * num_groups]; | |
| let weight_fp = vec![0.0; out_features * in_features]; | |
| Self { | |
| in_features, | |
| out_features, | |
| weight_packed, | |
| weight_scale, | |
| weight_fp, | |
| } | |
| } | |
| /// Absmean quantization: scale = 1 / mean(abs(w)), w_ternary = round(w/scale) clamped to {-1,0,1} | |
| /// From BitNet paper and FAQ | |
| pub fn absmean_quant(&self, w: &[f32]) -> (Vec<TernaryWeight>, f32) { | |
| let abs_mean = w.iter().map(|v| v.abs()).sum::<f32>() / w.len() as f32; | |
| let scale = if abs_mean < 1e-5 { 1e-5 } else { abs_mean }; | |
| let ternary: Vec<TernaryWeight> = w.iter().map(|&v| { | |
| let scaled = v / scale; | |
| let rounded = scaled.round() as i8; | |
| match rounded { | |
| -1 => TernaryWeight::NegOne, | |
| 0 => TernaryWeight::Zero, | |
| 1 => TernaryWeight::PosOne, | |
| x if x < -1 => TernaryWeight::NegOne, | |
| _ => TernaryWeight::PosOne, | |
| } | |
| }).collect(); | |
| (ternary, scale) | |
| } | |
| /// Pack ternary weights into bytes: 4 per byte, 2 bits each | |
| pub fn pack_ternary(ternary: &[TernaryWeight]) -> Vec<u8> { | |
| let mut packed = Vec::with_capacity((ternary.len() + 3) / 4); | |
| for chunk in ternary.chunks(4) { | |
| let mut byte = 0u8; | |
| for (i, &t) in chunk.iter().enumerate() { | |
| let bits = match t { | |
| TernaryWeight::NegOne => 0b00, | |
| TernaryWeight::Zero => 0b01, | |
| TernaryWeight::PosOne => 0b10, | |
| }; | |
| byte |= bits << (i*2); | |
| } | |
| packed.push(byte); | |
| } | |
| packed | |
| } | |
| /// Unpack bytes to ternary | |
| pub fn unpack_ternary(packed: &[u8], num_ternary: usize) -> Vec<TernaryWeight> { | |
| let mut ternary = Vec::with_capacity(num_ternary); | |
| for &byte in packed { | |
| for i in 0..4 { | |
| if ternary.len() >= num_ternary { break; } | |
| let bits = (byte >> (i*2)) & 0b11; | |
| ternary.push(TernaryWeight::from_2bit(bits)); | |
| } | |
| } | |
| ternary | |
| } | |
| /// Forward: NO MATMUL, only ADD/SUB | |
| /// x: [batch, in_features] f32 | |
| /// Returns: [batch, out_features] f32 | |
| /// | |
| /// Real implementation would use: | |
| /// - AVX2: _mm256_add_ps, _mm256_sub_ps | |
| /// - NEON: vaddq_f32, vsubq_f32 | |
| /// - TBL/PSHUF for LUT lookup | |
| pub fn forward(&self, x: &[f32]) -> Vec<f32> { | |
| // For POC, simple loop: sum where w=1, sub where w=-1, skip 0 | |
| // Real would use SIMD: 8x f32 per AVX2 register, 4x per NEON | |
| let batch = x.len() / self.in_features; | |
| let mut out = vec![0.0; batch * self.out_features]; | |
| // Unpack weights for this forward (in real, would use LUT directly without full unpack) | |
| let ternary = Self::unpack_ternary(&self.weight_packed, self.out_features * self.in_features); | |
| for b in 0..batch { | |
| for o in 0..self.out_features { | |
| let mut sum = 0.0; | |
| let mut scale = 1.0; | |
| // Group-wise scale: 128 weights per group | |
| let group_idx = 0; // simplified, real would be o * num_groups + group | |
| if group_idx < self.weight_scale.len() { | |
| scale = self.weight_scale[group_idx]; | |
| } | |
| for i in 0..self.in_features { | |
| let w = ternary[o * self.in_features + i]; | |
| match w { | |
| TernaryWeight::PosOne => sum += x[b * self.in_features + i] * scale, // ADD | |
| TernaryWeight::NegOne => sum -= x[b * self.in_features + i] * scale, // SUB | |
| TernaryWeight::Zero => {}, // SKIP (sparsity) | |
| } | |
| } | |
| out[b * self.out_features + o] = sum; | |
| } | |
| } | |
| out | |
| } | |
| /// Fused kernel: BitLinear + Hadamard + TurboQuant dequant in ONE kernel | |
| /// Minimizes HBM read/write like FlashAttention | |
| pub fn forward_fused( | |
| &self, | |
| x: &[f32], | |
| turboquant_codes: Option<&[u8]>, | |
| codebook: Option<&[f32]>, | |
| rotation: Option<&[f32]>, // [D*D] flattened | |
| ) -> Vec<f32> { | |
| // Step 1: Dequant TurboQuant codes via LUT in SRAM | |
| let mut x_dequant = x.to_vec(); | |
| if let (Some(codes), Some(cb)) = (turboquant_codes, codebook) { | |
| // LUT lookup: codes [N,D] uint8 -> float via codebook | |
| // In real T-MAC: TBL instruction, 32 indices with 1 instruction | |
| for (i, &code) in codes.iter().enumerate() { | |
| if i < x_dequant.len() { | |
| x_dequant[i] = cb[code as usize]; | |
| } | |
| } | |
| // Inverse rotation if provided | |
| if let Some(rot) = rotation { | |
| // x_dequant @ rot.T, in SRAM | |
| // Simplified for POC | |
| } | |
| } | |
| // Step 2: Hadamard transform (in SRAM, no weights, only add/sub) | |
| // Would call hadamard_transform here | |
| // Step 3: BitLinear ternary matmul (in SRAM) | |
| self.forward(&x_dequant) | |
| } | |
| } | |
| mod tests { | |
| use super::*; | |
| fn test_ternary_packing() { | |
| let ternary = vec![ | |
| TernaryWeight::NegOne, | |
| TernaryWeight::Zero, | |
| TernaryWeight::PosOne, | |
| TernaryWeight::Zero, | |
| ]; | |
| let packed = BitLinear::pack_ternary(&ternary); | |
| assert_eq!(packed.len(), 1); | |
| let unpacked = BitLinear::unpack_ternary(&packed, 4); | |
| assert_eq!(unpacked, ternary); | |
| } | |
| fn test_bitlinear_no_matmul() { | |
| let mut bl = BitLinear::new(8, 4); | |
| // Set some weights | |
| let ternary = vec![ | |
| TernaryWeight::PosOne, TernaryWeight::NegOne, TernaryWeight::Zero, TernaryWeight::PosOne, | |
| TernaryWeight::Zero, TernaryWeight::PosOne, TernaryWeight::NegOne, TernaryWeight::Zero, | |
| ]; | |
| bl.weight_packed = BitLinear::pack_ternary(&ternary); | |
| let x = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; | |
| let out = bl.forward(&x); | |
| // Manual: out[0] = 1*1 + (-1)*2 + 0*3 + 1*4 = 1 -2 +0 +4 = 3 | |
| // But we have 4 out_features, first 8 ternary only for first out | |
| println!("Output: {:?}", out); | |
| } | |
| } | |