@@ -12,6 +12,7 @@ import sk.ainet.context.DirectCpuExecutionContext
1212import sk.ainet.lang.tensor.dsl.tensor
1313import sk.ainet.lang.tensor.matmul
1414import sk.ainet.lang.tensor.pprint
15+ import sk.ainet.lang.types.Int8
1516
1617class 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)
0 commit comments