|
| 1 | +# path: src/kernels/ternary_kernels.py |
| 2 | + |
| 3 | +import mlx.core as mx |
| 4 | +from typing import Tuple |
| 5 | + |
| 6 | + |
| 7 | +def abs_mean_quantize(weight: mx.array, eps: float = 1e-8) -> Tuple[mx.array, mx.array]: |
| 8 | + """BitNet 1.58 AbsMean quantization to ternary {-1, 0, +1}.""" |
| 9 | + scale = mx.mean(mx.abs(weight), axis=1, keepdims=True) + eps |
| 10 | + scaled = weight / scale |
| 11 | + ternary = mx.where(scaled > 0.5, 1.0, |
| 12 | + mx.where(scaled < -0.5, -1.0, 0.0)) |
| 13 | + return ternary.astype(mx.int8), scale |
| 14 | + |
| 15 | + |
| 16 | +def ternary_matmul(input: mx.array, ternary_weight: mx.array, scale: mx.array) -> mx.array: |
| 17 | + """Optimized ternary matmul for BitNet 1.58.""" |
| 18 | + w = ternary_weight.astype(input.dtype) * scale |
| 19 | + return mx.matmul(input, w.T) |
| 20 | + |
| 21 | + |
| 22 | +class BitNetLinear: |
| 23 | + """BitNet 1.58 style linear layer.""" |
| 24 | + def __init__(self, in_features: int, out_features: int, bias: bool = False): |
| 25 | + self.in_features = in_features |
| 26 | + self.out_features = out_features |
| 27 | + weight = mx.random.normal((out_features, in_features)) * 0.02 |
| 28 | + self.ternary_weight, self.scale = abs_mean_quantize(weight) |
| 29 | + self.bias = mx.zeros((out_features,)) if bias else None |
| 30 | + |
| 31 | + def __call__(self, x: mx.array) -> mx.array: |
| 32 | + out = ternary_matmul(x, self.ternary_weight, self.scale) |
| 33 | + if self.bias is not None: |
| 34 | + out = out + self.bias |
| 35 | + return out |
0 commit comments