Skip to content

Commit 2983327

Browse files
michalharakalclaude
andcommitted
test: Q4_K and Q6_K lazy-transpose invariant coverage
Adds direct unit tests for the Q4_K and Q6_K `ops.transpose` specializations in `DefaultCpuOpsJvm` — the ones added in ef4c40c and ace832a. Both checks run in the same `QuantizedMemSegMatmulTest` class that already covers the Q4/Q8 MemSeg variants. Each test asserts the two invariants the specialization promises: 1. Shape is swapped (rows × cols → cols × rows). 2. `packedData` byte array is the SAME reference — no copy, no re-layout. This is the load-bearing property: without it, the transpose falls through to the generic element-wise path in `DefaultCpuOpsBase`, which attempts a Byte→Float cast on the packed nibbles and blows up with `ClassCastException` a few stack frames deep inside `DenseTensorDataFactory.init`. Closes the test gap both ef4c40c and ace832a noted out loud ("Validated at the DSL level by GemmaDslQ4KTest in the transformers repo"): that integration test can't guard SKaiNET on its own because it lives in the sibling repo. These two local tests reproduce the contract here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 37f5d94 commit 2983327

1 file changed

Lines changed: 58 additions & 0 deletions

File tree

skainet-backends/skainet-backend-cpu/src/jvmTest/kotlin/sk/ainet/exec/tensor/ops/QuantizedMemSegMatmulTest.kt

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import sk.ainet.lang.tensor.VoidOpsTensor
1010
import sk.ainet.lang.tensor.data.DenseTensorDataFactory
1111
import sk.ainet.lang.tensor.data.Q4MemorySegmentMarker
1212
import sk.ainet.lang.tensor.data.Q4MemorySegmentTensorData
13+
import sk.ainet.lang.tensor.data.Q4_KBlockTensorData
14+
import sk.ainet.lang.tensor.data.Q4_KTensorData
15+
import sk.ainet.lang.tensor.data.Q6_KBlockTensorData
16+
import sk.ainet.lang.tensor.data.Q6_KTensorData
1317
import sk.ainet.lang.tensor.data.Q8MemorySegmentMarker
1418
import sk.ainet.lang.tensor.data.Q8MemorySegmentTensorData
1519
import sk.ainet.lang.tensor.data.TensorData
@@ -125,6 +129,60 @@ class QuantizedMemSegMatmulTest {
125129
arena.close()
126130
}
127131

132+
@Test
133+
fun `Q4_K lazy transpose swaps shape dimensions and keeps packed bytes`() {
134+
// Two Q4_K blocks (256 elements each = 144 bytes each). Content doesn't
135+
// matter for the shape/identity invariant — use random bytes.
136+
val numBlocks = 2
137+
val bytes = ByteArray(numBlocks * Q4_KTensorData.BYTES_PER_BLOCK) { i -> (i and 0x7F).toByte() }
138+
val q4k = Q4_KBlockTensorData(
139+
Shape(numBlocks, Q4_KTensorData.BLOCK_SIZE),
140+
bytes
141+
)
142+
@Suppress("UNCHECKED_CAST")
143+
val tensor: Tensor<FP32, Float> = VoidOpsTensor(q4k as TensorData<FP32, Float>, FP32::class)
144+
145+
val transposed = ops.transpose(tensor)
146+
assertEquals(Shape(Q4_KTensorData.BLOCK_SIZE, numBlocks), transposed.shape)
147+
assertTrue(
148+
transposed.data is Q4_KTensorData,
149+
"transpose must preserve Q4_K packed layout, got ${transposed.data::class.simpleName}"
150+
)
151+
// Lazy invariant: the packed byte array must be the SAME reference —
152+
// no copy, no re-layout. This is what distinguishes the specialized
153+
// branch from the fallback per-element transpose (which would crash
154+
// on Byte → Float casts, the very regression this test guards).
155+
val transposedPacked = (transposed.data as Q4_KTensorData).packedData
156+
assertTrue(
157+
transposedPacked === bytes,
158+
"Q4_K lazy transpose must keep the same packedData reference (zero-copy)"
159+
)
160+
}
161+
162+
@Test
163+
fun `Q6_K lazy transpose swaps shape dimensions and keeps packed bytes`() {
164+
val numBlocks = 2
165+
val bytes = ByteArray(numBlocks * Q6_KTensorData.BYTES_PER_BLOCK) { i -> (i and 0x7F).toByte() }
166+
val q6k = Q6_KBlockTensorData(
167+
Shape(numBlocks, Q6_KTensorData.BLOCK_SIZE),
168+
bytes
169+
)
170+
@Suppress("UNCHECKED_CAST")
171+
val tensor: Tensor<FP32, Float> = VoidOpsTensor(q6k as TensorData<FP32, Float>, FP32::class)
172+
173+
val transposed = ops.transpose(tensor)
174+
assertEquals(Shape(Q6_KTensorData.BLOCK_SIZE, numBlocks), transposed.shape)
175+
assertTrue(
176+
transposed.data is Q6_KTensorData,
177+
"transpose must preserve Q6_K packed layout, got ${transposed.data::class.simpleName}"
178+
)
179+
val transposedPacked = (transposed.data as Q6_KTensorData).packedData
180+
assertTrue(
181+
transposedPacked === bytes,
182+
"Q6_K lazy transpose must keep the same packedData reference (zero-copy)"
183+
)
184+
}
185+
128186
// ── Q4_0 Matmul Tests ───────────────────────────────────────────────────
129187

130188
@Test

0 commit comments

Comments
 (0)