Skip to content

Quantized matmul dispatch skips rank-1 (single-token decode) activations, falls through to broken matmulGeneric #993

Description

@fiwio-developer

Summary

chooseQuantizedMatmulHeap (skainet-backend-cpu/src/commonMain/.../DefaultCpuOps.kt) and chooseQuantizedMatmul (skainet-backend-cpu/src/jvmMain/.../DefaultCpuOpsJvm.kt) both gate on a.shape.rank >= 2, returning null (i.e. "not a case I handle, fall through") for a rank-1 activation. In practice, a real forward pass produces a rank-1 hidden-state vector for every incremental decode step once it moves past the initial batched prefill (which is rank-2+). That rank-1 case then falls all the way through to matmulGeneric's untyped per-element TensorData.get() access, which — for a packed-quant weight (including one wrapped by SKaiNET-transformers' PreTransposedWeight marker types, e.g. PreTransposedQ4_K) — returns the raw packed byte rather than a dequantized Float, throwing:

java.lang.ClassCastException: class java.lang.Byte cannot be cast to class java.lang.Float
	at sk.ainet.exec.tensor.ops.DefaultCpuOpsBase.matmulGeneric$lambda$1(DefaultCpuOps.kt:755)
	at sk.ainet.exec.tensor.ops.DefaultCpuOpsBase.matmulGeneric(DefaultCpuOps.kt:704)
	at sk.ainet.exec.tensor.ops.DefaultCpuOpsBase.matmul(DefaultCpuOps.kt:623)
	at sk.ainet.lang.nn.transformer.LinearProjectionKt.linearProject(LinearProjection.kt:40)
	at sk.ainet.lang.nn.transformer.MultiHeadAttention.attentionImpl(MultiHeadAttention.kt:298)
	...

Related to #991 / #992 (same dispatch functions, same crash signature) but a distinct root cause: #991/#992 was about the activation operand's TensorData subtype being too strict (FloatArrayTensorData-only) for a rank-2 input; this one is about rank, and reproduces even after #991/#992's fix is applied.

Repro

End-to-end against a real Llama-3.2-1B-Instruct-Q4_K_M.gguf via SKaiNET-transformers' KLlamaJava.loadGGUF (DIRECT mode): the first forward pass (prefill, rank-2 batched input) succeeds; the crash hits on the first post-prefill decode step, where the runtime passes a rank-1 [hidden_dim] activation.

Fix

Pushed as an additional commit to #992 (fix/q6k-memseg-matmul-dispatch), since it touches the exact same dispatch functions:

  • Both guards changed from a.shape.rank < 2 to a.shape.rank < 1 — the existing leading-dim-flattening logic (added for rank>2 attention batches) already generalizes correctly to rank-1 (empty leading dims, flatBatch=1, [in] promotes to [1, in], result squeezes back to [out]) with no further changes needed.
  • matmulGeneric hardened as defense in depth: a lazily-materialized copyToFloatArray() fallback if a TensorData's generic get() ever returns something other than the tensor's own dtype, instead of an unconditional unsafe as Float cast.
  • New regression test: rank-1 FP32 activation against a Q4_K-packed weight, asserting no throw and finite output.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions