Matthew Bennett

Logo

I am a data scientist working on time series forecasting (using R and Python 3) at the London Ambulance Service NHS Trust. I earned my PhD in cognitive neuroscience at the University of Glasgow working with fmri data and neural networks. I favour linux machines, and working in the terminal with Vim as my editor of choice.

View my GitHub Profile

View my LinkedIn Profile

View my CV

Pivots, rank, singularity, determinant, positive definiteness

Pivots

Now that we have the method of elimination, we can get a lot of information about a matrix easily. The pivots of a matrix are the first non-zero numbers sitting in each row of U after elimination. Here we return those pivots along with the columns in which they were found:

def pivots(self):
    _, _, _, U, _, _ = self.elimination()
    # extract the first non-zero from each row - track the column number
    U = U.tr()
    pivots = {}
    found = []
    for j, col in enumerate(U.data):
        piv_pos = sum(list(map(bool, col)))
        if piv_pos not in found:
            found.append(piv_pos)
            pivots[j] = col[piv_pos-1]
    return pivots

Demo

We create two matrices, call the pivots method and print the results:

import linalg as la

A = la.Mat([[4, 2, 4],
            [0, 3, 3],
            [0, 1, 2]])

B = la.Mat([[1, 2, 3],
            [4, 5, 6],
            [5, 7, 9]])

print(A.pivots())

print(B.pivots())

Outputs:

>>> print(A_pivots)
{0: 4.0, 1: 3.0, 2: 1.0}

>>> print(B_pivots)
{0: 1.0, 1: -3.0}

Rank

The rank of a matrix tells us a huge amount, and is simply the number of pivots:

def rank(self):
    return len(self.pivots())

Demo

We create two matrices, call the rank method and print the results:

A = la.Mat([[1, 2, 3],
            [4, 5, 6],
            [5, 8, 9]])

B = la.Mat([[1, 2, 3],
            [4, 5, 6],
            [5, 7, 9]])

A_rank = A.rank()

B_rank = B.rank()

print(A_rank)
print()
print(B_rank)

Outputs:

>>> print(A_rank)
3

>>> print(B_rank)
2

Singularity

The matrix is singular if during elimination row exchanges can't avoid a zero in a pivot position. Our elimination method already returns a variable indicating singularity, and so this next method just provides a cleaner way of accessing that variable:

def is_singular(self):
    _, _, _, _, singular, _ = self.elimination()
    return singular

Demo

We create two matrices, call the is_singular method and print the results:

A = la.Mat([[1, 2, 3],
            [4, 5, 6],
            [5, 8, 9]])

B = la.Mat([[1, 2, 3],
            [4, 5, 6],
            [5, 7, 9]])

A_sing = A.is_singular()

B_sing = B.is_singular()

print(A_sing)
print()
print(B_sing)

Outputs:

>>> print(A_sing)
0
>>> print(B_sing)
1

Determinant

The determinant of a matrix is simply the product of the pivots, with a negative sign if there were an odd number of row exchanges. Here we don't use the pivot method we wrote, as we also need the number of row exchanges:

def determinant(self):
    # find U
    _, _, _, U, _, row_exchange_count = self.elimination()
    # muliply the pivots
    det = 1
    diag_vals = U.diag()
    for val in diag_vals:
        det *= val
    # if an odd number of row exchanges, multiply determinant by minus one
    if row_exchange_count % 2:
        det *= -1
    return det

Demo

We create two matrices, call the determinant method and print the results:

A = la.Mat([[1, 2, 3],
            [4, 5, 6],
            [5, 8, 9]])

B = la.Mat([[1, 2, 3],
            [4, 5, 6],
            [5, 7, 9]])

A_determinant = A.determinant()

B_determinant = B.determinant()

print(A_determinant)
print()
print(B_determinant)

Outputs:

>>> print(A_determinant)
6.0

>>> print(B_determinant)
-0.0

Is matrix positive definite?

If all the pivots of a matrix are strictly greater than zero, then the matrix is positive definite. If they are greater than or equal to zero, then the matrix is positive semi-definite. Similary, strictly less than zero means the matrix is negative definite and if there are also some zeros then the matrix is negative semi-definite.

Code implementation

We also create a method that returns the base-10 integer representation of a base-2 (i.e. binary) number. The binary number is constructed from the TRUE/FALSE answers to 3 queries: whether the matrix contains any negative, zero (i.e less pivots than columns), or positive pivots. The answers to these 3 queries uniquely determine what kind of 'definiteness' (if any) we can call the matrix. Thus the method returns a number (between 0 and 7: since $2^3 = 8$ possibilities) which can be used as a unique identifier of the 'definiteness':

def pivot_sign_code(self):
    '''Returns number between 0 and 7 according to signs of pivots. We do
    this by constructing a 3-bit binary number, where each bit represents
    the presence/absence of negative, zero, or positive pivots, and then
    converting from binary to a base 10 integer.'''
    pivot_info = self.pivots().items()

    neg = int(any(piv[1] < 0 for piv in pivot_info))
    semi = int(len(pivot_info) < self.size(1))
    pos = int(any(piv[1] > 0 for piv in pivot_info))

    return int(str(neg) + str(semi) + str(pos), 2)

def is_negdef(self):
    return self.pivot_sign_code() == 4

def is_negsemidef(self):
    return self.pivot_sign_code() == 6

def is_possemidef(self):
    return self.pivot_sign_code() == 3

def is_posdef(self):
    return self.pivot_sign_code() == 1

Demo

We use the same matrices as in the Pivots section above , and call the pivots method and check if they positive definite:

import linalg as la

A = la.Mat([[4, 2, 4],
            [0, 3, 3],
            [0, 1, 2]])

B = la.Mat([[1, 2, 3],
            [4, 5, 6],
            [5, 7, 9]])

print(A.pivots())
A.is_posdef()

print(B.pivots())
B.is_posdef()

Outputs:

>>> print(A_pivots)
{0: 4.0, 1: 3.0, 2: 1.0}

>>> A.is_posdef()
True

>>> print(B_pivots)
{0: 1.0, 1: -3.0}

>>> B.is_posdef()
False

< Back substitution

Inverse >

back to project main page
back to home