Skip to content

Commit 3c2ee83

Browse files
tianyizheng02github-actions
and
github-actions
committed
Fix mypy errors in lu_decomposition.py (attempt 2) (TheAlgorithms#8100)
* updating DIRECTORY.md * Fix mypy errors in lu_decomposition.py * Replace for-loops with comprehensions * Add explanation of LU decomposition and extra doctests Add an explanation of LU decomposition with conditions for when an LU decomposition exists Add extra doctests to handle each of the possible conditions for when a decomposition exists/doesn't exist * updating DIRECTORY.md * updating DIRECTORY.md --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
1 parent 8dfc81d commit 3c2ee83

File tree

1 file changed

+65
-26
lines changed

1 file changed

+65
-26
lines changed

arithmetic_analysis/lu_decomposition.py

+65-26
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,101 @@
1-
"""Lower-Upper (LU) Decomposition.
1+
"""
2+
Lower–upper (LU) decomposition factors a matrix as a product of a lower
3+
triangular matrix and an upper triangular matrix. A square matrix has an LU
4+
decomposition under the following conditions:
5+
- If the matrix is invertible, then it has an LU decomposition if and only
6+
if all of its leading principal minors are non-zero (see
7+
https://en.wikipedia.org/wiki/Minor_(linear_algebra) for an explanation of
8+
leading principal minors of a matrix).
9+
- If the matrix is singular (i.e., not invertible) and it has a rank of k
10+
(i.e., it has k linearly independent columns), then it has an LU
11+
decomposition if its first k leading principal minors are non-zero.
12+
13+
This algorithm will simply attempt to perform LU decomposition on any square
14+
matrix and raise an error if no such decomposition exists.
215
3-
Reference:
4-
- https://en.wikipedia.org/wiki/LU_decomposition
16+
Reference: https://en.wikipedia.org/wiki/LU_decomposition
517
"""
618
from __future__ import annotations
719

820
import numpy as np
9-
from numpy import float64
10-
from numpy.typing import ArrayLike
11-
1221

13-
def lower_upper_decomposition(
14-
table: ArrayLike[float64],
15-
) -> tuple[ArrayLike[float64], ArrayLike[float64]]:
16-
"""Lower-Upper (LU) Decomposition
17-
18-
Example:
1922

23+
def lower_upper_decomposition(table: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
24+
"""
25+
Perform LU decomposition on a given matrix and raises an error if the matrix
26+
isn't square or if no such decomposition exists
2027
>>> matrix = np.array([[2, -2, 1], [0, 1, 2], [5, 3, 1]])
21-
>>> outcome = lower_upper_decomposition(matrix)
22-
>>> outcome[0]
28+
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
29+
>>> lower_mat
2330
array([[1. , 0. , 0. ],
2431
[0. , 1. , 0. ],
2532
[2.5, 8. , 1. ]])
26-
>>> outcome[1]
33+
>>> upper_mat
2734
array([[ 2. , -2. , 1. ],
2835
[ 0. , 1. , 2. ],
2936
[ 0. , 0. , -17.5]])
3037
38+
>>> matrix = np.array([[4, 3], [6, 3]])
39+
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
40+
>>> lower_mat
41+
array([[1. , 0. ],
42+
[1.5, 1. ]])
43+
>>> upper_mat
44+
array([[ 4. , 3. ],
45+
[ 0. , -1.5]])
46+
47+
# Matrix is not square
3148
>>> matrix = np.array([[2, -2, 1], [0, 1, 2]])
32-
>>> lower_upper_decomposition(matrix)
49+
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
3350
Traceback (most recent call last):
3451
...
3552
ValueError: 'table' has to be of square shaped array but got a 2x3 array:
3653
[[ 2 -2 1]
3754
[ 0 1 2]]
55+
56+
# Matrix is invertible, but its first leading principal minor is 0
57+
>>> matrix = np.array([[0, 1], [1, 0]])
58+
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
59+
Traceback (most recent call last):
60+
...
61+
ArithmeticError: No LU decomposition exists
62+
63+
# Matrix is singular, but its first leading principal minor is 1
64+
>>> matrix = np.array([[1, 0], [1, 0]])
65+
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
66+
>>> lower_mat
67+
array([[1., 0.],
68+
[1., 1.]])
69+
>>> upper_mat
70+
array([[1., 0.],
71+
[0., 0.]])
72+
73+
# Matrix is singular, but its first leading principal minor is 0
74+
>>> matrix = np.array([[0, 1], [0, 1]])
75+
>>> lower_mat, upper_mat = lower_upper_decomposition(matrix)
76+
Traceback (most recent call last):
77+
...
78+
ArithmeticError: No LU decomposition exists
3879
"""
39-
# Table that contains our data
40-
# Table has to be a square array so we need to check first
80+
# Ensure that table is a square array
4181
rows, columns = np.shape(table)
4282
if rows != columns:
4383
raise ValueError(
44-
f"'table' has to be of square shaped array but got a {rows}x{columns} "
45-
+ f"array:\n{table}"
84+
f"'table' has to be of square shaped array but got a "
85+
f"{rows}x{columns} array:\n{table}"
4686
)
87+
4788
lower = np.zeros((rows, columns))
4889
upper = np.zeros((rows, columns))
4990
for i in range(columns):
5091
for j in range(i):
51-
total = 0
52-
for k in range(j):
53-
total += lower[i][k] * upper[k][j]
92+
total = sum(lower[i][k] * upper[k][j] for k in range(j))
93+
if upper[j][j] == 0:
94+
raise ArithmeticError("No LU decomposition exists")
5495
lower[i][j] = (table[i][j] - total) / upper[j][j]
5596
lower[i][i] = 1
5697
for j in range(i, columns):
57-
total = 0
58-
for k in range(i):
59-
total += lower[i][k] * upper[k][j]
98+
total = sum(lower[i][k] * upper[k][j] for k in range(j))
6099
upper[i][j] = table[i][j] - total
61100
return lower, upper
62101

0 commit comments

Comments
 (0)