Find values for which matrix becomes singular in Python - python

Let's take the following square matrix:
import numpy as np
A = np.array([[10.0, -498.0],
[-2.0, 100.0]])
A will be singular if its determinant (A[0,0]*A[1,1]-A[0,1]*A[1,0]) is zero. For example, A will be singular if A[0,1] takes the value -500.0 (all else unchanged):
from sympy import symbols, Eq, solve
y = symbols('y')
eq = Eq(A[0,0]*A[1,1]-y*A[1,0])
sol = solve(eq)
sol
How to find all values (A[0,0],A[0,1],...) for which A (or any given square matrix) becomes singular efficiently (I work with large matrices)? Many thanks in advance.

The trick is to use Laplace expansion to calculate the determinant. The formula is
det(A) = sum (-1)^(i+j) * a_ij * M_ij
So to make a matrix singular, you just need to use the above formula, change the subject to a_ij and set det(A) = 0. It can be done like this:
import numpy as np
def cofactor(A, i, j):
A = np.delete(A, (i), axis=0)
A = np.delete(A, (j), axis=1)
return (-1)**(i+j) * np.linalg.det(A)
def make_singular(A, I, J):
n = A.shape[0]
s = 0
for i in range(n):
if i != J:
s += A[I, i] * cofactor(A, I, i)
M = cofactor(A, I, J)
if M == 0:
return 'No solution'
else:
return -s / M
Testing:
>>> M = np.array([[10.0, -498.0],
[-2.0, 100.0]])
>>> make_singular(M, 0, 1)
-500.0000000000002
>>> M = np.array([[10.0, -498.0],
[0, 100.0]])
>>> make_singular(M, 0, 1)
'No solution'

This thing works for square matrices...
What it does is it bruteforces through every item in the matrix and check if its singular, (so theres a lot of messy output, ue it if you like it tho)
And also very important, it is a Recursive function that returns a matrix if it is singular. So it throws RecursiveError recursively....:|
This is the code i have come up with, you can use it if its okay for you
import numpy as np
def is_singular(_temp_int:str, matrix_size:int):
kwargs = [int(i) for i in _temp_int]
arr = [] # Creates the matrix from the given size
temp_count = 0
for i in range(matrix_size):
arr.append([])
m = arr[i]
for j in range(matrix_size):
m.append(int(_temp_int[temp_count]))
temp_count += 1
n_array = np.array(arr)
if int(np.linalg.det(n_array)) == 0:
print(n_array) # print(n_array) for a pretty output or print(arr) for single line output of the determinant matrix
_temp_int = str(_temp_int[:-len(str(int(_temp_int)+1))] + str(int(_temp_int)+1))
is_singular(_temp_int, matrix_size)
# Only square matrices, so only one-digit integer as input
print("List of singular matrices in the size of '3x3': ")
is_singular('112278011', 3)
# Just give a temporary integer string which will be converted to matrix like [[1, 1, 2], [2, 7, 8], [0, 1, 1]]
# From the provided integer string, it adds up 1 after every iteration
I think this is the code you want, let me know if its not working

Related

generating random matrices in python

In the following code I have implemented Gaussian elimination with partial pivoting for a general square linear system Ax=b. I have tested my code and it produced the right output. However now I am trying to do the following but I am not quite sure how to code it, looking for some help with this!
I want to test my implementation by solving Ax=b where A is a random 100x100 matrix and b is a random 100x1 vector.
In my code I have put in the matrices
A = np.array([[3.,2.,-4.],[2.,3.,3.],[5.,-3.,1.]])
b = np.array([[3.],[15.],[14.]])
and gotten the following correct output:
[3. 1. 2.]
[3. 1. 2.]
but now how do I change it to generate the random matrices?
here is my code below:
import numpy as np
def GEPP(A, b, doPricing = True):
'''
Gaussian elimination with partial pivoting.
input: A is an n x n numpy matrix
b is an n x 1 numpy array
output: x is the solution of Ax=b
with the entries permuted in
accordance with the pivoting
done by the algorithm
post-condition: A and b have been modified.
'''
n = len(A)
if b.size != n:
raise ValueError("Invalid argument: incompatible sizes between"+
"A & b.", b.size, n)
# k represents the current pivot row. Since GE traverses the matrix in the
# upper right triangle, we also use k for indicating the k-th diagonal
# column index.
# Elimination
for k in range(n-1):
if doPricing:
# Pivot
maxindex = abs(A[k:,k]).argmax() + k
if A[maxindex, k] == 0:
raise ValueError("Matrix is singular.")
# Swap
if maxindex != k:
A[[k,maxindex]] = A[[maxindex, k]]
b[[k,maxindex]] = b[[maxindex, k]]
else:
if A[k, k] == 0:
raise ValueError("Pivot element is zero. Try setting doPricing to True.")
#Eliminate
for row in range(k+1, n):
multiplier = A[row,k]/A[k,k]
A[row, k:] = A[row, k:] - multiplier*A[k, k:]
b[row] = b[row] - multiplier*b[k]
# Back Substitution
x = np.zeros(n)
for k in range(n-1, -1, -1):
x[k] = (b[k] - np.dot(A[k,k+1:],x[k+1:]))/A[k,k]
return x
if __name__ == "__main__":
A = np.array([[3.,2.,-4.],[2.,3.,3.],[5.,-3.,1.]])
b = np.array([[3.],[15.],[14.]])
print (GEPP(np.copy(A), np.copy(b), doPricing = False))
print (GEPP(A,b))
You're already using numpy. Have you considered np.random.rand?
np.random.rand(m, n) will get you a random matrix with values in [0, 1). You can further process it by multiplying random values or rounding.
EDIT: Something like this
if __name__ == "__main__":
A = np.round(np.random.rand(100, 100)*10)
b = np.round(np.random.rand(100)*10)
print (GEPP(np.copy(A), np.copy(b), doPricing = False))
print (GEPP(A,b))
So I would use np.random.randint for this.
numpy.random.randint(low, high=None, size=None, dtype='l')
which outputs a size-shaped array of random integers from the appropriate distribution, or a single such random int if size not provided.
low is the lower bound of the ints you want in your range
high is one greater than the upper bound in your desired range
size is the dimensions of your output array
dtype is the dtype of the result
so if I was you I would write
A = np.random.randint(0, 11, (100, 100))
b = np.random.randint(0, 11, 100)
Basically you could create the desired matrices with ones and then iterate over them, setting each value to random.randint(0,100) for example.
Empty matrix with ones is:
one_array = np.ones((100, 100))
EDIT:
like:
for x in one_array.shape[0]:
for y in one_array.shape[1]:
one_array[x][y] = random.randint(0, 100)
A = np.random.normal(size=(100,100))
b = np.random.normal(size=(100,1))
x = np.linalg.solve(A,b)
assert max(abs(A#x - b)) < 1e-12
Clearly, you can use different distributions than normal, like uniform.
You can use numpy's native rand function:
np.random.rand()
In your code just define A and b as:
A = np.random.rand(100, 100)
b = np.random.rand(100)
This will generate 100x100 matrix and 100x1 vector (both numpy arrays) filled with random values between 0 and 1.
See the docs for this function to learn more.

Getting the adjugate of matrix in python

i having some problems in solving the question finding the adjugate of a matrix by given the formula of cofactor matrix
c[i][j] = (-1)**(i+j)*m[i][j]
where m stand for determinant of matrix.
x = np.array([[1,3,5],[-2,-4,-5],[3,6,1]] , dtype = 'int')
i only able to do this and don't know how to continue , please help
to find the cofactor i have this hint
def COF(C)
create an empty matrix CO
for row
for col
sel_rows = all rows except current row
sel_columns = all cols except current col
MATij = [selected rows and selected columns]
compute COij
return CO
import numpy as np
x = np.array([[1,3,5],[-2,-4,-5],[3,6,1]] , dtype = 'int')
m = np.linalg.det(x)
c =[[i for i in range(3)] for j in range(3)]
for i in range(3):
for j in range(3):
c[i][j] = (-1)*(i+j)*m
The c.T to work without any errors, the array c should be a numpy array. Here the array c that #TaohidulIslam declared is a Python List. So you are getting an error.
Declare c as follows:
c =np.array([[i for i in range(3)] for j in range(3)])
You can calculate the adjugate matrix by the transposal of the cofactor matrix
with the method below which is suitable for non singular matrices.
First, find the cofactor matrix, as follows:
https://www.geeksforgeeks.org/how-to-find-cofactor-of-a-matrix-using-numpy/
Then, find the transposal of the cofactor matrix.
import numpy as np
import math as mth
# get cofactors matrix
def getcofat(x):
eps = 1e-6
detx = np.linalg.det(x)
if (mth.fabs(detx) < eps):
print("No possible to get cofactors for singular matrix with this method")
x = None
return x
invx = np.linalg.pinv(x)
invxT = invx.T
x = invxT * detx
return x
# get adj matrix
def getadj(x):
eps = 1e-6
detx = np.linalg.det(x)
if (mth.fabs(detx) < eps):
print("No possible to get adj matrix for singular matrix with this method")
adjx = None
return adjx
cofatx = getcofat(x)
adjx = cofatx.T
return adjx
A = np.array([[1, 3, 5], [-2, -4, -5], [3, 6, 1]])
print(A)
print(np.linalg.det(A))
Acofat = getcofat(A)
print(Acofat)
Aadj = getadj(A)
print(Aadj)

I would like to create a nxn matrix from two length n arrays using an addition function in python

I have two arrays n and k and I would like to create a matrix with the formula n + ik. I would like the matrix to have the following form;
n[0]+ik[0] n[0]+ik[1] n[0]+ik[2] etc.
n[1]+ik[0] n[1]+ik[1] n[0]+ik[2] etc.
etc.
so far I have;
z = 0 + 1j
for i,j in n
for i,j in k
n_com = n + k*z
but I know that its not working, and I realise it doesnt really make any sense. Do I have to use append?
I think the following code is clear
n = [1, 2, 3]
k = [4, 5, 6]
mat = []
for i in range(len(n)):
row = [] # ready to make a row
for j in range(len(k)):
row.append(n[i] + 1j * k[j])
mat.append(row) # add the row to the mat
print(mat) # we get it
A more pythonic way would be (if you are interested)
mat = [[x + 1j * y for y in k] for x in n]
Further, many science people would use numpy, you may expect better performance and usability when matrix is large.
import numpy as np
n = np.array(n)
k = np.array(k).reshape((-1, 1))
mat = n + k.repeat(len(n), 1) * 1j

Add Condition on generated numpy

I have piece of code :
V = numpy.floor(3*np.random.rand(5,5))
print V
It create random result of array in 5x5 table, how to add condition "1" only generate x times, "2" only generate y times, else are "0".
Thanks
Try this:
import numpy as np
def newArray( x, y, n):
if x + y > n ** 2:
print "Values error!"
return
res = [[0 for col in range(n)] for row in range(n)]
# Create roulette
roulette = range(0, n ** 2)
printkxtimes(res, roulette, 1, x, n)
printkxtimes(res, roulette, 2, y, n)
print res
# This function draws random element from roulette,
# gets the position in array and sets value of this position to k.
# Then removes this element from roulette to prevent drawing it again
def printkxtimes(array, roulette, k, x, n):
for i in xrange(0, x):
r = int(np.floor(roulette.__len__()*np.random.rand(1))[0])
array[roulette[r] / n][roulette[r] % n] = k
roulette.pop(r)
newArray(10,2,5)
A little explanation of roulette :
Every element of table res can be represented equivalently with a number from range(0, n^2) :
z = row*n + column <=> row = int(z/n) , column= z%n
We can then represent the list of positions in table res as the table [0,1,...,n^2-1]
How about the following?
import numpy as np
shape = (5, 5)
area = shape[0] * shape[1]
np.random.permutation([1]*x + [2]*y + [0]*(area-x-y)).reshape(shape)
Seems pretty simple. You take a random permutation of [1, ... 1, 2, ... 2, 0, ... 0] and then you just turn it into a square. I'm not too sure but it also seems less computationally expensive and is also extensible to n numbers or dimensions quite easily.

Can I vectorize this 2d array indexing where the 2nd dimension depends on the value of the first?

In the example below I have a 2D array that has some real results that are shifted and padded. The shifts depend on the row (the padding is used to make the array rectangular as required by numpy). Is it possible to extract the real results without a Python loop?
import numpy as np
# results are 'shifted' where the shift depends on the row
shifts = np.array([0, 8, 4, 2], dtype=int)
max_shift = shifts.max()
n = len(shifts)
t = 10 # length of the real results we care about
a = np.empty((n, t + max_shift), dtype=int)
b = np.empty((n, t), dtype=int)
for i in range(n):
a[i] = np.concatenate([[0] * shifts[i], # shift
(i+1) * np.arange(1, t+1), # real data
[0] * (max_shift - shifts[i]) # padding
])
print "shifted and padded\n", a
# I'd like to remove this Python loop if possible
for i in range(n):
b[i] = a[i, shifts[i]:shifts[i] + t]
print "real data\n", b
You can use two array to get the data out:
a[np.arange(4)[:, None], shifts[:, None] + np.arange(10)]
or:
i, j = np.ogrid[:4, :10]
a[i, shifts[:, None]+j]
This is called Advanced indexing in NumPy document.

Categories