@@ -2829,24 +2829,21 @@ def max_singular_value_power_iter(A_outer: Tensor, max_abs: Optional[Tensor] = N
28292829 Rayleigh quotient of row with the largest norm + optional power iterations
28302830 """
28312831 x_norm , max_idx = A_outer .norm (dim = 1 ).max (dim = 0 )
2832- x_norm = promote (x_norm )
2832+ x_norm = promote (x_norm ). clamp ( min = torch . finfo ( torch . float32 ). tiny )
28332833
2834- def _inner ():
2835- A = A_outer
2836- x = A .index_select (0 , max_idx ).flatten ().contiguous ()
2837- A = stochastic_round_ (A / x_norm )
2838- x = x / x_norm
2834+ A = A_outer
2835+ x = A .index_select (0 , max_idx ).flatten ().contiguous ()
2836+ A = stochastic_round_ (A / x_norm )
2837+ x = x / x_norm
28392838
2840- def _mv (x ):
2841- return promote (A .T .mv (A .mv (x .to (A .dtype ))))
2839+ def _mv (x ):
2840+ return promote (A .T .mv (A .mv (x .to (A .dtype ))))
28422841
2843- for _ in range (iterations ):
2844- # A @ A.T @ x, but explicitly telling torch.compile not to compute the full matrix
2845- x = F .normalize (_mv (x ), dim = 0 )
2846- out = (promote (x ) @ _mv (x )).to (x_norm .dtype ).sqrt () * x_norm
2847- return out .squeeze ().clone ()
2848-
2849- return cond (x_norm > 0 , _inner , lambda : x_norm .squeeze ().clone ())
2842+ for _ in range (iterations ):
2843+ # A @ A.T @ x, but explicitly telling torch.compile not to compute the full matrix
2844+ x = F .normalize (_mv (x ), dim = 0 )
2845+ out = (promote (x ) @ _mv (x )).to (x_norm .dtype ).sqrt () * x_norm
2846+ return out .squeeze ()
28502847
28512848
28522849@decorator_knowngood
@@ -2904,30 +2901,25 @@ def max_eigenvalue_spd(A_outer: Tensor, power_iter: int = 4) -> Tensor:
29042901 if A_outer .ndim < 2 :
29052902 return A_outer .max ()
29062903 x_norm , max_idx = A_outer .norm (dim = 1 ).max (dim = 0 )
2907- x_norm = promote (x_norm )
2908-
2909- def _inner ():
2910- x = A_outer .index_select (0 , max_idx ).flatten ().contiguous ()
2911- A = promote (A_outer ) / x_norm
2912- x = x / x_norm
2904+ x_norm = promote (x_norm ).clamp (min = torch .finfo (torch .float32 ).tiny )
29132905
2914- def _mv (x ):
2915- return promote ((x @ A .mT ) @ A .mT )
2906+ x = A_outer .index_select (0 , max_idx ).flatten ().contiguous ()
2907+ A = promote (A_outer ) / x_norm
2908+ x = x / x_norm
29162909
2917- for _ in range (power_iter ):
2918- x = F .normalize (_mv (x ), dim = 0 )
2919- return (x @ _mv (x )).sqrt () * x_norm
2910+ def _mv (x ):
2911+ return promote ((x @ A .mT ) @ A .mT )
29202912
2921- return cond (x_norm > 0 , _inner , lambda : x_norm .squeeze ().clone ()).squeeze ()
2913+ for _ in range (power_iter ):
2914+ x = F .normalize (_mv (x ), dim = 0 )
2915+ return ((x @ _mv (x )).sqrt () * x_norm ).squeeze ()
29222916
29232917
29242918@decorator_knowngood
29252919def clamped_max_singular_value (
29262920 A : Tensor , min : float , max_svd : int = 0 , use_cholesky : bool = False , power_iter : int = 16
29272921) -> Tensor :
2928- norm = A .norm () # L2 norm is an upper bound for the spectral norm. If the upper bound is below the minimum, the real value will be too.
2929- out = cond (norm > min , lambda : max_singular_value (A , max_svd , use_cholesky , power_iter ), lambda : norm .clone ())
2930- return out .clamp (min = min )
2922+ return max_singular_value (A , max_svd , use_cholesky , power_iter ).clamp (min = min )
29312923
29322924
29332925@decorator_knowngood
@@ -2953,24 +2945,22 @@ def min_singular_value(
29532945
29542946 row_norms = A .norm (dim = 1 )
29552947 norm , idx = row_norms .min (dim = 0 )
2956- v = cond (norm > 0 , lambda : A .index_select (0 , idx ).flatten (), lambda : torch .rand_like (A [0 ]))
2948+ v = A .index_select (0 , idx ).flatten ()
2949+ v = v + torch .randn_like (v ) * torch .finfo (v .dtype ).tiny # break degeneracy if zero row
29572950
2958- v = v / promote (v .norm ())
2951+ v = v / promote (v .norm (). clamp ( min = torch . finfo ( torch . float32 ). tiny ) )
29592952 for _ in range (power_iter ):
29602953 v = lambda_upper * v - promote (A .mv (v .to (A .dtype )))
2961- v = v / promote (v .norm ())
2954+ v = v / promote (v .norm (). clamp ( min = torch . finfo ( torch . float32 ). tiny ) )
29622955 mu_hat = promote (v ) @ (lambda_upper * promote (v ) - promote (A .mv (v .to (A .dtype ))))
29632956
29642957 lambda_min_hat = lambda_upper - mu_hat
29652958
2966- def _approx ():
2967- mu = A .trace () / n
2968- sigma_square = A .square ().sum () / n - mu ** 2
2969- return mu - (sigma_square / (n - 1 )).sqrt ()
2959+ mu = A .trace () / n
2960+ sigma_square = A .square ().sum () / n - mu ** 2
2961+ approx = mu - (sigma_square / (n - 1 )).sqrt ()
29702962
2971- return cond (
2972- (~ torch .isfinite (lambda_min_hat )) | (lambda_min_hat <= 0 ), _approx , lambda : lambda_min_hat .clone ()
2973- ).squeeze ()
2963+ return torch .where ((~ torch .isfinite (lambda_min_hat )) | (lambda_min_hat <= 0 ), approx , lambda_min_hat ).squeeze ()
29742964
29752965
29762966@decorator_knowngood
@@ -3162,14 +3152,12 @@ def psgd_pro_update_precond(
31623152
31633153 # procrustes_step
31643154 R = (q_ .T - q_ ).contiguous ()
3165- R_norm = max_singular_value (R , power_iter = power_iter ) + torch .finfo (R .dtype ).smallest_normal
3166- R = R / R_norm
3155+ R = R / (max_singular_value (R , power_iter = power_iter ) + torch .finfo (R .dtype ).smallest_normal )
31673156 RQ = R @ q_
31683157 RRQ = R @ RQ
3169- tr_RQ = RQ .diagonal ().sum ()
3170- tr_RRQ = RRQ .diagonal ().sum ()
3171- a = torch .where (tr_RRQ < 0 , (- tr_RQ / tr_RRQ ).clamp (max = max_step_size ), max_step_size )
3172- copy_stochastic_ (q , q_ + a * RQ + 0.5 * a * a * RRQ )
3158+ c1 , c2 = RQ .diagonal ().sum (), RRQ .diagonal ().sum ()
3159+ a = torch .where (c2 < 0 , (- c1 / c2 ).clamp (min = 0 , max = 0.5 ), 0.5 )
3160+ copy_stochastic_ (q , q_ + a * RQ + (0.5 * a * a ) * RRQ )
31733161
31743162
31753163@decorator_knowngood
0 commit comments