Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(mypy): type annotations for linear algebra algorithms #4317

Merged
merged 2 commits into from
Apr 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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