Computing the Nullspace

In [16]:
#keep
import numpy as np
import numpy.linalg as la
In [17]:
#keep
n = 5
np.random.seed(25)
A = np.random.randn(n, n)

# Decrease the rank
A[4] = A[0] + 5 * A[2]
A[1] = 3 * A[0] -2 * A[3]
In [18]:
#keep
from m_echelon import m_echelon
In [29]:
#keep
M, U = m_echelon(A.T)
In [30]:
#keep
la.norm(
    M.dot(A.T) - U)
Out[30]:
1.2599818366099757e-15
In [31]:
#keep
U.round(3)
Out[31]:
array([[  1.027,   0.676,  -0.232,   1.202,  -0.135],
       [  0.   ,  -3.498,  -1.468,   1.749,  -7.342],
       [  0.   ,   0.   ,  -2.14 ,   0.   , -10.699],
       [  0.   ,   0.   ,   0.   ,   0.   ,  -0.   ],
       [  0.   ,   0.   ,   0.   ,   0.   ,  -0.   ]])

Now define NUT as vectors spanning the nullspace of $N(U^T)$.

In [32]:
NUT = np.eye(n)[:, 3:]
NUT
Out[32]:
array([[ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.],
       [ 1.,  0.],
       [ 0.,  1.]])

Check that it's actually a nullspace:

In [33]:
U.T.dot(NUT)
Out[33]:
array([[  0.000e+00,   0.000e+00],
       [  0.000e+00,   0.000e+00],
       [  0.000e+00,   0.000e+00],
       [  3.644e-16,   6.163e-33],
       [ -1.776e-15,  -2.174e-16]])

Now define NA as some vectors spanning $N(A)$:

In [34]:
NA = M.T.dot(NUT)

And check:

In [35]:
A.dot(NA)
Out[35]:
array([[  1.110e-16,  -1.110e-16],
       [  4.441e-16,  -4.441e-16],
       [ -2.220e-16,  -5.551e-17],
       [  1.110e-16,   1.110e-16],
       [ -8.882e-16,  -6.661e-16]])
In [ ]: