@@ -1058,16 +1058,14 @@ def _layout(a, kind):
10581058 return np .ascontiguousarray (a )
10591059 if kind == "F" :
10601060 return np .asfortranarray (a )
1061- if kind == "T" :
1062- return np .ascontiguousarray (a .T ).T
10631061 if kind == "S" :
10641062 big = np .zeros ((a .shape [0 ] * 2 , a .shape [1 ] * 2 ), dtype = a .dtype )
10651063 big [::2 , ::2 ] = a
10661064 return big [::2 , ::2 ]
10671065 raise ValueError (kind )
10681066
1069- @pytest .mark .parametrize ("a_kind" , ["C" , "F" , "T" , " S" ])
1070- @pytest .mark .parametrize ("b_kind" , ["C" , "F" , "T" , " S" ])
1067+ @pytest .mark .parametrize ("a_kind" , ["C" , "F" , "S" ])
1068+ @pytest .mark .parametrize ("b_kind" , ["C" , "F" , "S" ])
10711069 def test_gemm_all_layout_combinations (self , a_kind , b_kind ):
10721070 rng = np .random .default_rng (seed = 100 )
10731071 A_f = rng .standard_normal ((4 , 3 ))
@@ -1076,6 +1074,16 @@ def test_gemm_all_layout_combinations(self, a_kind, b_kind):
10761074 B_q = self ._layout (_qnd (B_f ), b_kind )
10771075 _assert_matmul_matches_float64 (A_q , B_q , A_f , B_f )
10781076
1077+ def test_transposed_view_operands (self ):
1078+ """Genuine transposed views (``a.T``) are zero-copy views that share
1079+ memory with the source array, unlike the F-order *copies* produced by
1080+ ``np.asfortranarray``. They exercise the transa/transb BLAS dispatch on
1081+ col-major views."""
1082+ rng = np .random .default_rng (seed = 104 )
1083+ A_f = rng .standard_normal ((3 , 4 ))
1084+ B_f = rng .standard_normal ((5 , 3 ))
1085+ _assert_matmul_matches_float64 (_qnd (A_f ).T , _qnd (B_f ).T , A_f .T , B_f .T )
1086+
10791087 @pytest .mark .parametrize ("out_kind" , ["C" , "F" ])
10801088 def test_gemm_output_layout (self , out_kind ):
10811089 rng = np .random .default_rng (seed = 101 )
@@ -1091,7 +1099,7 @@ def test_gemm_output_layout(self, out_kind):
10911099 for j in range (5 ):
10921100 assert abs (float (out [i , j ]) - ref [i , j ]) <= 1e-13 + 1e-13 * max (abs (ref [i , j ]), 1.0 )
10931101
1094- @pytest .mark .parametrize ("a_kind" , ["C" , "F" , "T" , " S" ])
1102+ @pytest .mark .parametrize ("a_kind" , ["C" , "F" , "S" ])
10951103 def test_gemv_all_layouts (self , a_kind ):
10961104 rng = np .random .default_rng (seed = 102 )
10971105 A_f = rng .standard_normal ((5 , 4 ))
@@ -1117,6 +1125,16 @@ def test_empty_core_dims_match_float64(self, A_shape, B_shape):
11171125 B_f = np .ones (B_shape )
11181126 _assert_matmul_matches_float64 (_qnd (A_f ), _qnd (B_f ), A_f , B_f )
11191127
1128+ def test_matmul_zero_contracted_dim (self ):
1129+ """Contracted dimension of zero: ``(m, 0) @ (0, n)`` is the all-zeros
1130+ ``(m, n)`` matrix (empty sum). Matches NumPy/float64."""
1131+ A_f = np .zeros ((4 , 0 ))
1132+ B_f = np .zeros ((0 , 5 ))
1133+ got = np .matmul (_qnd (A_f ), _qnd (B_f ))
1134+ assert got .shape == (4 , 5 )
1135+ assert np .all (got .astype (np .float64 ) == 0.0 )
1136+ _assert_matmul_matches_float64 (_qnd (A_f ), _qnd (B_f ), A_f , B_f )
1137+
11201138
11211139class TestMatmulCopyPath :
11221140 """Issue #89 Tier 2: layouts that are not directly BLAS-able (fully strided,
@@ -1137,6 +1155,13 @@ def test_negative_stride_input(self):
11371155 B_f = rng .standard_normal ((5 , 7 ))
11381156 _assert_matmul_matches_float64 (_qnd (A_f )[::- 1 ], _qnd (B_f ), A_f [::- 1 ], B_f )
11391157
1158+ def test_negative_column_stride_input (self ):
1159+ rng = np .random .default_rng (seed = 205 )
1160+ A_f = rng .standard_normal ((6 , 5 ))
1161+ B_f = rng .standard_normal ((5 , 7 ))
1162+ _assert_matmul_matches_float64 (_qnd (A_f )[:, ::- 1 ], _qnd (B_f ),
1163+ A_f [:, ::- 1 ], B_f )
1164+
11401165 def test_strided_A_fortran_B_fortran_out_three_temps (self ):
11411166 rng = np .random .default_rng (seed = 202 )
11421167 A_f = rng .standard_normal ((20 , 7 ))
@@ -1160,3 +1185,17 @@ def test_gemv_negative_stride_vector(self):
11601185 A_f = rng .standard_normal ((5 , 6 ))
11611186 x_f = rng .standard_normal ((6 ,))
11621187 _assert_matmul_matches_float64 (_qnd (A_f ), _qnd (x_f )[::- 1 ], A_f , x_f [::- 1 ])
1188+
1189+ def test_strided_out_vector (self ):
1190+ """Strided output buffer for the gemv path: ``out`` has a column stride
1191+ of two elements, so QBLAS must honour ``incy`` (or the scatter fallback)
1192+ when writing results back."""
1193+ rng = np .random .default_rng (seed = 206 )
1194+ A_f = rng .standard_normal ((7 , 5 ))
1195+ x_f = rng .standard_normal ((5 ,))
1196+ out = np .empty (14 , dtype = QuadPrecDType ())[::2 ]
1197+ np .matmul (_qnd (A_f ), _qnd (x_f ), out = out )
1198+ ref = A_f @ x_f
1199+ assert out .shape == (7 ,)
1200+ for i in range (7 ):
1201+ assert abs (float (out [i ]) - ref [i ]) <= 1e-13 + 1e-13 * max (abs (ref [i ]), 1.0 )
0 commit comments