Last active
April 13, 2020 02:35
-
-
Save philzook58/269dcb47aefc6b1dffe51860ae52d1e7 to your computer and use it in GitHub Desktop.
FinVect with numpy
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
import numpy as np | |
import scipy | |
import scipy.linalg | |
# https://docs.scipy.org/doc/numpy/user/basics.subclassing.html | |
class FinVect(np.ndarray): | |
def __new__(cls, input_array, info=None): | |
# Input array is an already formed ndarray instance | |
# We first cast to be our class type | |
obj = np.asarray(input_array).view(cls) | |
assert(len(obj.shape) == 2) # should be matrix | |
# add the new attribute to the created instance | |
# Finally, we must return the newly created object: | |
return obj | |
@property | |
def dom(self): | |
''' returns the domain of the morphism. This is the number of columns of the matrix''' | |
return self.shape[1] | |
@property | |
def cod(self): | |
''' returns the codomain of the morphism. This is the numer of rows of the matrix''' | |
return self.shape[0] | |
def idd(n): | |
''' The identity morphism is the identity matrix. Isn't that nice? ''' | |
return FinVect(np.eye(n)) | |
def compose(f,g): | |
''' Morphism composition is matrix multiplication. Isn't that nice?''' | |
return f @ g |
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
import numpy as np | |
import scipy | |
import scipy.linalg | |
# https://docs.scipy.org/doc/numpy/user/basics.subclassing.html | |
class FinVect(np.ndarray): | |
def __new__(cls, input_array, info=None): | |
# Input array is an already formed ndarray instance | |
# We first cast to be our class type | |
obj = np.asarray(input_array).view(cls) | |
assert(len(obj.shape) == 2) # should be matrix | |
# add the new attribute to the created instance | |
# Finally, we must return the newly created object: | |
return obj | |
@property | |
def dom(self): | |
''' returns the domain of the morphism. This is the number of columns of the matrix''' | |
return self.shape[1] | |
@property | |
def cod(self): | |
''' returns the codomain of the morphism. This is the numer of rows of the matrix''' | |
return self.shape[0] | |
def idd(n): | |
''' The identity morphism is the identity matrix. Isn't that nice? ''' | |
return FinVect(np.eye(n)) | |
def compose(f,g): | |
''' Morphism composition is matrix multiplication. Isn't that nice?''' | |
return f @ g | |
def par(f,g): | |
''' One choice of monoidal product is the direct sum ''' | |
r, c = f.shape | |
rg, cg = g.shape | |
return Vect(np.block( [ [f , np.zeros((r,cg))] , | |
[np.zeros((rg,c)) , g ]] )) | |
def par2(f,g): | |
''' another choice is the kroncker product''' | |
return np.kron(f,g) | |
def monic(self): | |
''' Is mapping injective. | |
In other words, maps every incoming vector to distinct outgoing vector. | |
In other words, are the columns independent. | |
In other words, does `self @ g == self @ f` imply `g == f` forall g,f | |
https://en.wikipedia.org/wiki/Monomorphism ''' | |
return np.linalg.matrix_rank(self) == self.dom | |
def epic(self): | |
''' is mapping surjective? ''' | |
return np.linalg.matrix_rank(self) == self.cod | |
def product(a,b): | |
''' The product takes in two object (dimensions) a,b and outputs a new dimension d , two projection morphsisms | |
and a universal morphism. | |
The "product" dimension is the sum of the individual dimensions (funky, huh?) | |
''' | |
d = a + b # new object | |
p1 = FinVect(np.hstack( [np.eye(a) , np.zeros((a,b))] )) | |
p2 = FinVect(np.hstack( [np.zeros((b,a)), np.eye(b) ] )) | |
def univ(f,g): | |
assert(f.dom == g.dom) # look at diagram. The domains of f and g must match | |
e = np.vstack(f,g) | |
# postconditions. True by construction. | |
assert( np.allclose(p1 @ e , f )) # triangle condition 1 | |
assert( np.allclose(p2 @ e , g ) ) # triangle condition 2 | |
return e | |
return d, p1, p2, univ | |
def coproduct(a,b): | |
''' The coproduct takes in two object (dimensions) and outputs a new dimension and a universal morphism | |
''' | |
d = a + b # new object | |
p1 = FinVect(np.hstack( [np.eye(a) , np.zeros((a,b))] )) | |
p2 = FinVect(np.hstack( [np.zeros((b,a)), np.eye(b) ] )) | |
def univ(f,g): | |
assert(f.dom == g.dom) # look at diagram. The domains of f and g must match | |
return np.vstack(f,g) | |
return d, p1, p2, univ | |
return a + b, lambda f,g : np.hstack(f,g) | |
def equalizer(f,g): | |
''' The equalizer is one way of expression an equality problem in the language of Vect. | |
It converts an implicit representation of the nullspace into an explicit representation of the | |
''' | |
assert( f.dom == g.dom and f.cod == g.cod ) # Must be parallel morphisms | |
return Vect( np.linalg.null_space( np.hstack([f,-g]) ) ) | |
def coequalizer(f,g): | |
assert( f.dom == g.dom and f.cod == g.cod ) # Must be parallel morphisms | |
#assert() | |
#Vect( scipy.linalg.orth(np.vstack( [f,g] )) ) | |
pass | |
def pullback(f,g): | |
assert( f.cod == g.cod ) # Most have common codomain | |
# horizontally stack the two operations. | |
null = scipy.linalg.null_space( np.hstack([f,-g]) ) | |
d = null.shape[1] # dimension object of corner of pullback | |
p1 = FinVect(null[:f.dom, :]) | |
p2 = FinVect(null[f.dom:, :]) | |
def univ(u,v): | |
# preconditions | |
assert(u.dom == v.dom ) | |
assert( np.allclose(f @ u , g @ v ) ) # given a new square. This means u,v have to inject into the nullspace | |
e = null.T @ np.vstack([u,v]) # calculate universal morphism == p1 @ u + p2 @ v | |
# postcondition | |
assert( np.allclose(p1 @ e, u )) # verify triangle 1 | |
assert( np.allclose(p2 @ e, v ) ) # verify triangle 2 | |
return e | |
# postcondition | |
assert( np.allclose( f @ p1, g @ p2 ) ) # These projections form a commutative square. | |
return d, p1, p2, univ | |
def pushout(f,g): | |
assert( f.dom == g.dom ) # Most have common codomain | |
pass | |
def exponential(self): | |
pass | |
''' | |
The expential object is Hom( V , W -> U ) ~ Hom( V \otimes W , U ) | |
''' | |
def terminal(): | |
''' terminal object has unique morphism to it ''' | |
return 0, lambda x : FinVect(np.ones((0, x))) | |
def initial(): | |
''' the initial object has a unique morphism from it | |
The initial and final object are the same in FinVect''' | |
return 0, lambda x : FinVect(np.ones((x, 0))) |
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
def monic(self): | |
''' Is mapping injective. | |
In other words, maps every incoming vector to distinct outgoing vector. | |
In other words, are the columns independent. | |
In other words, does `self @ g == self @ f` imply `g == f` forall g,f | |
https://en.wikipedia.org/wiki/Monomorphism ''' | |
return np.linalg.matrix_rank(self) == self.dom | |
def epic(self): | |
''' is mapping surjective? ''' | |
return np.linalg.matrix_rank(self) == self.cod |
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
def product(a,b): | |
''' The product takes in two object (dimensions) a,b and outputs a new dimension d , two projection morphsisms | |
and a universal morphism. | |
The "product" dimension is the sum of the individual dimensions (funky, huh?) | |
''' | |
d = a + b # new object | |
p1 = FinVect(np.hstack( [np.eye(a) , np.zeros((a,b))] )) | |
p2 = FinVect(np.hstack( [np.zeros((b,a)), np.eye(b) ] )) | |
def univ(f,g): | |
assert(f.dom == g.dom) # look at diagram. The domains of f and g must match | |
e = np.vstack(f,g) | |
# postconditions. True by construction. | |
assert( np.allclose(p1 @ e , f )) # triangle condition 1 | |
assert( np.allclose(p2 @ e , g ) ) # triangle condition 2 | |
return e | |
return d, p1, p2, univ |
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
def pullback(f,g): | |
assert( f.cod == g.cod ) # Most have common codomain | |
# horizontally stack the two operations. | |
null = scipy.linalg.null_space( np.hstack([f,-g]) ) | |
d = null.shape[1] # dimension object of corner of pullback | |
p1 = FinVect(null[:f.dom, :]) | |
p2 = FinVect(null[f.dom:, :]) | |
def univ(q1,q2): | |
# preconditions | |
assert(q1.dom == q2.dom ) | |
assert( np.allclose(f @ q1 , g @ q2 ) ) # given a new square. This means u,v have to inject into the nullspace | |
u = null.T @ np.vstack([q1,q2]) # calculate universal morphism == p1 @ u + p2 @ v | |
# postcondition | |
assert( np.allclose(p1 @ u, q1 )) # verify triangle 1 | |
assert( np.allclose(p2 @ u, q2 ) ) # verify triangle 2 | |
return u | |
# postcondition | |
assert( np.allclose( f @ p1, g @ p2 ) ) # These projections form a commutative square. | |
return d, p1, p2, univ |
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
def terminal(): | |
''' terminal object has unique morphism to it ''' | |
return 0, lambda x : FinVect(np.ones((0, x))) | |
def initial(): | |
''' the initial object has a unique morphism from it | |
The initial and final object are the same in FinVect''' | |
return 0, lambda x : FinVect(np.ones((x, 0))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment