diff --git a/linear-algebra-python/README.md b/linear-algebra-python/README.md new file mode 100644 index 000000000..bca0af449 --- /dev/null +++ b/linear-algebra-python/README.md @@ -0,0 +1,70 @@ +# Linear algebra library for Python + +This module contains some useful classes and functions for dealing with linear algebra in python 2. + +--- + +## Overview + +- class Vector + - This class represents a vector of arbitray size and operations on it. + + **Overview about the methods:** + + - constructor(components : list) : init the vector + - set(components : list) : changes the vector components. + - __str__() : toString method + - component(i : int): gets the i-th component (start by 0) + - size() : gets the size of the vector (number of components) + - euclidLength() : returns the eulidean length of the vector. + - operator + : vector addition + - operator - : vector subtraction + - operator * : scalar multiplication and dot product + - copy() : copies this vector and returns it. + - changeComponent(pos,value) : changes the specified component. + +- function zeroVector(dimension) + - returns a zero vector of 'dimension' +- function unitBasisVector(dimension,pos) + - returns a unit basis vector with a One at index 'pos' (indexing at 0) +- function axpy(scalar,vector1,vector2) + - computes the axpy operation +- class Matrix + - This class represents a matrix of arbitrary size and operations on it. + + **Overview about the methods:** + + - __str__() : returns a string representation + - operator * : implements the matrix vector multiplication + implements the matrix-scalar multiplication. + - changeComponent(x,y,value) : changes the specified component. + - component(x,y) : returns the specified component. + - width() : returns the width of the matrix + - height() : returns the height of the matrix + - operator + : implements the matrix-addition. + - operator - _ implements the matrix-subtraction +- function squareZeroMatrix(N) + - returns a square zero-matrix of dimension NxN +--- + +## Documentation + +The module is well documented. You can use the python in-built ```help(...)``` function. +For instance: ```help(Vector)``` gives you all information about the Vector-class. +Or ```help(unitBasisVector)``` gives you all information you needed about the +global function ```unitBasisVector(...)```. If you need informations about a certain +method you type ```help(CLASSNAME.METHODNAME)```. + +--- + +## Usage + +You will find the module in the **src** directory its called ```lib.py```. You need to +import this module in your project. Alternative you can also use the file ```lib.pyc``` in python-bytecode. + +--- + +## Tests + +In the **src** directory you also find the test-suite, its called ```tests.py```. +The test-suite uses the built-in python-test-framework **unittest**. diff --git a/linear-algebra-python/src/lib.py b/linear-algebra-python/src/lib.py new file mode 100644 index 000000000..efded3a2a --- /dev/null +++ b/linear-algebra-python/src/lib.py @@ -0,0 +1,332 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Feb 26 14:29:11 2018 + +@author: Christian Bender +@license: MIT-license + +This module contains some useful classes and functions for dealing +with linear algebra in python. + +Overview: + +- class Vector +- function zeroVector(dimension) +- function unitBasisVector(dimension,pos) +- function axpy(scalar,vector1,vector2) +- class Matrix +- squareZeroMatrix(N) +""" + + +import math + + +class Vector(object): + """ + This class represents a vector of arbitray size. + You need to give the vector components. + + Overview about the methods: + + constructor(components : list) : init the vector + set(components : list) : changes the vector components. + __str__() : toString method + component(i : int): gets the i-th component (start by 0) + size() : gets the size of the vector (number of components) + euclidLength() : returns the eulidean length of the vector. + operator + : vector addition + operator - : vector subtraction + operator * : scalar multiplication and dot product + copy() : copies this vector and returns it. + changeComponent(pos,value) : changes the specified component. + TODO: compare-operator + """ + def __init__(self,components): + """ + input: components or nothing + simple constructor for init the vector + """ + self.__components = components + def set(self,components): + """ + input: new components + changes the components of the vector. + replace the components with newer one. + """ + if len(components) > 0: + self.__components = components + else: + raise Exception("please give any vector") + def __str__(self): + """ + returns a string representation of the vector + """ + ans = "(" + length = len(self.__components) + for i in range(length): + if i != length-1: + ans += str(self.__components[i]) + "," + else: + ans += str(self.__components[i]) + ")" + if len(ans) == 1: + ans += ")" + return ans + def component(self,i): + """ + input: index (start at 0) + output: the i-th component of the vector. + """ + if i < len(self.__components) and i >= 0: + return self.__components[i] + else: + raise Exception("index out of range") + def size(self): + """ + returns the size of the vector + """ + return len(self.__components) + def eulidLength(self): + """ + returns the eulidean length of the vector + """ + summe = 0 + for c in self.__components: + summe += c**2 + return math.sqrt(summe) + def __add__(self,other): + """ + input: other vector + assumes: other vector has the same size + returns a new vector that represents the sum. + """ + size = self.size() + result = [] + if size == other.size(): + for i in range(size): + result.append(self.__components[i] + other.component(i)) + else: + raise Exception("must have the same size") + return Vector(result) + def __sub__(self,other): + """ + input: other vector + assumes: other vector has the same size + returns a new vector that represents the differenz. + """ + size = self.size() + result = [] + if size == other.size(): + for i in range(size): + result.append(self.__components[i] - other.component(i)) + else: # error case + raise Exception("must have the same size") + return Vector(result) + def __mul__(self,other): + """ + mul implements the scalar multiplication + and the dot-product + """ + ans = [] + if isinstance(other,float) or isinstance(other,int): + for c in self.__components: + ans.append(c*other) + elif (isinstance(other,Vector) and (self.size() == other.size())): + size = self.size() + summe = 0 + for i in range(size): + summe += self.__components[i] * other.component(i) + return summe + else: # error case + raise Exception("invalide operand!") + return Vector(ans) + def copy(self): + """ + copies this vector and returns it. + """ + components = [x for x in self.__components] + return Vector(components) + def changeComponent(self,pos,value): + """ + input: an index (pos) and a value + changes the specified component (pos) with the + 'value' + """ + #precondition + assert (pos >= 0 and pos < len(self.__components)) + self.__components[pos] = value + +def zeroVector(dimension): + """ + returns a zero-vector of size 'dimension' + """ + #precondition + assert(isinstance(dimension,int)) + ans = [] + for i in range(dimension): + ans.append(0) + return Vector(ans) + + +def unitBasisVector(dimension,pos): + """ + returns a unit basis vector with a One + at index 'pos' (indexing at 0) + """ + #precondition + assert(isinstance(dimension,int) and (isinstance(pos,int))) + ans = [] + for i in range(dimension): + if i != pos: + ans.append(0) + else: + ans.append(1) + return Vector(ans) + + +def axpy(scalar,x,y): + """ + input: a 'scalar' and two vectors 'x' and 'y' + output: a vector + computes the axpy operation + """ + # precondition + assert(isinstance(x,Vector) and (isinstance(y,Vector)) \ + and (isinstance(scalar,int) or isinstance(scalar,float))) + return (x*scalar + y) + + +class Matrix(object): + """ + class: Matrix + This class represents a arbitrary matrix. + + Overview about the methods: + + __str__() : returns a string representation + operator * : implements the matrix vector multiplication + implements the matrix-scalar multiplication. + changeComponent(x,y,value) : changes the specified component. + component(x,y) : returns the specified component. + width() : returns the width of the matrix + height() : returns the height of the matrix + operator + : implements the matrix-addition. + operator - _ implements the matrix-subtraction + """ + def __init__(self,matrix,w,h): + """ + simple constructor for initialzes + the matrix with components. + """ + self.__matrix = matrix + self.__width = w + self.__height = h + def __str__(self): + """ + returns a string representation of this + matrix. + """ + ans = "" + for i in range(self.__height): + ans += "|" + for j in range(self.__width): + if j < self.__width -1: + ans += str(self.__matrix[i][j]) + "," + else: + ans += str(self.__matrix[i][j]) + "|\n" + return ans + def changeComponent(self,x,y, value): + """ + changes the x-y component of this matrix + """ + if x >= 0 and x < self.__height and y >= 0 and y < self.__width: + self.__matrix[x][y] = value + else: + raise Exception ("changeComponent: indices out of bounds") + def component(self,x,y): + """ + returns the specified (x,y) component + """ + if x >= 0 and x < self.__height and y >= 0 and y < self.__width: + return self.__matrix[x][y] + else: + raise Exception ("changeComponent: indices out of bounds") + def width(self): + """ + getter for the width + """ + return self.__width + def height(self): + """ + getter for the height + """ + return self.__height + def __mul__(self,other): + """ + implements the matrix-vector multiplication. + implements the matrix-scalar multiplication + """ + if isinstance(other, Vector): # vector-matrix + if (other.size() == self.__width): + ans = zeroVector(self.__height) + for i in range(self.__height): + summe = 0 + for j in range(self.__width): + summe += other.component(j) * self.__matrix[i][j] + ans.changeComponent(i,summe) + summe = 0 + return ans + else: + raise Exception("vector must have the same size as the " + + "number of columns of the matrix!") + elif isinstance(other,int) or isinstance(other,float): # matrix-scalar + matrix = [] + for i in range(self.__height): + row = [] + for j in range(self.__width): + row.append(self.__matrix[i][j] * other) + matrix.append(row) + return Matrix(matrix,self.__width,self.__height) + def __add__(self,other): + """ + implements the matrix-addition. + """ + if (self.__width == other.width() and self.__height == other.height()): + matrix = [] + for i in range(self.__height): + row = [] + for j in range(self.__width): + row.append(self.__matrix[i][j] + other.component(i,j)) + matrix.append(row) + return Matrix(matrix,self.__width,self.__height) + else: + raise Exception("matrix must have the same dimension!") + def __sub__(self,other): + """ + implements the matrix-subtraction. + """ + if (self.__width == other.width() and self.__height == other.height()): + matrix = [] + for i in range(self.__height): + row = [] + for j in range(self.__width): + row.append(self.__matrix[i][j] - other.component(i,j)) + matrix.append(row) + return Matrix(matrix,self.__width,self.__height) + else: + raise Exception("matrix must have the same dimension!") + + +def squareZeroMatrix(N): + """ + returns a square zero-matrix of dimension NxN + """ + ans = [] + for i in range(N): + row = [] + for j in range(N): + row.append(0) + ans.append(row) + return Matrix(ans,N,N) + + \ No newline at end of file diff --git a/linear-algebra-python/src/lib.pyc b/linear-algebra-python/src/lib.pyc new file mode 100644 index 000000000..7aeca0e1c Binary files /dev/null and b/linear-algebra-python/src/lib.pyc differ diff --git a/linear-algebra-python/src/tests.py b/linear-algebra-python/src/tests.py new file mode 100644 index 000000000..b84612b4c --- /dev/null +++ b/linear-algebra-python/src/tests.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +""" +Created on Mon Feb 26 15:40:07 2018 + +@author: Christian Bender +@license: MIT-license + +This file contains the test-suite for the linear algebra library. +""" + +import unittest +from lib import * + +class Test(unittest.TestCase): + def test_component(self): + """ + test for method component + """ + x = Vector([1,2,3]) + self.assertEqual(x.component(0),1) + self.assertEqual(x.component(2),3) + try: + y = Vector() + self.assertTrue(False) + except: + self.assertTrue(True) + def test_str(self): + """ + test for toString() method + """ + x = Vector([0,0,0,0,0,1]) + self.assertEqual(x.__str__(),"(0,0,0,0,0,1)") + def test_size(self): + """ + test for size()-method + """ + x = Vector([1,2,3,4]) + self.assertEqual(x.size(),4) + def test_euclidLength(self): + """ + test for the eulidean length + """ + x = Vector([1,2]) + self.assertAlmostEqual(x.eulidLength(),2.236,3) + def test_add(self): + """ + test for + operator + """ + x = Vector([1,2,3]) + y = Vector([1,1,1]) + self.assertEqual((x+y).component(0),2) + self.assertEqual((x+y).component(1),3) + self.assertEqual((x+y).component(2),4) + def test_sub(self): + """ + test for - operator + """ + x = Vector([1,2,3]) + y = Vector([1,1,1]) + self.assertEqual((x-y).component(0),0) + self.assertEqual((x-y).component(1),1) + self.assertEqual((x-y).component(2),2) + def test_mul(self): + """ + test for * operator + """ + x = Vector([1,2,3]) + a = Vector([2,-1,4]) # for test of dot-product + b = Vector([1,-2,-1]) + self.assertEqual((x*3.0).__str__(),"(3.0,6.0,9.0)") + self.assertEqual((a*b),0) + def test_zeroVector(self): + """ + test for the global function zeroVector(...) + """ + self.assertTrue(zeroVector(10).__str__().count("0") == 10) + def test_unitBasisVector(self): + """ + test for the global function unitBasisVector(...) + """ + self.assertEqual(unitBasisVector(3,1).__str__(),"(0,1,0)") + def test_axpy(self): + """ + test for the global function axpy(...) (operation) + """ + x = Vector([1,2,3]) + y = Vector([1,0,1]) + self.assertEqual(axpy(2,x,y).__str__(),"(3,4,7)") + def test_copy(self): + """ + test for the copy()-method + """ + x = Vector([1,0,0,0,0,0]) + y = x.copy() + self.assertEqual(x.__str__(),y.__str__()) + def test_changeComponent(self): + """ + test for the changeComponent(...)-method + """ + x = Vector([1,0,0]) + x.changeComponent(0,0) + x.changeComponent(1,1) + self.assertEqual(x.__str__(),"(0,1,0)") + def test_str_matrix(self): + A = Matrix([[1,2,3],[2,4,5],[6,7,8]],3,3) + self.assertEqual("|1,2,3|\n|2,4,5|\n|6,7,8|\n",A.__str__()) + def test__mul__matrix(self): + A = Matrix([[1,2,3],[4,5,6],[7,8,9]],3,3) + x = Vector([1,2,3]) + self.assertEqual("(14,32,50)",(A*x).__str__()) + self.assertEqual("|2,4,6|\n|8,10,12|\n|14,16,18|\n",(A*2).__str__()) + def test_changeComponent_matrix(self): + A = Matrix([[1,2,3],[2,4,5],[6,7,8]],3,3) + A.changeComponent(0,2,5) + self.assertEqual("|1,2,5|\n|2,4,5|\n|6,7,8|\n",A.__str__()) + def test_component_matrix(self): + A = Matrix([[1,2,3],[2,4,5],[6,7,8]],3,3) + self.assertEqual(7,A.component(2,1),0.01) + def test__add__matrix(self): + A = Matrix([[1,2,3],[2,4,5],[6,7,8]],3,3) + B = Matrix([[1,2,7],[2,4,5],[6,7,10]],3,3) + self.assertEqual("|2,4,10|\n|4,8,10|\n|12,14,18|\n",(A+B).__str__()) + def test__sub__matrix(self): + A = Matrix([[1,2,3],[2,4,5],[6,7,8]],3,3) + B = Matrix([[1,2,7],[2,4,5],[6,7,10]],3,3) + self.assertEqual("|0,0,-4|\n|0,0,0|\n|0,0,-2|\n",(A-B).__str__()) + def test_squareZeroMatrix(self): + self.assertEqual('|0,0,0,0,0|\n|0,0,0,0,0|\n|0,0,0,0,0|\n|0,0,0,0,0|' + +'\n|0,0,0,0,0|\n',squareZeroMatrix(5).__str__()) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file