Does scipy.linalg.inv check whether the matrix is diagonal? - python

Getting the inverse of a diagonal matrix is very simple and does not require complex methods. Does scipy.linalg.inv check whether the matrix is diagonal before it applies more complex methods or do I need to check this myself?

As you can see the Github code of scipy.linalg.inv, function inv first calls
getrf, getri, getri_lwork = get_lapack_funcs(('getrf', 'getri','getri_lwork'),
Then function getrf does it job to give the LU decomposition and so on. Now we have to investigate how getrf function gives the LU decomposition. Because if it checks if it's diagonal before to process the input matrix, then no need to check it yourself.
Function getrf is obtained by calling _get_funcs but I can't go further from there (_get_funcs is called with the following arguments _get_funcs(names, arrays, dtype, "LAPACK", _flapack, _clapack, "flapack", "clapack", _lapack_alias)).
I suggest that you run an experiment with a large diagonal matrix to compare the time given to spit the output with linalg and an inversion by hand.
Update (by question author):
import numpy as np
from scipy.linalg import inv
a = np.diag(np.random.random(19999))
b = a.copy()
np.fill_diagonal(a, 1/a.diagonal())
c = inv(b)
does not even require a time measuring tool: It it very obvious that inv is much slower... (that is surprisingly disappointing).

Please check: scipy.linalg.inv
If you put scipy.linalg.inv in try except if it raises LinAlgError when matrix a is singular. The determinant for singular matrix it zero.
try:
# your code that will (maybe) throw scipy.linalg.inv(your matrix)
except np.linalg.LinAlgError as err:
# It shows your matrix is singular
# Its determinant of a matrix is equal to zero
# The matrix does not have an inverse.
# You can conclude if the matrix is diagonal or not
If the determinant of a matrix is equal to zero:
The matrix is less than full rank. The matrix is singular. The matrix
does not have an inverse.
As manually:
def is_diagonal(matrix):
#create a dummy matrix
dummy_matrix = np.ones(matrix.shape, dtype=np.uint8)
# Fill the diagonal of dummy matrix with 0.
np.fill_diagonal(dummy_matrix, 0)
return np.count_nonzero(np.multiply(dummy_matrix, matrix)) == 0
diagonal_matrix = np.array([[3, 0, 0],
[0, 7, 0],
[0, 0, 4]])
print is_diagonal(diagonal_matrix)
>>> True
random_matrix = np.array([[3, 8, 0],
[1, 7, 8],
[5, 0, 4]])
print is_diagonal(random_matrix)
>>> False
scipy.sparse.dia_matrix.diagonal returns the k-th diagonal of the matrix.
from scipy.sparse import csr_matrix
A = csr_matrix([[1, 2, 0], [0, 0, 3], [4, 0, 5]])
A.diagonal()
array([1, 0, 5])
A.diagonal(k=1)
array([2, 3])
Also, from scipy.linalg import block_diag creates diagonal matrix if input arrays are square, therefore if they are not square, it can not create diagonal matrix.
Please consider in Jupyter you can find out time complexity. %timeit yourfunctionname

Related

How to determine two vectors are linearly dependent or independent in python?

Take in two 3 dimensional vectors, each represented as an array, and tell whether they are linearly independent. I tried to use np.linalg.solve() to get the solution of x, and tried to find whether x is trivial or nontrivial. But it shows 'LinAlgError: Last 2 dimensions of the array must be square'. Can anyone help me how to figure that out?
from sympy import *
import numpy as np
from scipy import linalg
from numpy import linalg
v1 = np.array([0, 5, 0])
v2 = np.array([0, -10, 0])
a = np.array([v1,v2])
b = np.zeros(3)
x = np.linalg.solve(a, b)
As your final matrix will be in a rectangular form, a simple approach of EigenValues will not work. You need to use the library of sympy
import sympy
import numpy as np
matrix = np.array([
[0, 5, 0],
[0, -10, 0]
])
_, indexes = sympy.Matrix(matrix).T.rref() # T is for transpose
print(indexes)
This will print the indexes of linearly independent rows. To further print them from the matrix, use
print(matrix[indexes,:])
To answer your specific question, check if two vectors are linearly dependant or not. You can most definitely use an if statement afterwards if it is the two vectors you are always going to check.
if len(indexes) == 2:
print("linearly independant")
else:
print("linearly dependant")
If one eigenvalue of the matrix is zero, its corresponding eigenvector is linearly dependent.
So the following code would work for simple case:
from sympy import *
import numpy as np
from scipy import linalg
from numpy import linalg
matrix = np.array([[0, 1, 0, 0], [0, 0, 1, 0], [0, 1, 1, 0], [1, 0, 0,
1]])
(lambdas, V) = np.linalg.eig(matrix.T)
print matrix[lambdas == 0, :]
Output: [[0 1 1 0]]

row echelon in python

this code is for 3*3 matrix. i need it to be applicable on ant m*n matrix.
import numpy as np
b=np.arange(1,10).reshape(3,3)
this checks the shape of matrix but it only applicable for 3*3 matrix
if b.shape[0]==b.shape[1]:
for i in range(b.shape[0]):
for j in range(b.shape[1]):
if i==1:
b[i]=b[i-i][j]*b[i]-[b[i][j]*b[i-i]]
i=i+1
b[i]=b[i-i][j]*b[i]-[b[i][j]*b[i-i]]
j=j+1
b[i]=b[i]-[b[i-1]*(b[i][j]/b[i-1][j])]
print(b)
There's actually a built-in library in python called sympy. The function Matrix().rref() can be used to obtain the reduced row echelon form of a matrix. The return value of this function includes two things: 1) the reduced row echelon form of the given matrix and 2) the indices of the rows in the matrix which contain the pivots (note that rows are 0-indexed).
Here's an example of how to use this function:
import sympy
sympy.Matrix([[1,2,3],[2,3,4]]).rref()
(Matrix([
[1, 0, -1],
[0, 1, 2]]), (0, 1))
You can find the implementation of Matrix().rref() here.

Applying matrix functions like scipy.linalg.eigh to higher dimensional arrays

I am new to numpy but have been using python for quite a while as an engineer.
I am writing a program that currently stores stress tensors as 3x3 numpy arrays within another NxM array which represents values through time and through the thickness of a wall, so overall it is an NxMx3x3 numpy array. I want to efficiently calculate the eigenvals and vectors of each 3x3 array within this larger array. So far I have tried to using "fromiter" but this doesn't seem to work because the functions returns 2 arrays. I have also tried apply_along_axis which also doesn't work because it says the inner 3x3 is not a square matrix? I can do it with list comprehension, but this doesn't seem ideal to resort to using lists.
Example just calculating eigenvals using list comprehension
import numpy as np
from scipy import linalg
a=np.random.random((2,2,3,3))
f=linalg.eigvalsh
ans=np.asarray([f(x) for x in a.reshape((4,3,3))])
ans.shape=(2,2,3)
I thought something like this would work but I have played around with it and can't get it working:
np.apply_along_axis(f,0,a)
BTW the 2x2 bit could be up to 5000x100 and this code is repeated ~50x50x200 times hence the need for efficiency. Any help would be greatly appreciated?
You can use numpy.linalg.eigh. It accepts an array like your example a.
Here's an example. First, create an array of 3x3 symmetric arrays:
In [96]: a = np.random.random((2, 2, 3, 3))
In [97]: a = a + np.transpose(a, axes=(0, 1, 3, 2))
In [98]: a[0, 0]
Out[98]:
array([[0.61145048, 0.85209618, 0.03909677],
[0.85209618, 1.79309413, 1.61209077],
[0.03909677, 1.61209077, 1.55432465]])
Compute the eigenvalues and eigenvectors of all the 3x3 arrays:
In [99]: evals, evecs = np.linalg.eigh(a)
In [100]: evals.shape
Out[100]: (2, 2, 3)
In [101]: evecs.shape
Out[101]: (2, 2, 3, 3)
Take a look at the result for a[0, 0]:
In [102]: evals[0, 0]
Out[102]: array([-0.31729364, 0.83148477, 3.44467813])
In [103]: evecs[0, 0]
Out[103]:
array([[-0.55911658, 0.79634401, 0.23070516],
[ 0.63392772, 0.23128064, 0.73800062],
[-0.53434473, -0.55887877, 0.63413738]])
Verify that it is the same as computing the eigenvalues and eigenvectors for a[0, 0] separately:
In [104]: np.linalg.eigh(a[0, 0])
Out[104]:
(array([-0.31729364, 0.83148477, 3.44467813]),
array([[-0.55911658, 0.79634401, 0.23070516],
[ 0.63392772, 0.23128064, 0.73800062],
[-0.53434473, -0.55887877, 0.63413738]]))

Return diagonal elements of scipy sparse matrix

Given a scipy.sparse.csr.csr_matrix, is there a quick way to return the elements on the diagonal?
The reason I would like to do this is to compute inv(D) A, where D is a diagonal matrix whose diagonal entries agree with A (A is my sparse matrix, guaranteed to have nonzeros on the diagonal).
Use csr_matrix.diagonal():
Returns the main diagonal of the matrix
Example:
>>> import numpy as np
>>> from scipy.sparse import csr_matrix
>>> mymat = csr_matrix((4, 4), dtype=np.int8)
>>> mymat.diagonal()
array([0, 0, 0, 0], dtype=int8)

form numpy array from possible numpy array

EDIT
I realized that I did not check my mwe very well and as such asked something of the wrong question. The main problem is when the numpy array is passed in as a 2d array instead of 1d (or even when a python list is passed in as 1d instead of 2d). So if we have
x = np.array([[1], [2], [3]])
then obviously if you try to index this then you will get arrays out (if you use item you do not). this same thing also applies to standard python lists.
Sorry about the confusion.
Original
I am trying to form a new numpy array from something that may be a numpy array or may be a standard python list.
for example
import numpy as np
x = [2, 3, 1]
y = np.array([[0, -x[2], x[1]], [x[2], 0, -x[0]], [-x[1], x[0], 0]])
Now I would like to form a function such that I can make y easily.
def skew(vector):
"""
this function returns a numpy array with the skew symmetric cross product matrix for vector.
the skew symmetric cross product matrix is defined such that
np.cross(a, b) = np.dot(skew(a), b)
:param vector: An array like vector to create the skew symmetric cross product matrix for
:return: A numpy array of the skew symmetric cross product vector
"""
return np.array([[0, -vector[2], vector[1]],
[vector[2], 0, -vector[0]],
[-vector[1], vector[0], 0]])
This works great and I can now write (assuming the above function is included)
import numpy as np
x=[2, 3, 1]
y = skew(x)
However, I would also like to be able to call skew on existing 1d or 2d numpy arrays. For instance
import numpy as np
x = np.array([2, 3, 1])
y = skew(x)
Unfortunately, doing this returns a numpy array where the elements are also numpy arrays, not python floats as I would like them to be.
Is there an easy way to form a new numpy array like I have done from something that is either a python list or a numpy array and have the result be just a standard numpy array with floats in each element?
Now obviously one solution is to check to see if the input is a numpy array or not:
def skew(vector):
"""
this function returns a numpy array with the skew symmetric cross product matrix for vector.
the skew symmetric cross product matrix is defined such that
np.cross(a, b) = np.dot(skew(a), b)
:param vector: An array like vector to create the skew symmetric cross product matrix for
:return: A numpy array of the skew symmetric cross product vector
"""
if isinstance(vector, np.ndarray):
return np.array([[0, -vector.item(2), vector.item(1)],
[vector.item(2), 0, -vector.item(0)],
[-vector.item(1), vector.item(0), 0]])
else:
return np.array([[0, -vector[2], vector[1]],
[vector[2], 0, -vector[0]],
[-vector[1], vector[0], 0]])
however, it gets very tedious having to write these instance checks all over the place.
Another solution would be to cast everything to an array first and then just use the array call
def skew(vector):
"""
this function returns a numpy array with the skew symmetric cross product matrix for vector.
the skew symmetric cross product matrix is defined such that
np.cross(a, b) = np.dot(skew(a), b)
:param vector: An array like vector to create the skew symmetric cross product matrix for
:return: A numpy array of the skew symmetric cross product vector
"""
vector = np.array(vector)
return np.array([[0, -vector.item(2), vector.item(1)],
[vector.item(2), 0, -vector.item(0)],
[-vector.item(1), vector.item(0), 0]])
but I feel like this is inefficient as it requires creating a new copy of vector (in this case not a big deal since vector is small but this is just a simple example).
My question is, is there a different way to do this outside of what I've discussed or am I stuck using one of these methods?
Arrays are iterable. You can write in your skew function:
def skew(x):
return np.array([[0, -x[2], x[1]],
[x[2], 0, -x[0]],
[-x[1], x[0], 0]])
x = [1,2,3]
y = np.array([1,2,3])
>>> skew(y)
array([[ 0, -3, 2],
[ 3, 0, -1],
[-2, 1, 0]])
>>> skew(x)
array([[ 0, -3, 2],
[ 3, 0, -1],
[-2, 1, 0]])
In any case your methods ended with 1st dimension elements being numpy arrays containing floats. You'll need in any case a call on the 2nd dimension to get the floats inside.
Regarding what you told me in the comments, you may add an if condition for 2d arrays:
def skew(x):
if (isinstance(x,ndarray) and len(x.shape)>=2):
return np.array([[0, -x[2][0], x[1][0]],
[x[2][0], 0, -x[0][0]],
[-x[1][0], x[0][0], 0]])
else:
return np.array([[0, -x[2], x[1]],
[x[2], 0, -x[0]],
[-x[1], x[0], 0]])
You can implement the last idea efficiently using numpy.asarray():
vector = np.asarray(vector)
Then, if vector is already a NumPy array, no copying occurs.
You can keep the first version of your function and convert the numpy array to list:
def skew(vector):
if isinstance(vector, np.ndarray):
vector = vector.tolist()
return np.array([[0, -vector[2], vector[1]],
[vector[2], 0, -vector[0]],
[-vector[1], vector[0], 0]])
In [58]: skew([2, 3, 1])
Out[58]:
array([[ 0, -1, 3],
[ 1, 0, -2],
[-3, 2, 0]])
In [59]: skew(np.array([2, 3, 1]))
Out[59]:
array([[ 0, -1, 3],
[ 1, 0, -2],
[-3, 2, 0]])
This is not an optimal solution but is a very easy one.
You can just convert the vector into list by default.
def skew(vector):
vector = list(vector)
return np.array([[0, -vector[2], vector[1]],
[vector[2], 0, -vector[0]],
[-vector[1], vector[0], 0]])

Categories

Resources