Created
December 16, 2017 17:26
-
-
Save DagnyTagg2013/cd4411932aa7f9e940974458b4a1a336 to your computer and use it in GitHub Desktop.
FunctionalMatrixOps
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
# 2D Matrix Ops | |
import sys | |
from functools import partial | |
# SLICING | |
""" | |
=> operates on SHALLOW REFERENCE PTR to COMPLEX types (like list, array); | |
OR actual VALUES of PRIMITIVE types (like char, int) | |
- https://www.python-course.eu/deep_copy.php | |
a[start:end] # items start through end-1 (ie NOT inclusive of END) | |
a[start:] # items start through the rest of the array | |
a[:end] # items from the beginning through end-1 | |
a[:] # a copy of the whole array | |
a[start:end:step] # | |
a[-1] # last item | |
x[::-1] # reverse | |
""" | |
# FUNCTION CURRYING -- i.e. breakdown COMPOUND-NESTED function into functions-parameterized-by-level-of-nesting | |
""" | |
https://stackoverflow.com/questions/24881604/when-should-i-use-function-currying-in-python | |
https://stackoverflow.com/questions/218025/what-is-the-difference-between-currying-and-partial-application | |
- eg f takes input of three function arguments and two constants from L to R | |
- function arguments within f are applied in nested fashion in ORDER from most-nested function LEFTMOST | |
on orig arglist to | |
outer function from RIGHTMOST | |
- then PARTIAL parameters may be passed into arglists to generate a PARTIAL function which essentially applies | |
DEFAULT functional or non args | |
from LEFT to RIGHT | |
then allows flexible input args | |
to be supplied on the RIGHT | |
- if argument referenced is NOT in current function scope or arglist; | |
its value will get searched for in the NEXT-ENCLOSING function closure | |
i.e. function WITHIN a function will search for next-enclosing function's 'closure' for variable declaration | |
- function argument DEFAULTING applies ORDER from LEFT to RIGHT; | |
where LEFT is OUTERMOST nested closure, | |
but RIGHT is INNERMOST | |
- constant argument DEFAULTING applies ORDER from RIGHT to LEFT | |
function f(x,y,z,a,b) { x(y(z(a,b)); } where x,y,z are lambda function types; | |
with nested function/closure scope; | |
where LEFTMOST functional arg x has OUTERMOST enclosing scope; | |
and RIGHTMOST functional arg z has INNERMOST nested enclosing scope | |
but where a, b are non-function types | |
and DEFAULT applies from LEFT to RIGHT | |
=> | |
function q(a,b) { lambda(x)(y,z,a,b) { lambda(y)(z,a,b) { lambda(z)(a,b); } } } | |
=> | |
equivalently: | |
function q(a,b) = f(x)(y)(z)(a,b); | |
=> | |
If you only call 'curried-form' f(x)(y) passing in actual x, y params, then you get a | |
PARTIAL-applied-function | |
the return value is a closure of lambda(z){(x(y))} | |
with passed-in the values of x and y to f(x,y). | |
e.g. | |
function fold(combineFunction, accumalator, list) {/* ... */} | |
function sum = curry(fold)(lambda(accumulated-value,element-of-list-e){a+e}))(0); | |
- where 0 initializes accumulated-value | |
- where lambda parameterizes a customizable combineFunction | |
another example with ANONYMOUS on-the-fly function declarations | |
http://www.secnetix.de/olli/Python/lambda_functions.hawk | |
nums = [ for i in range(0,(50+1) ] | |
# where 2-7 are prime numbers | |
for i in range(2,8): | |
# where op returning 0 (divisibility by prime factor) will filter OUT value from result | |
nums = filter(lambda x: x == i or x % i, nums) | |
How does it work? First, we put all numbers from 2 to 49 into a list called "nums". Then we have a "for" loop that iterates over all possible divisors, i.e. the value of "i" goes from 2 to 7. Naturally, all numbers that are multiples of those divisors cannot be prime numbers, so we use a filter function to remove them from the list. (This algorithm is called "the sieve of Eratosthenes".) | |
*** Curry vs Partial function application | |
*** dynamic anonymous lambda declaration | |
vs static reusable def declaration of object type function | |
https://docs.python.org/2/library/functools.html | |
https://mtomassoli.wordpress.com/2012/03/18/currying-in-python/ | |
""" | |
# ATTN: opChar is on LHS as defaultable PARTIAL function parameter | |
def binaryOp(opChar, leftNum, rightNum): | |
result = None | |
if (opChar == '+'): | |
result = leftNum + rightNum | |
elif (opChar == '-'): | |
result = leftNum - rightNum | |
elif (opChar == '*'): | |
result = leftNum * rightNum | |
elif (opChar == '/'): | |
result = leftNum / rightNum | |
else: | |
raise ValueError("Unrecognized Binary Operator Char") | |
return result | |
A = [ [1,2,3],[4,5,6] ] | |
B = [ [10,20,30],[40,50,60] ] | |
# DIMENSIONS of matrix | |
# inspect rows and columns of matrix | |
numRows = len(A) | |
numCols = len(A[0]) | |
print ('NumRows is: {}, and NumCols is: {}'.format(numRows, numCols)) | |
""" | |
ATTENTION: | |
- one REASON WHY SCALA is BETTER: | |
- TYPE of FUNCTION with SPECIFIC PARAM-LIST is VALIDATED | |
""" | |
# pointWise operation A op B, | |
# - where binaryOp is a function or lambda type | |
# ATTN: defaultable NESTED function args are put to LEFTMOST of arglist | |
# but non-function args are put to RIGHT of arglist | |
# where OUTERMOST function in nesting is the LEFTMOST | |
def pointWiseMatrixOp(binaryOp, leftM, rightM): | |
# check dimensions are equal | |
numRowsLeft = len(leftM) | |
numColsLeft = len(leftM[0]) | |
numRowsRight = len(rightM) | |
numColsRight = len(rightM[0]) | |
if ( (numRowsLeft != numRowsRight) or (numColsLeft != numColsRight)): | |
raise ValueError("Matrix Dimensions must match for Pointwise Op!") | |
# pre-allocate 2D matrix | |
# allocate rows | |
result = [ [ None for i in range(numCols) ] for i in range(numRows) ] | |
for row in range(numRows): | |
for col in range(numCols): | |
result[row][col] = binaryOp(leftM[row][col], rightM[row][col]) | |
return result | |
# ********** TEST DRIVER SCRIPT ************* | |
try: | |
# AWESOMENESS: curry on lambda-parameterized function which internally | |
# implements a NESTED operation, | |
# can use 'CURRY' syntactic sugar of CHAINING-EACH-LEVEL-OF-NESTING | |
# on function params list | |
# - i.e. any function args NOT DEFAULTED on the LHS, | |
# are the RHS REMAINDER (function or primitive variable) arglist | |
# of the resulting partially-defined complex function! | |
numBinaryAddOp = partial(binaryOp, '+') | |
matrixPointWiseAddOp = partial(pointWiseMatrixOp, numBinaryAddOp) | |
result = matrixPointWiseAddOp(A, B) | |
print ('\nLEFT:\n{}\nRIGHT:\n{}'.format(A, B)) | |
print ('POINTWISE ADD RESULT:\n{}'.format(result)) | |
except ValueError as err: | |
print(err) | |
except: | |
print("Unexpected error:", sys.exc_info()[0]) | |
raise | |
# 2D Matrix transpose (rows to columns) | |
# ATTN indexing: | |
# point = A[row][col] | |
# row = A[row] | |
# col => take column SLICE through matrix ACROSS all rows | |
transposeA = [ [row[j] for row in A] for j in range(numCols) ] | |
print ('\nA: ', A) | |
print ('TRANSPOSE A: ', transposeA) | |
# TODOs: | |
# 2D Matrix inversion (only on SQUARE) | |
# 2D Matrix multiply (N x K) x (K x M) => (N x M) | |
# slice column by accessing list comprehension as in transpose op, for specific column j |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment