1
1
"""A custom implementation of some Basic Linear Algebra Subprograms (BLAS)."""
2
2
3
3
from typing import Sequence , Callable
4
+ from math import sqrt , acos
4
5
5
- import numpy as np
6
6
7
+ class Vector :
8
+ """Structure which can be used as a column vector."""
7
9
8
- class Matrix :
9
- """Matrix structured for row-major access."""
10
-
11
- def __init__ (self , first : Sequence , * rest : Sequence ):
12
- if isinstance (first , np .ndarray ):
13
- self ._matrix = first
14
- else :
15
- self ._matrix = np .array ([first ] + list (rest ), np .float32 )
10
+ def __init__ (self , first , * rest ):
11
+ self ._coordinates = [first ] + list (rest )
16
12
17
13
@staticmethod
18
- def from_lists (rows : Sequence ):
19
- """Creates a Matrix with the given sequence of sequences."""
20
- return Matrix (* rows )
21
-
22
- @staticmethod
23
- def from_function (m : int , n : int , builder : Callable ):
24
- """
25
- Builds an MxN Matrix by calling a given builder function on each index
26
- pair (row, column) and filling its cell with the function's return.
27
- """
28
- return Matrix .from_lists ([[builder (i , j ) for j in range (n )] for i in range (m )])
29
-
30
- @staticmethod
31
- def identity (n : int ):
32
- """Create an NxN identity Matrix."""
33
- return Matrix .from_function (n , n , lambda i , j : 1 if j == i else 0 )
34
-
35
- @staticmethod
36
- def zeros (m : int , n : int ):
37
- """Create an MxN Matrix filled with zeros."""
38
- return Matrix .from_function (m , n , lambda i , j : 0 )
39
-
40
- @property
41
- def rows (self ):
42
- return len (self )
43
-
44
- @property
45
- def columns (self ):
46
- return len (self [0 ])
14
+ def angle (a , b ):
15
+ """Finds the angle between two vectors."""
16
+ return acos ((a @ b ) / (a .length * b .length ))
47
17
48
18
def __getitem__ (self , key ):
49
- return self ._matrix [key ]
19
+ return self ._coordinates [key ]
50
20
51
21
def __setitem__ (self , key , item ):
52
- self ._matrix [key ] = item
22
+ self ._coordinates [key ] = item
53
23
54
24
def __len__ (self ):
55
- return len (self ._matrix )
25
+ return len (self ._coordinates )
56
26
57
27
def __repr__ (self ):
58
- return str (self ._matrix )
28
+ return str (self ._coordinates )
59
29
60
- def __add__ (self , other ):
61
- if not isinstance (other , Matrix ):
62
- other = Matrix (* other )
63
- return Matrix (self ._matrix + other ._matrix )
30
+ def __add__ (self , other : Sequence ):
31
+ try :
32
+ return Vector (* tuple (v + other [i ] for i , v in enumerate (self )))
33
+ except Exception as exc :
34
+ raise NotImplementedError from exc
64
35
65
36
def __radd__ (self , other ):
66
37
return self + other
67
38
68
- def __sub__ (self , other ):
69
- if not isinstance (other , Matrix ):
70
- other = Matrix (* other )
71
- return Matrix (self ._matrix - other ._matrix )
39
+ def __sub__ (self , other : Sequence ):
40
+ return self + tuple (- x for x in other )
72
41
73
42
def __rsub__ (self , other ):
74
43
return other + (- self )
75
44
76
45
def __mul__ (self , scalar ): # scalar product
77
- return Matrix (self ._matrix * scalar )
46
+ try :
47
+ return Vector (* tuple (scalar * x for x in self ))
48
+ except Exception as exc :
49
+ raise NotImplementedError from exc
78
50
79
51
def __rmul__ (self , other ):
80
52
return self * other
81
53
82
- def __matmul__ (self , other ):
83
- if not isinstance ( other , Matrix ) :
84
- other = Matrix ( * other )
85
- product = self . _matrix @ other . _matrix
86
- return product if isinstance ( product , np . float32 ) else Matrix ( product )
54
+ def __matmul__ (self , other : Sequence ): # dot product
55
+ try :
56
+ return sum ( tuple ( v * other [ i ] for i , v in enumerate ( self )) )
57
+ except Exception as exc :
58
+ raise NotImplementedError from exc
87
59
88
60
def __neg__ (self ):
89
61
return self * - 1
@@ -92,12 +64,19 @@ def __truediv__(self, scalar):
92
64
return self * (1 / scalar )
93
65
94
66
def __mod__ (self , z ):
95
- return Matrix ( self . _matrix % z )
67
+ return Vector ( * tuple ( x % z for x in self ) )
96
68
97
69
def __eq__ (self , other ):
98
- if not isinstance (other , Matrix ):
99
- other = Matrix (* other )
100
- return np .array_equal (self , other )
70
+ if len (self ) != len (other ):
71
+ return False
72
+ for i , v in enumerate (self ):
73
+ if v != other [i ]:
74
+ return False
75
+ return True
76
+
77
+ def normalized (self ):
78
+ """Returns a normalized version of this vector."""
79
+ return self / self .length
101
80
102
81
@property
103
82
def x (self ):
@@ -123,9 +102,75 @@ def z(self):
123
102
def z (self , z ):
124
103
self [2 ] = z
125
104
105
+ @property
106
+ def length (self ):
107
+ squared_sum = 0
108
+ for component in self :
109
+ squared_sum += component * component
110
+ return sqrt (squared_sum )
126
111
127
- class Vector (Matrix ):
128
- """Structure which can be used as a column vector."""
129
112
130
- def __init__ (self , x , y , * others ):
131
- super ().__init__ (x , y , * others )
113
+ class Matrix (Vector ):
114
+ """Matrix structured for row-major access."""
115
+
116
+ def __init__ (self , first : Sequence , * rest : Sequence ):
117
+ convert = lambda seq : seq if isinstance (seq , Vector ) else Vector (* seq )
118
+ rows = [convert (first )]
119
+ for row in rest :
120
+ if len (row ) != len (first ):
121
+ raise ValueError ("Matrix row length mismatch." )
122
+ else :
123
+ rows .append (convert (row ))
124
+ super ().__init__ (* rows )
125
+
126
+ @staticmethod
127
+ def from_lists (rows : Sequence ):
128
+ """Creates a Matrix with the given sequence of sequences."""
129
+ return Matrix (* rows )
130
+
131
+ @staticmethod
132
+ def from_function (m : int , n : int , builder : Callable ):
133
+ """
134
+ Builds an MxN Matrix by calling a given builder function on each index
135
+ pair (row, column) and filling its cell with the function's return.
136
+ """
137
+ rows = [[builder (i , j ) for j in range (n )] for i in range (m )]
138
+ return Matrix .from_lists (rows )
139
+
140
+ @staticmethod
141
+ def identity (n : int ):
142
+ """Create an NxN identity Matrix."""
143
+ return Matrix .from_function (n , n , lambda i , j : 1 if j == i else 0 )
144
+
145
+ @staticmethod
146
+ def zeros (m : int , n : int ):
147
+ """Create an MxN Matrix filled with zeros."""
148
+ return Matrix .from_function (m , n , lambda i , j : 0 )
149
+
150
+ def __matmul__ (self , other ):
151
+ if isinstance (other , Matrix ):
152
+ m , n , p = self .rows , self .columns , other .columns
153
+ if other .rows != n :
154
+ raise ValueError ("Matrix has incompatible dimensions." )
155
+ result = Matrix .zeros (m , p )
156
+ for i in range (m ):
157
+ for j in range (p ):
158
+ for k in range (n ):
159
+ result [i ][j ] += self [i ][k ] * other [k ][j ]
160
+ return result
161
+ elif isinstance (other , Vector ):
162
+ n = len (other )
163
+ if n != self .columns :
164
+ raise ValueError ("Vector has incompatible dimensions." )
165
+ return Vector (* [line @ other for line in self ])
166
+ else :
167
+ raise NotImplementedError ("Can't multiply Matrix by %s." %
168
+ type (other ).__name__ )
169
+
170
+ @property
171
+ def rows (self ):
172
+ return len (self )
173
+
174
+ @property
175
+ def columns (self ):
176
+ return len (self [0 ])
0 commit comments