Skip to content

Commit

Permalink
fix(mypy): type annotations for linear algebra algorithms (TheAlgorit…
Browse files Browse the repository at this point in the history
…hms#4317)

* fix(mypy): type annotations for linear algebra algorithms

* refactor: remove linear algebra directory from mypy exclude
  • Loading branch information
dhruvmanila authored Apr 5, 2021
1 parent 20c7518 commit 8c29860
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
python -m pip install mypy pytest-cov -r requirements.txt
# FIXME: #4052 fix mypy errors in the exclude directories and remove them below
- run: mypy --ignore-missing-imports
--exclude '(data_structures|digital_image_processing|dynamic_programming|graphs|linear_algebra|maths|matrix|other|project_euler|scripts|searches|strings*)/$' .
--exclude '(data_structures|digital_image_processing|dynamic_programming|graphs|maths|matrix|other|project_euler|scripts|searches|strings*)/$' .
- name: Run tests
run: pytest --doctest-modules --ignore=project_euler/ --ignore=scripts/ --cov-report=term-missing:skip-covered --cov=. .
- if: ${{ success() }}
Expand Down
17 changes: 11 additions & 6 deletions linear_algebra/src/conjugate_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
- https://en.wikipedia.org/wiki/Conjugate_gradient_method
- https://en.wikipedia.org/wiki/Definite_symmetric_matrix
"""
from typing import Any

import numpy as np


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

# Check sign of all eigenvalues.
return np.all(eigen_values > 0)
# np.all returns a value of type np.bool_
return bool(np.all(eigen_values > 0))


def _create_spd_matrix(dimension: np.int64) -> np.array:
def _create_spd_matrix(dimension: int) -> Any:
"""
Returns a symmetric positive definite matrix given a dimension.
Expand All @@ -64,11 +67,11 @@ def _create_spd_matrix(dimension: np.int64) -> np.array:


def conjugate_gradient(
spd_matrix: np.array,
load_vector: np.array,
spd_matrix: np.ndarray,
load_vector: np.ndarray,
max_iterations: int = 1000,
tol: float = 1e-8,
) -> np.array:
) -> Any:
"""
Returns solution to the linear system np.dot(spd_matrix, x) = b.
Expand Down Expand Up @@ -141,6 +144,8 @@ def conjugate_gradient(

# Update number of iterations.
iterations += 1
if iterations > max_iterations:
break

return x

Expand Down
83 changes: 51 additions & 32 deletions linear_algebra/src/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import math
import random
from typing import Collection, Optional, Union, overload


class Vector:
Expand All @@ -45,7 +46,7 @@ class Vector:
TODO: compare-operator
"""

def __init__(self, components=None):
def __init__(self, components: Optional[Collection[float]] = None) -> None:
"""
input: components or nothing
simple constructor for init the vector
Expand All @@ -54,7 +55,7 @@ def __init__(self, components=None):
components = []
self.__components = list(components)

def set(self, components):
def set(self, components: Collection[float]) -> None:
"""
input: new components
changes the components of the vector.
Expand All @@ -65,13 +66,13 @@ def set(self, components):
else:
raise Exception("please give any vector")

def __str__(self):
def __str__(self) -> str:
"""
returns a string representation of the vector
"""
return "(" + ",".join(map(str, self.__components)) + ")"

def component(self, i):
def component(self, i: int) -> float:
"""
input: index (start at 0)
output: the i-th component of the vector.
Expand All @@ -81,22 +82,22 @@ def component(self, i):
else:
raise Exception("index out of range")

def __len__(self):
def __len__(self) -> int:
"""
returns the size of the vector
"""
return len(self.__components)

def euclidLength(self):
def euclidLength(self) -> float:
"""
returns the euclidean length of the vector
"""
summe = 0
summe: float = 0
for c in self.__components:
summe += c ** 2
return math.sqrt(summe)

def __add__(self, other):
def __add__(self, other: "Vector") -> "Vector":
"""
input: other vector
assumes: other vector has the same size
Expand All @@ -109,7 +110,7 @@ def __add__(self, other):
else:
raise Exception("must have the same size")

