Skip to content

Commit 5c86520

Browse files
committed
Fix failing tests
Related-To: #2, #147, #143
1 parent ba15c2d commit 5c86520

3 files changed

Lines changed: 100 additions & 28 deletions

File tree

skainet-backends/skainet-backend-cpu/src/commonMain/kotlin/sk/ainet/sk/ainet/exec/tensor/ops/DefaultCpuOps.kt

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,15 @@ public class DefaultCpuOps<V>(private val dataFactory: TensorDataFactory) : Tens
8686
return elementwise(a, b) { av, bv, dtype ->
8787
when (dtype) {
8888
sk.ainet.lang.types.FP32::class, sk.ainet.lang.types.FP16::class -> {
89-
val x = av as Float; val y = bv as Float; (x + y) as V
89+
val x = av as Float;
90+
val y = bv as Float; (x + y) as V
9091
}
92+
9193
sk.ainet.lang.types.Int32::class -> {
92-
val x = av as Int; val y = bv as Int; (x + y) as V
94+
val x = av as Int;
95+
val y = bv as Int; (x + y) as V
9396
}
97+
9498
else -> throw IllegalArgumentException("Unsupported dtype for add: ${'$'}dtype")
9599
}
96100
}
@@ -105,11 +109,15 @@ public class DefaultCpuOps<V>(private val dataFactory: TensorDataFactory) : Tens
105109
return elementwise(a, b) { av, bv, dtype ->
106110
when (dtype) {
107111
sk.ainet.lang.types.FP32::class, sk.ainet.lang.types.FP16::class -> {
108-
val x = av as Float; val y = bv as Float; (x - y) as V
112+
val x = av as Float;
113+
val y = bv as Float; (x - y) as V
109114
}
115+
110116
sk.ainet.lang.types.Int32::class -> {
111-
val x = av as Int; val y = bv as Int; (x - y) as V
117+
val x = av as Int;
118+
val y = bv as Int; (x - y) as V
112119
}
120+
113121
else -> throw IllegalArgumentException("Unsupported dtype for subtract: ${'$'}dtype")
114122
}
115123
}
@@ -124,11 +132,15 @@ public class DefaultCpuOps<V>(private val dataFactory: TensorDataFactory) : Tens
124132
return elementwise(a, b) { av, bv, dtype ->
125133
when (dtype) {
126134
sk.ainet.lang.types.FP32::class, sk.ainet.lang.types.FP16::class -> {
127-
val x = av as Float; val y = bv as Float; (x * y) as V
135+
val x = av as Float;
136+
val y = bv as Float; (x * y) as V
128137
}
138+
129139
sk.ainet.lang.types.Int32::class -> {
130-
val x = av as Int; val y = bv as Int; (x * y) as V
140+
val x = av as Int;
141+
val y = bv as Int; (x * y) as V
131142
}
143+
132144
else -> throw IllegalArgumentException("Unsupported dtype for multiply: ${'$'}dtype")
133145
}
134146
}
@@ -143,11 +155,15 @@ public class DefaultCpuOps<V>(private val dataFactory: TensorDataFactory) : Tens
143155
return elementwise(a, b) { av, bv, dtype ->
144156
when (dtype) {
145157
sk.ainet.lang.types.FP32::class, sk.ainet.lang.types.FP16::class -> {
146-
val x = av as Float; val y = bv as Float; (x / y) as V
158+
val x = av as Float;
159+
val y = bv as Float; (x / y) as V
147160
}
161+
148162
sk.ainet.lang.types.Int32::class -> {
149-
val x = av as Int; val y = bv as Int; if (y == 0) 0 as V else (x / y) as V
163+
val x = av as Int;
164+
val y = bv as Int; if (y == 0) 0 as V else (x / y) as V
150165
}
166+
151167
else -> throw IllegalArgumentException("Unsupported dtype for divide: ${'$'}dtype")
152168
}
153169
}
@@ -245,7 +261,9 @@ public class DefaultCpuOps<V>(private val dataFactory: TensorDataFactory) : Tens
245261
@Suppress("UNCHECKED_CAST")
246262
acc as V
247263
}
248-
sk.ainet.lang.types.Int32::class -> {
264+
265+
sk.ainet.lang.types.Int32::class,
266+
sk.ainet.lang.types.Int8::class -> {
249267
var acc = 0
250268
var k = 0
251269
while (k < kA) {
@@ -271,6 +289,7 @@ public class DefaultCpuOps<V>(private val dataFactory: TensorDataFactory) : Tens
271289
@Suppress("UNCHECKED_CAST")
272290
acc as V
273291
}
292+
274293
else -> throw IllegalArgumentException("Unsupported dtype for matmul: ${a.dtype}")
275294
}
276295
}

