Skip to content

Commit 8c29860

Browse files
authored
fix(mypy): type annotations for linear algebra algorithms (TheAlgorithms#4317)
* fix(mypy): type annotations for linear algebra algorithms * refactor: remove linear algebra directory from mypy exclude
1 parent 20c7518 commit 8c29860

8 files changed

+100
-74
lines changed

.github/workflows/build.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
python -m pip install mypy pytest-cov -r requirements.txt
2424
# FIXME: #4052 fix mypy errors in the exclude directories and remove them below
2525
- run: mypy --ignore-missing-imports
26-
--exclude '(data_structures|digital_image_processing|dynamic_programming|graphs|linear_algebra|maths|matrix|other|project_euler|scripts|searches|strings*)/$' .
26+
--exclude '(data_structures|digital_image_processing|dynamic_programming|graphs|maths|matrix|other|project_euler|scripts|searches|strings*)/$' .
2727
- name: Run tests
2828
run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. .
2929
- if: ${{ success() }}

linear_algebra/src/conjugate_gradient.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
- https://en.wikipedia.org/wiki/Conjugate_gradient_method
44
- https://en.wikipedia.org/wiki/Definite_symmetric_matrix
55
"""
6+
from typing import Any
7+
68
import numpy as np
79

810

9-
def _is_matrix_spd(matrix: np.array) -> bool:
11+
def _is_matrix_spd(matrix: np.ndarray) -> bool:
1012
"""
1113
Returns True if input matrix is symmetric positive definite.
1214
Returns False otherwise.
@@ -38,10 +40,11 @@ def _is_matrix_spd(matrix: np.array) -> bool:
3840
eigen_values, _ = np.linalg.eigh(matrix)
3941

4042
# Check sign of all eigenvalues.
41-
return np.all(eigen_values > 0)
43+
# np.all returns a value of type np.bool_
44+
return bool(np.all(eigen_values > 0))
4245

4346

44-
def _create_spd_matrix(dimension: np.int64) -> np.array:
47+
def _create_spd_matrix(dimension: int) -> Any:
4548
"""
4649
Returns a symmetric positive definite matrix given a dimension.
4750
@@ -64,11 +67,11 @@ def _create_spd_matrix(dimension: np.int64) -> np.array:
6467

6568

6669
def conjugate_gradient(
67-
spd_matrix: np.array,
68-
load_vector: np.array,
70+
spd_matrix: np.ndarray,
71+
load_vector: np.ndarray,
6972
max_iterations: int = 1000,
7073
tol: float = 1e-8,
71-
) -> np.array:
74+
) -> Any:
7275
"""
7376
Returns solution to the linear system np.dot(spd_matrix, x) = b.
7477
@@ -141,6 +144,8 @@ def conjugate_gradient(
141144

142145
# Update number of iterations.
143146
iterations += 1
147+
if iterations > max_iterations:
148+
break
144149

145150
return x
146151

linear_algebra/src/lib.py

+51-32
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import math
2424
import random
25+
from typing import Collection, Optional, Union, overload
2526

2627

2728
class Vector:
@@ -45,7 +46,7 @@ class Vector:
4546
TODO: compare-operator
4647
"""
4748

48-
def __init__(self, components=None):
49+
def __init__(self, components: Optional[Collection[float]] = None) -> None:
4950
"""
5051
input: components or nothing
5152
simple constructor for init the vector
@@ -54,7 +55,7 @@ def __init__(self, components=None):
5455
components = []
5556
self.__components = list(components)
5657

57-
def set(self, components):
58+
def set(self, components: Collection[float]) -> None:
5859
"""
5960
input: new components
6061
changes the components of the vector.
@@ -65,13 +66,13 @@ def set(self, components):
6566
else:
6667
raise Exception("please give any vector")
6768

68-
def __str__(self):
69+
def __str__(self) -> str:
6970
"""
7071
returns a string representation of the vector
7172
"""
7273
return "(" + ",".join(map(str, self.__components)) + ")"
7374

74-
def component(self, i):
75+
def component(self, i: int) -> float:
7576
"""
7677
input: index (start at 0)
7778
output: the i-th component of the vector.
@@ -81,22 +82,22 @@ def component(self, i):
8182
else:
8283
raise Exception("index out of range")
8384

84-
def __len__(self):
85+
def __len__(self) -> int:
8586
"""
8687
returns the size of the vector
8788
"""
8889
return len(self.__components)
8990

90-
def euclidLength(self):
91+
def euclidLength(self) -> float:
9192
"""
9293
returns the euclidean length of the vector
9394
"""
94-
summe = 0
95+
summe: float = 0
9596
for c in self.__components:
9697
summe += c ** 2
9798
return math.sqrt(summe)
9899

99-
def __add__(self, other):
100+
def __add__(self, other: "Vector") -> "Vector":
100101
"""
101102
input: other vector
102103
assumes: other vector has the same size
@@ -109,7 +110,7 @@ def __add__(self, other):
109110
else:
110111
raise Exception("must have the same size")
111112

112-
def __sub__(self, other):
113+
def __sub__(self, other: "Vector") -> "Vector":
113114
"""
114115
input: other vector
115116
assumes: other vector has the same size
@@ -122,7 +123,15 @@ def __sub__(self, other):
122123
else: # error case
123124
raise Exception("must have the same size")
124125

125-
def __mul__(self, other):
126+
@overload
127+
def __mul__(self, other: float) -> "Vector":
128+
...
129+
130+
@overload
131+
def __mul__(self, other: "Vector") -> float:
132+
...
133+
134+
def __mul__(self, other: Union[float, "Vector"]) -> Union[float, "Vector"]:
126135
"""
127136
mul implements the scalar multiplication
128137
and the dot-product
@@ -132,20 +141,20 @@ def __mul__(self, other):
132141
return Vector(ans)
133142
elif isinstance(other, Vector) and (len(self) == len(other)):
134143
size = len(self)
135-
summe = 0
144+
summe: float = 0
136145
for i in range(size):
137146
summe += self.__components[i] * other.component(i)
138147
return summe
139148
else: # error case
140149
raise Exception("invalid operand!")
141150

142-
def copy(self):
151+
def copy(self) -> "Vector":
143152
"""
144153
copies this vector and returns it.
145154
"""
146155
return Vector(self.__components)
147156

148-
def changeComponent(self, pos, value):
157+
def changeComponent(self, pos: int, value: float) -> None:
149158
"""
150159
input: an index (pos) and a value
151160
changes the specified component (pos) with the
@@ -156,7 +165,7 @@ def changeComponent(self, pos, value):
156165
self.__components[pos] = value
157166

158167

159-
def zeroVector(dimension):
168+
def zeroVector(dimension: int) -> Vector:
160169
"""
161170
returns a zero-vector of size 'dimension'
162171
"""
@@ -165,7 +174,7 @@ def zeroVector(dimension):
165174
return Vector([0] * dimension)
166175

167176

168-
def unitBasisVector(dimension, pos):
177+
def unitBasisVector(dimension: int, pos: int) -> Vector:
169178
"""
170179
returns a unit basis vector with a One
171180
at index 'pos' (indexing at 0)
@@ -177,7 +186,7 @@ def unitBasisVector(dimension, pos):
177186
return Vector(ans)
178187

179188

180-
def axpy(scalar, x, y):
189+
def axpy(scalar: float, x: Vector, y: Vector) -> Vector:
181190
"""
182191
input: a 'scalar' and two vectors 'x' and 'y'
183192
output: a vector
@@ -192,15 +201,15 @@ def axpy(scalar, x, y):
192201
return x * scalar + y
193202

194203

195-
def randomVector(N, a, b):
204+
def randomVector(N: int, a: int, b: int) -> Vector:
196205
"""
197206
input: size (N) of the vector.
198207
random range (a,b)
199208
output: returns a random vector of size N, with
200209
random integer components between 'a' and 'b'.
201210
"""
202211
random.seed(None)
203-
ans = [random.randint(a, b) for i in range(N)]
212+
ans = [random.randint(a, b) for _ in range(N)]
204213
return Vector(ans)
205214

206215

@@ -222,7 +231,7 @@ class Matrix:
222231
operator - _ implements the matrix-subtraction
223232
"""
224233

225-
def __init__(self, matrix, w, h):
234+
def __init__(self, matrix: list[list[float]], w: int, h: int) -> None:
226235
"""
227236
simple constructor for initializing
228237
the matrix with components.
@@ -231,7 +240,7 @@ def __init__(self, matrix, w, h):
231240
self.__width = w
232241
self.__height = h
233242

234-
def __str__(self):
243+
def __str__(self) -> str:
235244
"""
236245
returns a string representation of this
237246
matrix.
@@ -246,7 +255,7 @@ def __str__(self):
246255
ans += str(self.__matrix[i][j]) + "|\n"
247256
return ans
248257

249-
def changeComponent(self, x, y, value):
258+
def changeComponent(self, x: int, y: int, value: float) -> None:
250259
"""
251260
changes the x-y component of this matrix
252261
"""
@@ -255,7 +264,7 @@ def changeComponent(self, x, y, value):
255264
else:
256265
raise Exception("changeComponent: indices out of bounds")
257266

258-
def component(self, x, y):
267+
def component(self, x: int, y: int) -> float:
259268
"""
260269
returns the specified (x,y) component
261270
"""
@@ -264,13 +273,13 @@ def component(self, x, y):
264273
else:
265274
raise Exception("changeComponent: indices out of bounds")
266275

267-
def width(self):
276+
def width(self) -> int:
268277
"""
269278
getter for the width
270279
"""
271280
return self.__width
272281

273-
def height(self):
282+
def height(self) -> int:
274283
"""
275284
getter for the height
276285
"""
@@ -303,7 +312,15 @@ def determinate(self) -> float:
303312
else:
304313
raise Exception("matrix is not square")
305314

306-
def __mul__(self, other):
315+
@overload
316+
def __mul__(self, other: float) -> "Matrix":
317+
...
318+
319+
@overload
320+
def __mul__(self, other: Vector) -> Vector:
321+
...
322+
323+
def __mul__(self, other: Union[float, Vector]) -> Union[Vector, "Matrix"]:
307324
"""
308325
implements the matrix-vector multiplication.
309326
implements the matrix-scalar multiplication
@@ -312,7 +329,7 @@ def __mul__(self, other):
312329
if len(other) == self.__width:
313330
ans = zeroVector(self.__height)
314331
for i in range(self.__height):
315-
summe = 0
332+
summe: float = 0
316333
for j in range(self.__width):
317334
summe += other.component(j) * self.__matrix[i][j]
318335
ans.changeComponent(i, summe)
@@ -330,7 +347,7 @@ def __mul__(self, other):
330347
]
331348
return Matrix(matrix, self.__width, self.__height)
332349

333-
def __add__(self, other):
350+
def __add__(self, other: "Matrix") -> "Matrix":
334351
"""
335352
implements the matrix-addition.
336353
"""
@@ -345,7 +362,7 @@ def __add__(self, other):
345362
else:
346363
raise Exception("matrix must have the same dimension!")
347364

348-
def __sub__(self, other):
365+
def __sub__(self, other: "Matrix") -> "Matrix":
349366
"""
350367
implements the matrix-subtraction.
351368
"""
@@ -361,19 +378,21 @@ def __sub__(self, other):
361378
raise Exception("matrix must have the same dimension!")
362379

363380

364-
def squareZeroMatrix(N):
381+
def squareZeroMatrix(N: int) -> Matrix:
365382
"""
366383
returns a square zero-matrix of dimension NxN
367384
"""
368-
ans = [[0] * N for i in range(N)]
385+
ans: list[list[float]] = [[0] * N for _ in range(N)]
369386
return Matrix(ans, N, N)
370387

371388

372-
def randomMatrix(W, H, a, b):
389+
def randomMatrix(W: int, H: int, a: int, b: int) -> Matrix:
373390
"""
374391
returns a random matrix WxH with integer components
375392
between 'a' and 'b'
376393
"""
377394
random.seed(None)
378-
matrix = [[random.randint(a, b) for j in range(W)] for i in range(H)]
395+
matrix: list[list[float]] = [
396+
[random.randint(a, b) for _ in range(W)] for _ in range(H)
397+
]
379398
return Matrix(matrix, W, H)

linear_algebra/src/polynom_for_points.py

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
from __future__ import annotations
2-
3-
41
def points_to_polynomial(coordinates: list[list[int]]) -> str:
52
"""
63
coordinates is a two dimensional matrix: [[x, y], [x, y], ...]
@@ -55,12 +52,12 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str:
5552

5653
if check == 1:
5754
count_of_line = 0
58-
matrix = []
55+
matrix: list[list[float]] = []
5956
# put the x and x to the power values in a matrix
6057
while count_of_line < x:
6158
count_in_line = 0
6259
a = coordinates[count_of_line][0]
63-
count_line: list[int] = []
60+
count_line: list[float] = []
6461
while count_in_line < x:
6562
count_line.append(a ** (x - (count_in_line + 1)))
6663
count_in_line += 1
@@ -69,7 +66,7 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str:
6966

7067
count_of_line = 0
7168
# put the y values into a vector
72-
vector: list[int] = []
69+
vector: list[float] = []
7370
while count_of_line < x:
7471
vector.append(coordinates[count_of_line][1])
7572
count_of_line += 1
@@ -96,14 +93,14 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str:
9693
# make solutions
9794
solution: list[str] = []
9895
while count < x:
99-
solution.append(vector[count] / matrix[count][count])
96+
solution.append(str(vector[count] / matrix[count][count]))
10097
count += 1
10198

10299
count = 0
103100
solved = "f(x)="
104101

105102
while count < x:
106-
remove_e: list[str] = str(solution[count]).split("E")
103+
remove_e: list[str] = solution[count].split("E")
107104
if len(remove_e) > 1:
108105
solution[count] = remove_e[0] + "*10^" + remove_e[1]
109106
solved += "x^" + str(x - (count + 1)) + "*" + str(solution[count])

0 commit comments

Comments
 (0)