def __sub__(self, other):
def __sub__(self, other: "Vector") -> "Vector":
"""
input: other vector
assumes: other vector has the same size
Expand All @@ -122,7 +123,15 @@ def __sub__(self, other):
else: # error case
raise Exception("must have the same size")

def __mul__(self, other):
@overload
def __mul__(self, other: float) -> "Vector":
...

@overload
def __mul__(self, other: "Vector") -> float:
...

def __mul__(self, other: Union[float, "Vector"]) -> Union[float, "Vector"]:
"""
mul implements the scalar multiplication
and the dot-product
Expand All @@ -132,20 +141,20 @@ def __mul__(self, other):
return Vector(ans)
elif isinstance(other, Vector) and (len(self) == len(other)):
size = len(self)
summe = 0
summe: float = 0
for i in range(size):
summe += self.__components[i] * other.component(i)
return summe
else: # error case
raise Exception("invalid operand!")

def copy(self):
def copy(self) -> "Vector":
"""
copies this vector and returns it.
"""
return Vector(self.__components)

def changeComponent(self, pos, value):
def changeComponent(self, pos: int, value: float) -> None:
"""
input: an index (pos) and a value
changes the specified component (pos) with the
Expand All @@ -156,7 +165,7 @@ def changeComponent(self, pos, value):
self.__components[pos] = value


def zeroVector(dimension):
def zeroVector(dimension: int) -> Vector:
"""
returns a zero-vector of size 'dimension'
"""
Expand All @@ -165,7 +174,7 @@ def zeroVector(dimension):
return Vector([0] * dimension)


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


def axpy(scalar, x, y):
def axpy(scalar: float, x: Vector, y: Vector) -> Vector:
"""
input: a 'scalar' and two vectors 'x' and 'y'
output: a vector
Expand All @@ -192,15 +201,15 @@ def axpy(scalar, x, y):
return x * scalar + y


def randomVector(N, a, b):
def randomVector(N: int, a: int, b: int) -> Vector:
"""
input: size (N) of the vector.
random range (a,b)
output: returns a random vector of size N, with
random integer components between 'a' and 'b'.
"""
random.seed(None)
ans = [random.randint(a, b) for i in range(N)]
ans = [random.randint(a, b) for _ in range(N)]
return Vector(ans)


Expand All @@ -222,7 +231,7 @@ class Matrix:
operator - _ implements the matrix-subtraction
"""

def __init__(self, matrix, w, h):
def __init__(self, matrix: list[list[float]], w: int, h: int) -> None:
"""
simple constructor for initializing
the matrix with components.
Expand All @@ -231,7 +240,7 @@ def __init__(self, matrix, w, h):
self.__width = w
self.__height = h

def __str__(self):
def __str__(self) -> str:
"""
returns a string representation of this
matrix.
Expand All @@ -246,7 +255,7 @@ def __str__(self):
ans += str(self.__matrix[i][j]) + "|\n"
return ans

def changeComponent(self, x, y, value):
def changeComponent(self, x: int, y: int, value: float) -> None:
"""
changes the x-y component of this matrix
"""
Expand All @@ -255,7 +264,7 @@ def changeComponent(self, x, y, value):
else:
raise Exception("changeComponent: indices out of bounds")

def component(self, x, y):
def component(self, x: int, y: int) -> float:
"""
returns the specified (x,y) component
"""
Expand All @@ -264,13 +273,13 @@ def component(self, x, y):
else:
raise Exception("changeComponent: indices out of bounds")

def width(self):
def width(self) -> int:
"""
getter for the width
"""
return self.__width

def height(self):
def height(self) -> int:
"""
getter for the height
"""
Expand Down Expand Up @@ -303,7 +312,15 @@ def determinate(self) -> float:
else:
raise Exception("matrix is not square")

def __mul__(self, other):
@overload
def __mul__(self, other: float) -> "Matrix":
...

@overload
def __mul__(self, other: Vector) -> Vector:
...

def __mul__(self, other: Union[float, Vector]) -> Union[Vector, "Matrix"]:
"""
implements the matrix-vector multiplication.
implements the matrix-scalar multiplication
Expand All @@ -312,7 +329,7 @@ def __mul__(self, other):
if len(other) == self.__width:
ans = zeroVector(self.__height)
for i in range(self.__height):
summe = 0
summe: float = 0
for j in range(self.__width):
summe += other.component(j) * self.__matrix[i][j]
ans.changeComponent(i, summe)
Expand All @@ -330,7 +347,7 @@ def __mul__(self, other):
]
return Matrix(matrix, self.__width, self.__height)

