1+ from collections import namedtuple
2+
13import pytest
24import torch
35
46import flag_gems
57
6- from . import base
8+ from . import base , consts
9+ from .conftest import Config
710
811DEVICE = flag_gems .device
912VENDOR = flag_gems .vendor_name
1922else :
2023 _PIVOT_VALUES = [True ]
2124
25+ if VENDOR == "ascend" :
26+ Config .mode = consts .BenchMode .OPERATOR
27+
28+ LINALG_LU_FACTOR_SHAPE = [
29+ [16 , 16 ],
30+ [32 , 32 ],
31+ [64 , 64 ],
32+ [128 , 128 ],
33+ [256 , 256 ],
34+ [1024 , 512 ],
35+ [32 , 16 ],
36+ [16 , 32 ],
37+ [128 , 64 ],
38+ [64 , 128 ],
39+ [4 , 32 , 32 ],
40+ [128 , 16 , 16 ],
41+ [1024 , 512 , 512 ],
42+ ]
43+
44+ LinalgLUFactorResult = namedtuple ("LinalgLUFactorResult" , ["LU" , "pivots" ])
45+
46+
47+ def _swap_rows (lu , i , pivot_row ):
48+ * batch_shape , m , n = lu .shape
49+ device = lu .device
50+
51+ rows = torch .arange (m , device = device ).expand (* batch_shape , - 1 )
52+
53+ mask_i = (rows == i ).float ().unsqueeze (- 1 )
54+ mask_p = (rows == pivot_row .unsqueeze (- 1 )).float ().unsqueeze (- 1 )
55+
56+ row_i_vals = (lu * mask_i ).sum (dim = - 2 , keepdim = True )
57+ row_p_vals = (lu * mask_p ).sum (dim = - 2 , keepdim = True )
58+
59+ mask_i_full = mask_i .expand (* batch_shape , m , n )
60+ mask_p_full = mask_p .expand (* batch_shape , m , n )
61+ diff_ip = (row_p_vals - row_i_vals ).expand (* batch_shape , m , n )
62+ diff_pi = (row_i_vals - row_p_vals ).expand (* batch_shape , m , n )
63+
64+ lu = lu + mask_i_full * diff_ip
65+ lu = lu + mask_p_full * diff_pi
66+ return lu
67+
68+
69+ def _lu_factor_pivot (lu , m , n , k ):
70+ * batch_shape , _ , _ = lu .shape
71+ device = lu .device
72+ pivots = torch .empty ((* batch_shape , k ), dtype = torch .int32 , device = device )
73+
74+ for i in range (k ):
75+ col = lu [..., i :, i ].abs ()
76+ pivot_rel = torch .argmax (col , dim = - 1 )
77+ pivot_row = pivot_rel + i
78+ pivots [..., i ] = (pivot_row + 1 ).to (torch .int32 )
79+
80+ lu = _swap_rows (lu , i , pivot_row )
81+
82+ pivot_val = lu [..., i , i ]
83+ lu [..., i + 1 :, i ] = lu [..., i + 1 :, i ] / pivot_val .unsqueeze (- 1 )
84+
85+ if i + 1 < m and i + 1 < n :
86+ l_col = lu [..., i + 1 :, i ].unsqueeze (- 1 )
87+ u_row = lu [..., i : i + 1 , i + 1 :]
88+ lu [..., i + 1 :, i + 1 :] = lu [..., i + 1 :, i + 1 :] - l_col @ u_row
89+
90+ return lu , pivots
91+
92+
93+ def _lu_factor_no_pivot (lu , m , n , k ):
94+ * batch_shape , _ , _ = lu .shape
95+ device = lu .device
96+ pivots = torch .empty ((* batch_shape , k ), dtype = torch .int32 , device = device )
97+
98+ for i in range (k ):
99+ pivots [..., i ] = i + 1
100+ pivot_val = lu [..., i , i ]
101+ lu [..., i + 1 :, i ] = lu [..., i + 1 :, i ] / pivot_val .unsqueeze (- 1 )
102+
103+ if i + 1 < m and i + 1 < n :
104+ l_col = lu [..., i + 1 :, i ].unsqueeze (- 1 )
105+ u_row = lu [..., i : i + 1 , i + 1 :]
106+ lu [..., i + 1 :, i + 1 :] = lu [..., i + 1 :, i + 1 :] - l_col @ u_row
107+
108+ return lu , pivots
109+
110+
111+ def ops_lu_factor (input , * , pivot = True ):
112+ if input .dim () < 2 :
113+ raise RuntimeError (
114+ "torch.linalg.lu_factor: Expected input to have at least 2 dimensions"
115+ )
116+ if input .dtype != torch .float32 :
117+ raise NotImplementedError ("Only float32 is supported" )
118+ m , n = input .shape [- 2 ], input .shape [- 1 ]
119+ if m == 0 or n == 0 :
120+ raise NotImplementedError ("Empty matrices are not supported" )
121+ if pivot not in (True , False ):
122+ raise TypeError (f"pivot must be a bool, got { type (pivot )} " )
123+
124+ input_contiguous = input .contiguous ()
125+ m , n = input_contiguous .shape [- 2 ], input_contiguous .shape [- 1 ]
126+ k = min (m , n )
127+ lu = input_contiguous .clone ()
128+
129+ if pivot :
130+ lu , pivots = _lu_factor_pivot (lu , m , n , k )
131+ else :
132+ lu , pivots = _lu_factor_no_pivot (lu , m , n , k )
133+
134+ return LinalgLUFactorResult (lu , pivots )
135+
136+
137+ if VENDOR == "ascend" :
138+
139+ def _torch_lu_factor (inp , * , pivot = True , out = None ):
140+ """Vendor-aware wrapper: uses torch ops on Ascend, native on other vendors."""
141+ res = ops_lu_factor (inp , pivot = pivot )
142+ if out is not None :
143+ lu_out , piv_out = out
144+ lu_out .copy_ (res .LU )
145+ piv_out .copy_ (res .pivots )
146+ return lu_out , piv_out
147+ return res .LU , res .pivots
148+
149+ else :
150+ _torch_lu_factor = torch .linalg .lu_factor
151+
22152
23153class LinalgLuFactorBenchmark (base .Benchmark ):
24154 DEFAULT_SHAPE_DESC = "input shape, pivot"
25155 DEFAULT_DTYPES = _TEST_DTYPES
156+ # mode = base.Config.mode if VENDOR != "ascend" else consts.BenchMode.OPERATOR
26157
27158 def get_input_iter (self , dtype ):
28- for inp_shape in self . shapes :
159+ for inp_shape in LINALG_LU_FACTOR_SHAPE :
29160 inp_shape = tuple (inp_shape )
30161 for pivot in _PIVOT_VALUES :
31162 inp = torch .randn (inp_shape , dtype = dtype , device = self .device )
@@ -36,10 +167,10 @@ def get_input_iter(self, dtype):
36167def test_linalg_lu_factor ():
37168 bench = LinalgLuFactorBenchmark (
38169 op_name = "linalg_lu_factor" ,
39- torch_op = torch .linalg .lu_factor ,
170+ torch_op = _torch_lu_factor ,
171+ gems_op = flag_gems .linalg_lu_factor ,
40172 dtypes = _TEST_DTYPES ,
41173 )
42- bench .set_gems (flag_gems .linalg_lu_factor )
43174 bench .run ()
44175
45176
@@ -48,7 +179,7 @@ class LinalgLuFactorOutBenchmark(base.Benchmark):
48179 DEFAULT_DTYPES = _TEST_DTYPES
49180
50181 def get_input_iter (self , dtype ):
51- for inp_shape in self . shapes :
182+ for inp_shape in LINALG_LU_FACTOR_SHAPE :
52183 inp_shape = tuple (inp_shape )
53184 for pivot in _PIVOT_VALUES :
54185 k = min (inp_shape [- 2 ], inp_shape [- 1 ])
@@ -65,7 +196,9 @@ def get_input_iter(self, dtype):
65196def test_linalg_lu_factor_out ():
66197 bench = LinalgLuFactorOutBenchmark (
67198 op_name = "linalg_lu_factor_out" ,
68- torch_op = torch . linalg . lu_factor ,
199+ torch_op = _torch_lu_factor ,
69200 dtypes = _TEST_DTYPES ,
70201 )
202+ if VENDOR == "ascend" :
203+ bench .gems_op = flag_gems .linalg_lu_factor_out
71204 bench .run ()
0 commit comments