2626__all__ = [
2727 "get_eigenbasis_eigh" ,
2828 "get_eigenbasis_qr" ,
29- "sort_eigenbasis_by_approx_eigvals" ,
3029 "get_eigenbasis_svd" ,
3130]
3231
3332
34- def sort_eigenbasis_by_approx_eigvals (
35- kronecker_factor_list : TensorList ,
36- eigenbasis_list : TensorList ,
37- exp_avg_sq : torch .Tensor ,
38- ) -> tuple [TensorList , torch .Tensor ]:
39- """Permute each eigenbasis and matching ``exp_avg_sq`` axis by descending approximate eigenvalues.
40-
41- Both the eigh and QR eigenbasis-update paths consume the sorted output: the eigh path discards
42- the permuted eigenbasis but uses the permuted ``exp_avg_sq`` (the sort_idx best approximates the
43- permutation component of the eigh-vs-old basis change under small drift); the QR path power-
44- iterates from the pre-sorted basis.
45-
46- Args:
47- kronecker_factor_list: List of preconditioner matrices (L and R).
48- eigenbasis_list: List of current eigenbases (QL and QR).
49- exp_avg_sq: Inner Adam second moment tensor permuted along each Kronecker-factor
50- axis to match the new descending-eigenvalue column ordering.
51-
52- Returns:
53- ``(sorted_eigenbasis_list, sorted_exp_avg_sq)``.
54- """
55- sorted_eigenbasis_list : TensorList = []
56- sorted_exp_avg_sq = exp_avg_sq
57- for ind , (kronecker_factor , eigenbasis ) in enumerate (zip (kronecker_factor_list , eigenbasis_list , strict = True )):
58- approx_eigvals = eig_utils .conjugate (kronecker_factor , eigenbasis , diag = True )
59- sort_idx = torch .argsort (approx_eigvals , descending = True )
60- sorted_eigenbasis_list .append (eigenbasis [:, sort_idx ])
61- sorted_exp_avg_sq = sorted_exp_avg_sq .index_select (ind , sort_idx )
62- return sorted_eigenbasis_list , sorted_exp_avg_sq
63-
64-
6533def get_eigenbasis_eigh (
6634 kronecker_factor_list : TensorList ,
6735) -> tuple [TensorList , TensorList ]:
@@ -73,17 +41,6 @@ def get_eigenbasis_eigh(
7341 Returns:
7442 Tuple of (list of eigenvalues in descending order, list of orthonormal kronecker factor
7543 eigenbases matrices).
76-
77- Example:
78- .. code-block:: python
79-
80- # Create sample Kronecker factors (symmetric positive definite matrices)
81- k_factor1 = torch.randn(4, 4)
82- k_factor1 = k_factor1 @ k_factor1.T # Make symmetric positive definite
83- k_factor2 = torch.randn(5, 5)
84- k_factor2 = k_factor2 @ k_factor2.T # Make symmetric positive definite
85-
86- eigvals_list, ortho_matrices = get_eigenbasis_eigh([k_factor1, k_factor2])
8744 """
8845 updated_eigenbasis_list : TensorList = []
8946 updated_eigvals_list : TensorList = []
@@ -111,19 +68,6 @@ def get_eigenbasis_svd(
11168
11269 Returns:
11370 List of orthonormal kronecker factor eigenbases matrices
114-
115- Example:
116- .. code-block:: python
117-
118- # Create sample Kronecker factors (symmetric positive definite matrices)
119- k_factor1 = torch.randn(4, 4)
120- k_factor1 = k_factor1 @ k_factor1.T # Make symmetric positive definite
121- k_factor2 = torch.randn(5, 5)
122- k_factor2 = k_factor2 @ k_factor2.T # Make symmetric positive definite
123-
124- # Get orthogonal matrices for these factors
125- ortho_matrices = get_eigenbasis_svd([k_factor1, k_factor2])
126- # ortho_matrices[0] has shape [4, 4] and ortho_matrices[1] has shape [5, 5]
12771 """
12872 updated_eigenbasis_list : TensorList = []
12973
@@ -137,63 +81,44 @@ def get_eigenbasis_svd(
13781def get_eigenbasis_qr (
13882 kronecker_factor_list : TensorList ,
13983 eigenbasis_list : TensorList ,
84+ exp_avg_sq : torch .Tensor ,
14085 power_iter_steps : int = 1 ,
141- ) -> tuple [TensorList , TensorList ]:
86+ ) -> tuple [TensorList , TensorList , torch . Tensor ]:
14287 """Updates the eigenbases of the preconditioner using power iteration and QR.
14388
144- Computes using multiple rounds of power iteration followed by QR decomposition (orthogonal iteration).
145- ``eigenbasis_list`` is expected to be already sorted by descending approximate eigenvalues (see
146- :func:`sort_eigenbasis_by_approx_eigvals`).
147-
148- The approximate eigenvalues of each kronecker factor in its updated eigenbasis (the Rayleigh
149- quotients :math:` \\ mathrm{diag}(Q^{ \\ top} K Q)`) are also computed .
89+ Each eigenbasis is refined with multiple rounds of power iteration followed by QR decomposition
90+ (orthogonal iteration), then its columns are sorted by descending approximate eigenvalues of the
91+ kronecker factor (the Rayleigh quotients :math:` \\ mathrm{diag}(Q^{ \\ top} K Q)`), so the returned
92+ eigenvalues and eigenbases are in descending order like :func:`get_eigenbasis_eigh`.
93+ ``exp_avg_sq`` is permuted along each kronecker- factor axis by the same sort, so its per-slot
94+ statistics stay aligned with the eigenbasis columns .
15095
15196 Args:
15297 kronecker_factor_list: List of preconditioner matrices (L and R).
15398 eigenbasis_list: List of current eigenbases (QL and QR).
99+ exp_avg_sq: Inner Adam second moment tensor, permuted along each kronecker-factor axis to
100+ match the sorted eigenbasis columns.
154101 power_iter_steps: Number of power iteration steps to perform before QR decomposition.
155102 More steps can lead to better convergence but increased computation time.
156103
157104 Returns:
158105 Tuple of (list of approximate eigenvalues of each kronecker factor in its updated eigenbasis,
159- updated list of orthonormal eigenbases (QL and QR)).
160-
161- Example:
162- .. code-block:: python
163-
164- # Create sample Kronecker factors (symmetric positive definite matrices)
165- n, m = 10, 20
166- k_factor1 = torch.randn(n, n)
167- k_factor1 = k_factor1 @ k_factor1.T # Make symmetric positive definite
168- k_factor2 = torch.randn(m, m)
169- k_factor2 = k_factor2 @ k_factor2.T # Make symmetric positive definite
170-
171- # Get orthogonal matrices for these kronecker factors
172- kronecker_factor_list = [k_factor1, k_factor2]
173- _, eigenbasis_list = get_eigenbasis_eigh(kronecker_factor_list)
174-
175- # Perturb the kronecker factor matrices, simulating the effect of gradient updates
176- perturbation = 1e-2*torch.randn(n, m)
177- perturbed_kronecker_factor_list = [None, None]
178- perturbed_kronecker_factor_list[0] = k_factor1 + perturbation@perturbation.T
179- perturbed_kronecker_factor_list[1] = k_factor2 + perturbation.T@perturbation
180-
181- # Refine the orthogonal matrices using QR (eigenbasis_list already sorted)
182- eigvals_list, updated_ortho_matrices = get_eigenbasis_qr(
183- perturbed_kronecker_factor_list,
184- eigenbasis_list,
185- )
106+ in descending order, updated list of orthonormal eigenbases (QL and QR) with columns ordered to
107+ match, ``exp_avg_sq`` permuted to match).
186108 """
187109 updated_eigenbasis_list : TensorList = []
188110 updated_eigvals_list : TensorList = []
189- for kronecker_factor , eigenbasis in zip (kronecker_factor_list , eigenbasis_list , strict = True ):
111+ for ind , ( kronecker_factor , eigenbasis ) in enumerate ( zip (kronecker_factor_list , eigenbasis_list , strict = True ) ):
190112 Q = eig_utils .orthogonal_iteration (
191113 kronecker_factor = kronecker_factor ,
192114 eigenbasis = eigenbasis ,
193115 power_iter_steps = power_iter_steps ,
194116 )
195- updated_eigenbasis_list .append (Q )
196117 with utils .fp32_matmul_precision ("highest" ):
197- updated_eigvals_list .append (eig_utils .conjugate (kronecker_factor , Q , diag = True ))
118+ approx_eigvals = eig_utils .conjugate (kronecker_factor , Q , diag = True )
119+ approx_eigvals , sort_idx = torch .sort (approx_eigvals , descending = True )
120+ updated_eigvals_list .append (approx_eigvals )
121+ updated_eigenbasis_list .append (Q [:, sort_idx ])
122+ exp_avg_sq = exp_avg_sq .index_select (ind , sort_idx )
198123
199- return updated_eigvals_list , updated_eigenbasis_list
124+ return updated_eigvals_list , updated_eigenbasis_list , exp_avg_sq
0 commit comments