def __add__(self, other):
def __add__(self, other: "Matrix") -> "Matrix":
"""
implements the matrix-addition.
"""
Expand All @@ -345,7 +362,7 @@ def __add__(self, other):
else:
raise Exception("matrix must have the same dimension!")

def __sub__(self, other):
def __sub__(self, other: "Matrix") -> "Matrix":
"""
implements the matrix-subtraction.
"""
Expand All @@ -361,19 +378,21 @@ def __sub__(self, other):
raise Exception("matrix must have the same dimension!")


def squareZeroMatrix(N):
def squareZeroMatrix(N: int) -> Matrix:
"""
returns a square zero-matrix of dimension NxN
"""
ans = [[0] * N for i in range(N)]
ans: list[list[float]] = [[0] * N for _ in range(N)]
return Matrix(ans, N, N)


def randomMatrix(W, H, a, b):
def randomMatrix(W: int, H: int, a: int, b: int) -> Matrix:
"""
returns a random matrix WxH with integer components
between 'a' and 'b'
"""
random.seed(None)
matrix = [[random.randint(a, b) for j in range(W)] for i in range(H)]
matrix: list[list[float]] = [
[random.randint(a, b) for _ in range(W)] for _ in range(H)
]
return Matrix(matrix, W, H)
13 changes: 5 additions & 8 deletions linear_algebra/src/polynom_for_points.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
from __future__ import annotations


def points_to_polynomial(coordinates: list[list[int]]) -> str:
"""
coordinates is a two dimensional matrix: [[x, y], [x, y], ...]
Expand Down Expand Up @@ -55,12 +52,12 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str:

if check == 1:
count_of_line = 0
matrix = []
matrix: list[list[float]] = []
# put the x and x to the power values in a matrix
while count_of_line < x:
count_in_line = 0
a = coordinates[count_of_line][0]
count_line: list[int] = []
count_line: list[float] = []
while count_in_line < x:
count_line.append(a ** (x - (count_in_line + 1)))
count_in_line += 1
Expand All @@ -69,7 +66,7 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str:

count_of_line = 0
# put the y values into a vector
vector: list[int] = []
vector: list[float] = []
while count_of_line < x:
vector.append(coordinates[count_of_line][1])
count_of_line += 1
Expand All @@ -96,14 +93,14 @@ def points_to_polynomial(coordinates: list[list[int]]) -> str:
# make solutions
solution: list[str] = []
while count < x:
solution.append(vector[count] / matrix[count][count])
solution.append(str(vector[count] / matrix[count][count]))
count += 1

count = 0
solved = "f(x)="

while count < x:
remove_e: list[str] = str(solution[count]).split("E")
remove_e: list[str] = solution[count].split("E")
if len(remove_e) > 1:
solution[count] = remove_e[0] + "*10^" + remove_e[1]
solved += "x^" + str(x - (count + 1)) + "*" + str(solution[count])
Expand Down
Loading

0 comments on commit 8c29860

Please sign in to comment.