skainet-backends/skainet-backend-cpu/src/commonTest/kotlin/sk/ainet/sk/ainet/exec/tensor/ops/DefaultCpuOpsMatmulTest.kt

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import sk.ainet.context.DirectCpuExecutionContext
1212
import sk.ainet.lang.tensor.dsl.tensor
1313
import sk.ainet.lang.tensor.matmul
1414
import sk.ainet.lang.tensor.pprint
15+
import sk.ainet.lang.types.Int8
1516

1617
class DefaultCpuOpsMatmulTest {
1718
private val dataFactory = DenseTensorDataFactory()
@@ -23,18 +24,27 @@ class DefaultCpuOpsMatmulTest {
2324
return VoidOpsTensor(data, FP32::class)
2425
}
2526

27+
private fun iTensor(shape: Shape, values: IntArray): VoidOpsTensor<Int8, Int> {
28+
val data = dataFactory.fromIntArray<Int8, Int>(shape, Int8::class, values)
29+
return VoidOpsTensor(data, Int8::class)
30+
}
31+
2632
@Test
2733
fun matmul_fp32_2d() {
2834
// 2x3 @ 3x2 = 2x2
29-
val a = fTensor(Shape(2, 3), floatArrayOf(
30-
1f, 2f, 3f,
31-
4f, 5f, 6f
32-
))
33-
val b = fTensor(Shape(3, 2), floatArrayOf(
34-
7f, 8f,
35-
9f, 10f,
36-
11f, 12f
37-
))
35+
val a = fTensor(
36+
Shape(2, 3), floatArrayOf(
37+
1f, 2f, 3f,
38+
4f, 5f, 6f
39+
)
40+
)
41+
val b = fTensor(
42+
Shape(3, 2), floatArrayOf(
43+
7f, 8f,
44+
9f, 10f,
45+
11f, 12f
46+
)
47+
)
3848
val r = cpuOpsF.matmul(a, b)
3949
assertEquals(Shape(2, 2), r.shape)
4050
// Manual result
@@ -49,8 +59,8 @@ class DefaultCpuOpsMatmulTest {
4959
@Test
5060
fun matmul_fp32_batched3d() {
5161
// batch=2, (2, 3, 4) @ (2, 4, 2) -> (2, 3, 2)
52-
val a = fTensor(Shape(2, 3, 4), FloatArray(2*3*4) { it.toFloat() + 1 })
53-
val b = fTensor(Shape(2, 4, 2), FloatArray(2*4*2) { (it % 5).toFloat() + 1 })
62+
val a = fTensor(Shape(2, 3, 4), FloatArray(2 * 3 * 4) { it.toFloat() + 1 })
63+
val b = fTensor(Shape(2, 4, 2), FloatArray(2 * 4 * 2) { (it % 5).toFloat() + 1 })
5464
val r = cpuOpsF.matmul(a, b)
5565
assertEquals(Shape(2, 3, 2), r.shape)
5666
// spot-check a couple values by recomputing
@@ -71,11 +81,15 @@ class DefaultCpuOpsMatmulTest {
7181
@Test
7282
fun matmul_fp32_bchw_images_last2dims() {
7383
// Treat last two dims as (H,W) x (W,N) per BCH batch/channel
74-
val B = 2; val C = 3; val H = 2; val W = 3; val N = 2
84+
val B = 2;
85+
val C = 3;
86+
val H = 2;
87+
val W = 3;
88+
val N = 2
7589
val aShape = Shape(B, C, H, W)
7690
val bShape = Shape(B, C, W, N) // broadcast over B,C exact match
77-
val a = fTensor(aShape, FloatArray(B*C*H*W) { (it + 1).toFloat() })
78-
val b = fTensor(bShape, FloatArray(B*C*W*N) { ((it % 7) + 1).toFloat() })
91+
val a = fTensor(aShape, FloatArray(B * C * H * W) { (it + 1).toFloat() })
92+
val b = fTensor(bShape, FloatArray(B * C * W * N) { ((it % 7) + 1).toFloat() })
7993

8094
val r = cpuOpsF.matmul(a, b)
8195
assertEquals(Shape(B, C, H, N), r.shape)
@@ -93,6 +107,36 @@ class DefaultCpuOpsMatmulTest {
93107
assertEquals(ref(1, 2, 1, 1), r.data[1, 2, 1, 1])
94108
}
95109

110+
@Test
111+
fun matmul_Int8_bchw_images_last2dims() {
112+
// Treat last two dims as (H,W) x (W,N) per BCH batch/channel
113+
val B = 2;
114+
val C = 3;
115+
val H = 2;
116+
val W = 3;
117+
val N = 2
118+
val aShape = Shape(B, C, H, W)
119+
val bShape = Shape(B, C, W, N) // broadcast over B,C exact match
120+
val a = iTensor(aShape, IntArray(B * C * H * W) { (it + 1) })
121+
val b = iTensor(bShape, IntArray(B * C * W * N) { ((it % 7) + 1) })
122+
123+
val r = cpuOpsI.matmul(a, b)
124+
assertEquals(Shape(B, C, H, N), r.shape)
125+
// Validate per-slice via zero-copy slicing semantics by direct indexing
126+
fun ref(bi: Int, ci: Int, h: Int, n: Int): Int {
127+
var acc = 0
128+
for (w in 0 until W) {
129+
val av = a.data[bi, ci, h, w]
130+
val bv = b.data[bi, ci, w, n]
131+
acc += av * bv
132+
}
133+
return acc
134+
}
135+
assertEquals(ref(0, 0, 0, 0), r.data[0, 0, 0, 0])
136+
assertEquals(ref(1, 2, 1, 1), r.data[1, 2, 1, 1])
137+
}
138+
139+
96140
@Test
97141
fun matmul_mismatched_inner_dims_throws() {
98142
val a = fTensor(Shape(2, 3), FloatArray(6) { 1f })
@@ -106,8 +150,13 @@ class DefaultCpuOpsMatmulTest {
106150
val b = fTensor(Shape(0, 3), FloatArray(0))
107151
val r = cpuOpsF.matmul(a, b)
108152
assertEquals(Shape(2, 3), r.shape)
109-
// All zeros by definition of sum over empty set
110-
assertEquals(0, r.shape.volume)
153+
// All zeros by definition of sum over empty set (result shape still has volume 2*3 = 6)
154+
assertEquals(6, r.shape.volume)
155+
// Spot-check that elements are zero
156+
if (r.shape.volume > 0) {
157+
assertEquals(0f, r.data[0, 0])
158+
assertEquals(0f, r.data[1, 2])
159+
}
111160
}
112161

113162
@Test
@@ -116,12 +165,13 @@ class DefaultCpuOpsMatmulTest {
116165
execute(ctx) {
117166
val a = tensor<FP32, Float> {
118167
shape(2, 3) {
119-
init { idx -> (idx[0]*3 + idx[1] + 1).toFloat() }
168+
init { idx -> (idx[0] * 3 + idx[1] + 1).toFloat() }
120169
}
121170
}
122171
val b = tensor<FP32, Float> {
123172
shape(3, 2) {
124-
init { idx -> (idx[0]*2 + idx[1] + 1).toFloat() }
173+
// Match the non-DSL test matrix values: [[7,8],[9,10],[11,12]]
174+
init { idx -> (7 + idx[0] * 2 + idx[1]).toFloat() }
125175
}
126176
}
127177
val r = a.matmul(b)

skainet-lang/skainet-lang-core/src/commonMain/kotlin/sk/ainet/lang/tensor/data/DenseTensorDataFactory.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ public class DenseTensorDataFactory: TensorDataFactory {
508508
}
509509
createFloatTensorData(shape, data, FP32 as T) as TensorData<T, V>
510510
}
511-
Int32::class -> {
511+
Int8::class, Int32::class -> {
512512
val data = IntArray(shape.volume) { flatIndex ->
513513
val indices = IntArray(shape.dimensions.size)
514514
var remaining = flatIndex
@@ -578,6 +578,9 @@ public class DenseTensorDataFactory: TensorDataFactory {
578578
Int32::class -> {
579579
createIntTensorData(shape, data) as TensorData<T, V>
580580
}
581+
Int8::class -> {
582+
createIntTensorData(shape, data) as TensorData<T, V>
583+
}
581584
else -> throw IllegalArgumentException("fromIntArray only supports Int32 type: $dtype")
582585
}
583586
}

0 commit comments

Comments
 (0)