So my first piece of code will be with respect to matrix operations. NumPy already has many in-built functions, but I still want to do this because I want to write my own code in the beginning rather than just use in-built components. So here goes matrix.py.
A few thoughts particularly with respect to my ex-darling C++.
1. There isn't the possibility of overloading the "=" assignment operator. So is I want "a" and "b" as arrays and I want to do:
a=b
This will only cause a reference "a" to point to the the object "b". A copy will not be made. So if I change "b" later, "a" changes too. Which is not what I want. So I need to define the __call__ method. This was I could do:
a(b)
But of course you would need to define "a" as a matrix before this is run.
2. Indentation is a requirement in Python. It improves the readability of code. True. But doing away with braces {}, or the begin/end statement blocks is probably going too far. Just a beginner's thought anyway.
3. Function/Operator overloading: In C++, you can overload a function any number of times with a different number of variables, different type of variables etc. So an overloaded function could accept a float or a string and two completely different functions can be written. In Python, everything is an object. So you can't specify float or Matrix before an argument in a function/method. You have to figure out what that is inside the functions. This I feel is unnecessary ambiguity.
I was trying to code the "*" oveload operator for matrices, and would like to have the possibility of multiplying two matrices or a matrix and a float/integer. But the only was to do that was inside the overloaded operator. A little googling told me that type checking was not a good idea. Probably because they want to have the possibility fo redefining the types or maybe defining additional super-types in later releases of Python. So the only thing I could use was exception handling. The code works but this isn't really an exception. This is a normal varation to the manner in which the operator is used on matrices. So seems odd.
4. Two more functions may be written depending on need - inverse and upper triangularization or lower triangularization. This will be decided later. The idea would be solve equations of the order Ax=b or E*d/dt(x)=Ax+bu. Using inverse to solve equations is usually avoided as matrix even close to singular will cause the simulation to blow up. So more on this later.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
import sys | |
class Matrix: | |
""" For defining arrays of any given size. | |
Contains overloaded operators for print, add, subtract and multiply. | |
Does not contain = assignment operator! Use a(b) instead of a=b. | |
Last updated - Dec 25, 2012 """ | |
def zeros(self, rows, columns=1): | |
""" Creates a zero matrix | |
Dimensions have to be passed as <Matrix>.zeros(rows, columns). | |
This function also creates the actual array. | |
So every method of the class/object should call this first before using the array.""" | |
self.rows=rows | |
self.columns=columns | |
self.data=[] | |
for count1 in range(self.rows): | |
row_vector=[] | |
for count2 in range(self.columns): | |
row_vector.append(0) | |
self.data.append(row_vector) | |
def __init__(self, rows=1, columns=1): | |
"""Creates an object with a zero array of size rowsXcolumns. | |
If only one dimension is specified specified, a column vector is assumed. | |
If no dimensions are specified, an null matrix is created.""" | |
self.rows=rows | |
self.columns=columns | |
self.zeros(self.rows, self.columns) | |
def __call__(self, matrix1): | |
""" Replaces the assignment operator =. Use as c(b) instead of c=b. | |
The = will only result in a reference to the object and not an actual copy. | |
This method ensures it. """ | |
self.rows=matrix1.rows | |
self.columns=matrix1.columns | |
self.zeros(self.rows, self.columns) | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
self.data[count1][count2]=matrix1.data[count1][count2] | |
def identity(self, rows): | |
""" Generates an identity matrix of rowsXrows. """ | |
self.rows=rows | |
self.columns=rows | |
self.zeros(self.rows, self.columns) | |
for count in range(self.rows): | |
self.data[count][count]=1.0 | |
def display(self): | |
""" Explicit method for displaying an array. """ | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
print self.data[count1][count2], | |
print "\t", | |
print "\n", | |
def __repr__(self): | |
""" Displays an array when print <Matrix> is used. """ | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
print self.data[count1][count2], | |
print "\t", | |
print "\n", | |
return " " | |
def __add__(self, matrix1): | |
""" Left addition of two matrices. """ | |
add_result=Matrix(self.rows, self.columns) | |
try: | |
if matrix1.rows: pass | |
except AttributeError: | |
print "*"*40 | |
print "Can't add a float or integer to a matrix directly." | |
print "*"*40 | |
sys.exit("Logic Error") | |
else: | |
if not (self.rows==matrix1.rows and self.columns==matrix1.columns): | |
print "Matrices not compatible for summation." | |
sys.exit("Logic Error") | |
else: | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
add_result.data[count1][count2]=self.data[count1][count2]+\ | |
matrix1.data[count1][count2] | |
return add_result | |
def __radd__(self, matrix1): | |
""" Right addition of two matrices. """ | |
add_result=Matrix(self.rows, self.columns) | |
try: | |
if matrix1.rows: pass | |
except AttributeError: | |
print "*"*40 | |
print "Can't add a float or integer to a matrix directly." | |
print "*"*40 | |
sys.exit("Logic Error") | |
else: | |
if not (self.rows==matrix1.rows and self.columns==matrix1.columns): | |
print "Matrices not compatible for summation." | |
else: | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
add_result.data[count1][count2]=self.data[count1][count2]+\ | |
matrix1.data[count1][count2] | |
return add_result | |
def __sub__(self, matrix1): | |
""" Left subtraction of two matrices. """ | |
sub_result=Matrix(self.rows, self.columns) | |
try: | |
if matrix1.rows: pass | |
except AttributeError: | |
print "*"*40 | |
print "Can't subtract a float or integer to a matrix directly." | |
print "*"*40 | |
sys.exit("Logic Error") | |
else: | |
if not (self.rows==matrix1.rows and self.columns==matrix1.columns): | |
print "Matrices not compatible for subtraction." | |
sys.exit("Logic Error") | |
else: | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
sub_result.data[count1][count2]=self.data[count1][count2]-\ | |
matrix1.data[count1][count2] | |
return sub_result | |
def __rsub__(self, matrix1): | |
""" Right subtraction of two mnatrices. """ | |
sub_result=Matrix(self.rows, self.columns) | |
try: | |
if matrix1.rows: pass | |
except AttributeError: | |
print "*"*40 | |
print "Can't subtract a float or integer to a matrix directly." | |
print "*"*40 | |
sys.exit("Logic Error") | |
else: | |
if not (self.rows==matrix1.rows and self.columns==matrix1.columns): | |
print "Matrices not compatible for subtraction." | |
sys.exit("Logic Error") | |
else: | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
sub_result.data[count1][count2]=matrix1.data[count1][count2]-\ | |
self.data[count1][count2] | |
return sub_result | |
def __mul__(self, matrix1): | |
""" Left multiplication of two matrices. | |
The multiplication is NOT done elementwise but in the conventional mathematical sense. """ | |
try: | |
if matrix1.rows: pass | |
except AttributeError: | |
mul_result=Matrix(self.rows, self.columns) | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
mul_result.data[count1][count2]=self.data[count1][count2]*matrix1 | |
else: | |
mul_result=Matrix() | |
if not (self.columns==matrix1.rows): | |
print "Matrices not compatible for multiplication." | |
else: | |
mul_result.zeros(self.rows, matrix1.columns) | |
for count1 in range(self.rows): | |
for count2 in range(matrix1.columns): | |
for count3 in range(self.columns): | |
mul_result.data[count1][count2]+=self.data[count1][count3]*\ | |
matrix1.data[count3][count2] | |
return mul_result | |
def __rmul__(self, matrix1): | |
""" Right multiplication of two matrices. | |
The multiplication is NOT done elementwise but in the conventional mathematical sense. """ | |
try: | |
if matrix1.rows: pass | |
except AttributeError: | |
mul_result=Matrix(self.rows, self.columns) | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
mul_result.data[count1][count2]=self.data[count1][count2]*matrix1 | |
else: | |
mul_result=Matrix() | |
if not (self.rows==matrix1.columns): | |
print "Matrices not compatible for multiplication." | |
else: | |
mul_result.zeros(matrix1.rows, self.columns) | |
for count1 in range(matrix1.rows): | |
for count2 in range(self.columns): | |
for count3 in range(self.rows): | |
mul_result.data[count1][count2]+=self.data[count1][count3]*\ | |
matrix1.data[count3][count2] | |
return mul_result | |
def transpose(self): | |
""" Returns a matrix that is transpose of the calling object array. | |
A new object is returned. Original object is untouched. """ | |
trans_result=Matrix(self.columns, self.rows) | |
for count1 in range(self.rows): | |
for count2 in range(self.columns): | |
trans_result.data[count2][count1]=self.data[count1][count2] | |
return trans_result |
A few thoughts particularly with respect to my ex-darling C++.
1. There isn't the possibility of overloading the "=" assignment operator. So is I want "a" and "b" as arrays and I want to do:
a=b
This will only cause a reference "a" to point to the the object "b". A copy will not be made. So if I change "b" later, "a" changes too. Which is not what I want. So I need to define the __call__ method. This was I could do:
a(b)
But of course you would need to define "a" as a matrix before this is run.
2. Indentation is a requirement in Python. It improves the readability of code. True. But doing away with braces {}, or the begin/end statement blocks is probably going too far. Just a beginner's thought anyway.
3. Function/Operator overloading: In C++, you can overload a function any number of times with a different number of variables, different type of variables etc. So an overloaded function could accept a float or a string and two completely different functions can be written. In Python, everything is an object. So you can't specify float or Matrix before an argument in a function/method. You have to figure out what that is inside the functions. This I feel is unnecessary ambiguity.
I was trying to code the "*" oveload operator for matrices, and would like to have the possibility of multiplying two matrices or a matrix and a float/integer. But the only was to do that was inside the overloaded operator. A little googling told me that type checking was not a good idea. Probably because they want to have the possibility fo redefining the types or maybe defining additional super-types in later releases of Python. So the only thing I could use was exception handling. The code works but this isn't really an exception. This is a normal varation to the manner in which the operator is used on matrices. So seems odd.
4. Two more functions may be written depending on need - inverse and upper triangularization or lower triangularization. This will be decided later. The idea would be solve equations of the order Ax=b or E*d/dt(x)=Ax+bu. Using inverse to solve equations is usually avoided as matrix even close to singular will cause the simulation to blow up. So more on this later.
No comments:
Post a Comment