Skip to content

[ternary-f32] Phase 2: TernaryF32GemvNative SPI + reference ViewKernel + TernaryF32KernelPack #1138

Description

@michalharakal

Part of #1136 (Phase 2 of 5). Pure commonMain in skainet-backend-api — the SPI, the portable reference kernel, and the dispatch pack. No native dependency; mirrors the existing BitNetGemvNative / TernaryKernelPacks seam.

Tasks

  • TernaryF32GemvNative.kt — array-shaped native seam:
    public interface TernaryF32GemvNative {
        public val name: String
        /** out[o] = Σ_k in[k] · code(o,k) — scale NOT applied; sequential
         *  4-codes-per-byte BitNet payload. inputDim % 4 == 0. */
        public fun gemvPacked(
            activation: FloatArray, activationOffset: Int,
            weight: ByteArray, weightByteOffset: Int,
            inputDim: Int, outputDim: Int,
            out: FloatArray, outOffset: Int,
        )
    }
  • TernaryF32GemvKernel.kt — portable reference ViewKernel: FP32 activation × BITNET_B1_58 weight, decode ((byte >> 2*(i%4)) & 3) − 1, fp32 accumulate, post-multiply by TernaryCodec.bitNetScale. keyFor() = the exact key matmul(FP32 dense contiguous × FP32/BITNET_B1_58 BLOCKED_ROW_MAJOR).
  • TernaryF32KernelPack.ktinstall(native: TernaryF32GemvNative?, capabilities, warn):
    • registers the native-backed view kernel under both the capability-free and capability keys (mirroring TernaryKernelPacks.install) only when native != null — without the native LUT kernel, the existing requantize→bitnet_gemv path remains the better portable fallback, so the exact key stays unregistered and behavior is unchanged
    • native view kernel: unwrap heap storage, loop rows for prefill (advance offsets by r*k / r*n), apply bitNetScale to outputs, fall back to the f32 reference (not BitNetGemvKernel) for non-heap storage
  • Rebase the Phase-1 FFM object onto the SPI: FfmTernaryF32Gemv : TernaryF32GemvNative + an install(warn) helper in skainet-backend-native-cpu/jvmMain

Tests

  • TernaryF32KernelPackTest (FakeNative, modeled on TernaryKernelPacksTest): exact-key selection beats the requantize path; pack absent → int8 path still serves; multi-row loop; scale application; install(null) warns and registers nothing
  • TernaryF32GemvKernelTest: reference vs TernaryCodec.decodeBitNet + naive FP32 matmul on random tensors (exact equality on the codes-dot; tolerance only for summation order)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions