Coverage for src\pqlattice\linalg\_linalg.py: 91%
90 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-01-07 03:12 +0100
« prev ^ index » next coverage.py v7.11.0, created at 2026-01-07 03:12 +0100
1from functools import reduce
3import numpy as np
5from ..typing import Matrix, SquareMatrix, Vector, validate_aliases
6from ._utils import row_add, row_scale, row_swap
9@validate_aliases
10def hnf(A: Matrix) -> tuple[Matrix, SquareMatrix]:
11 """_summary_
13 Parameters
14 ----------
15 A : Matrix
16 _description_
18 Returns
19 -------
20 tuple[Matrix, SquareMatrix]
21 _description_
22 """
23 H, U, _ = _hnf(A)
24 return H, U
27@validate_aliases
28def _hnf(a: Matrix) -> tuple[Matrix, SquareMatrix, int]:
29 H = np.array(a, dtype=object)
30 m, n = H.shape
31 U = np.eye(m, dtype=object) # The transformation matrix
32 pivot_row = 0
33 pivot_col = 0
34 det_U = 1
36 while pivot_row < m and pivot_col < n:
37 # pivot selection
38 if np.all(H[pivot_row:, pivot_col] == 0):
39 pivot_col += 1
40 continue
42 candidates = [(abs(H[i, pivot_col]), i) for i in range(pivot_row, m) if H[i, pivot_col] != 0]
43 _, best_row = min(candidates)
45 row_swap(H, pivot_row, best_row)
46 row_swap(U, pivot_row, best_row)
47 det_U *= -1
49 # clear below pivot
50 for i in range(pivot_row + 1, m):
51 while H[i, pivot_col] != 0:
52 factor = H[i, pivot_col] // H[pivot_row, pivot_col]
54 row_add(H, i, pivot_row, -factor)
55 row_add(U, i, pivot_row, -factor)
57 if H[i, pivot_col] != 0:
58 row_swap(H, pivot_row, i)
59 row_swap(U, pivot_row, i)
60 det_U *= -1
62 if H[pivot_row, pivot_col] < 0:
63 row_scale(H, pivot_row, -1)
64 row_scale(U, pivot_row, -1)
65 det_U *= -1
67 pivot_val = H[pivot_row, pivot_col]
69 # Reduce rows above pivot
70 for i in range(pivot_row):
71 factor = H[i, pivot_col] // pivot_val
72 row_add(H, i, pivot_row, -factor)
73 row_add(U, i, pivot_row, -factor)
75 pivot_row += 1
76 pivot_col += 1
78 return H, U, det_U
81@validate_aliases
82def left_kernel(a: Matrix):
83 """_summary_
85 Parameters
86 ----------
87 a : Matrix
88 _description_
90 Returns
91 -------
92 _type_
93 _description_
94 """
95 return right_kernel(a.T)
98@validate_aliases
99def right_kernel(a: Matrix) -> Matrix:
100 """_summary_
102 Parameters
103 ----------
104 a : Matrix
105 _description_
107 Returns
108 -------
109 Matrix
110 _description_
111 """
112 H, U = hnf(a.T)
113 kernel_basis: list[Vector] = []
115 m, _ = H.shape
116 for i in range(m):
117 if np.all(H[i] == 0):
118 kernel_basis.append(U[i])
120 return np.array(kernel_basis, dtype=object)
123@validate_aliases
124def left_nullity(a: Matrix) -> int:
125 """_summary_
127 Parameters
128 ----------
129 a : Matrix
130 _description_
132 Returns
133 -------
134 int
135 _description_
136 """
137 kernel = left_kernel(a)
138 return kernel.shape[0]
141@validate_aliases
142def right_nullity(a: Matrix) -> int:
143 """_summary_
145 Parameters
146 ----------
147 a : Matrix
148 _description_
150 Returns
151 -------
152 int
153 _description_
154 """
155 kernel = right_kernel(a)
156 return kernel.shape[0]
159def rank(a: Matrix) -> int:
160 """_summary_
162 Parameters
163 ----------
164 a : Matrix
165 _description_
167 Returns
168 -------
169 int
170 _description_
171 """
172 m, n = a.shape
173 l_rank = m - left_nullity(a)
174 r_rank = n - right_nullity(a)
175 assert l_rank == r_rank
176 return l_rank
179@validate_aliases
180def det(A: SquareMatrix) -> int:
181 """_summary_
183 Parameters
184 ----------
185 A : SquareMatrix
186 _description_
188 Returns
189 -------
190 int
191 _description_
192 """
193 H, _, det_U = _hnf(A)
195 return reduce(lambda a, b: a * b, np.diagonal(H), 1) * det_U
198@validate_aliases
199def minor(A: SquareMatrix, i: int, j: int) -> int:
200 """_summary_
202 Parameters
203 ----------
204 A : SquareMatrix
205 _description_
206 i : int
207 _description_
208 j : int
209 _description_
211 Returns
212 -------
213 int
214 _description_
215 """
216 return det(np.delete(np.delete(A, i, axis=0), j, axis=1))
219@validate_aliases
220def cofactor(A: SquareMatrix, i: int, j: int) -> int:
221 """_summary_
223 Parameters
224 ----------
225 A : SquareMatrix
226 _description_
227 i : int
228 _description_
229 j : int
230 _description_
232 Returns
233 -------
234 int
235 _description_
236 """
237 return minor(A, i, j) * ((-1) ** (i + 1 + j + 1))
240@validate_aliases
241def cofactor_matrix(A: SquareMatrix) -> SquareMatrix:
242 """_summary_
244 Parameters
245 ----------
246 A : SquareMatrix
247 _description_
249 Returns
250 -------
251 SquareMatrix
252 _description_
253 """
254 n = A.shape[0]
255 C = np.zeros((n, n), dtype=object)
256 for i in range(n):
257 for j in range(n):
258 C[i, j] = cofactor(A, i, j)
259 return C