PLU (LUP) decomposition failed with zeros on main diagonal - python

I am trying to write PLU (LUP) decomposition using Gaussian elimination. I found this article with this algorithm:
def plu(A):
#Get the number of rows
n = A.shape[0]
#Allocate space for P, L, and U
U = A.copy()
L = np.eye(n, dtype=np.double)
P = np.eye(n, dtype=np.double)
#Loop over rows
for i in range(n):
#Permute rows if needed
for k in range(i, n):
if ~np.isclose(U[i, i], 0.0):
break
U[[k, k+1]] = U[[k+1, k]]
P[[k, k+1]] = P[[k+1, k]]
#Eliminate entries below i with row
#operations on U and #reverse the row
#operations to manipulate L
factor = U[i+1:, i] / U[i, i]
L[i+1:, i] = factor
U[i+1:] -= factor[:, np.newaxis] * U[i]
return P, L, U
With normal matrix it's running well but when on main diagonal appears 0 it breaks. For example with this matrix:
[[1, 0, 26, 10],
[60, 0, 75, 50],
[45, 90, 31, 100],
[30, 45, 10, 0]]
P
[[1. 0. 0. 0.]
[0. 0. 1. 0.]
[0. 1. 0. 0.]
[0. 0. 0. 1.]]
L
[[ 1. 0. 0. 0.]
[60. 1. 0. 0.]
[45. 0. 1. 0.]
[30. 0.5 0.13501684 1.]]
U
[[1. 0. 26. 10. ]
[0. 90. -1139. -350. ]
[0. 0. -1485. -550. ]
[0. 0. 0. -50.74074074]]
Multiplication of PLU will not give the original matrix.
So I tried to fix the algorithm: when rows swapping they are swapping only in P and U, so I add L[[k, k + 1]] = L[[k + 1, k]] in the inner loop to swap it in the L too. Also I changed the initialization of L: in the start I fill L with zeros L = np.zeros_like(A) and add identity matrix in the end L += np.eye(n, dtype=np.double).
But fixed version of algorithm give wrong result when top left element is 0. Any ideas what I'm doing wrong?

I think I fix it.
def get_plu_decomposition(A):
n = A.shape[0]
U = csr_matrix(A.copy())
L = csr_matrix(np.zeros_like(A))
P = csr_matrix(np.eye(n, dtype=np.double))
for i in range(n - 1):
index = np.argmax(abs(U[i:, i]))
if not U[i:, i][index] != 0:
continue
index += i
if index != i:
U[[index, i]] = U[[i, index]]
P[[index, i]] = P[[i, index]]
L[[index, i]] = L[[i, index]]
factor = U[i + 1:, i] / U[i, i]
L[i + 1:, i] = factor
U[i + 1:] -= factor * U[i]
L += csr_matrix(np.eye(n, dtype=np.double))
P = P.transpose()
assert (np.allclose(A, (P#L#U).toarray()))
return P, L, U
The problem was in swapping rows and transposing P matrix

Related

Calculating the sum of array-based terms in SymPy [ValueError: Invalid limits given]

I have been trying to obtain some numerical outputs from the sum below. It needs to give numerical outputs normally. However, I receive an error related to the limits.
I have a sum with a low boundary of i = 0 and an upper boundary of i = k-1 mathematically. I took (i, 0, k) because the last term is excluded by SymPy, Sum. Besides, I need to get the result within this nested for loop. Could there be a mismatch between for loops and sum? Even so, I cannot change for loops for k and Nt. Here, k depends on the Nt.
The code:
import numpy as np
from sympy import *
from sympy import Sum
Nx = 31
Nt = 17
tau = .85 / Nt
u = np.ones((Nt, Nx)) * np.sin(np.pi)
Sigma = np.zeros((Nt, Nx))
for k in range(1, Nt):
for i in range(1, k):
for j in range(1, Nx-1):
#define sum
Sigma[i, j] = Sum( ((u[i+1][j] - u[i][j]) / tau * 97.1), (i, 0, k)).doit()
print(Sigma[i, j])
Error:
---> 16 Sigma[i, j] = Sum( ((u[i+1][j] - u[i][j]) / tau * 97.1), (i, 0, k) ).doit()
ValueError: Invalid limits given: ((1, 0, 2),)
Also, I am confused about sum, Sum or summation. None of these gave me a numerical result, or most likely I am not using these methods correctly. How can I get the numerical outputs? Again, I've tried np.sum() below.
for k in range(1, Nt):
for i in range(1, k):
for j in range(1, Nx-1):
#define sum
Sigma[i, j] = np.sum( ((u[i+1][j] - u[i][j]) / tau * 97.1), 0, k-1)
print(Sigma[i, j])
Output:
0.0
0.0
0.0...
I think I cannot properly write the limits of the sum in np.sum(). How can I correct this? How can I avoid 0 result?
EDIT:
I used sum():
for k in range(1, Nt):
for i in range(1, k):
for j in range(1, Nx-1):
#define sum
Sigma[i, j] = sum(u[i+1][j] - u[i][j])
print(Sigma[i, j])
Error:
TypeError: 'numpy.float64' object is not iterable
Thank you for any help!
Is this what you are looking for?
for k in range(1, Nt):
for i in range(1, k):
for j in range(1, Nx-1):
#define sum
Sigma[i, j] = sum(u[_+1][j] - u[_][j] for _ in range(0, k))
print(Sigma[i, j])
Although you could write Sum(u[_+1[j] - u[_], (_, 0, k)).doit(), the built-in sum is really what you are trying to do: an elementwise summation of literal values, not a summation of symbolic terms like Sum(1/x, (x, 0, 5)) -- there does not need to be an x array for SymPy to figure out that sum since the limits indicate what the values of x are going to be. In your case you have the values in an array already.
Reread the sympy docs. I think they specify that sum should be used with:
Sum(expr, (var, a, b)
(I probably shouldn't try to work from memory here, but you can check).
In your:
Sum( ((u[i+1][j] - u[i][j]) / tau * 97.1), (i, 0, k))
((u[i+1][j] - u[i][j]) / tau * 97.1) is a number, derived from numpy u. It isn't a sympy expression.
and i is a number, not sympy symbol. The error tells us that "Invalid limits given: ((1, 0, 2),)".
For someone who is new to Python, trying to use sympy will be difficult.
The problem with
np.sum( ((u[i+1][j] - u[i][j]) / tau * 97.1), 0, k-1)
is that np.sum does not take limits like the sympy Sum. Don't assume the documentation for one function applies to a similarly named one in another package. np.sum, if you read its docs, takes an array, with optional parameters like axis.
As for your last attempt:
sum(u[i+1][j] - u[i][j])
the python sum takes an "iterable", something like a list. u[i+1][j] - u[i][j] is a single number.
u is a 2d numpy array. u[i] is a 1d array, a "row";, u[i,j] is a single element of the array.
What exactly are you trying to sum?
I suspect you have some sort of mathematical summation in mind, and have tried to express that with sympy algebra. But your u is a 2d numpy array. So u[i+1,j]-u[i,j] is a single number, the difference between two elements. u[1:,j]-u[:-1,j] takes the difference between all such pairs of rows.
I haven't tried to figure out what your nested loops are doing, especially since i is a subset of possible rows.
edit
Let's simplify your example a bit - smaller dimensions, and removing the constants that don't change the behavior:
In [5]: Nx = 4
...: Nt = 3
...: u = np.ones((Nt, Nx))
...: Sigma = np.zeros((Nt, Nx))
...:
...: for k in range(1, Nt):
...: print('k',k)
...: for i in range(1, k):
...: print('i',i)
...: for j in range(1, Nx-1):
...: print('j',j)
...: Sigma[i,j] = u[i+1,j] - u[i,j]
...: print(Sigma)
...:
k 1
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
k 2
i 1
j 1
j 2
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
When k is 1, there's no i iteration since range(1,1) is empty. So Sigma is still the original 0s.
For k 2, i ranges(1,2), i.e. once. j iterates range(1,3), i.e. 1 and 2. But Sigma is still 0. u is all ones, so paired differences are 0. #smichr already pointed this out (I missed it on earlier reads).
In [3]: u
Out[3]:
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
In [4]: u[1:]-u[:-1]
Out[4]:
array([[0., 0., 0., 0.],
[0., 0., 0., 0.]])
I'm not sure it's worth pursuing this further. You need a realistic example where the u differences matter. But keep it small (like this (4,3), so you can actually specify what values you seek.
If I define a random u:
In [13]: u
Out[13]:
array([[14, 1, 1, 11],
[ 2, 4, 17, 4],
[11, 2, 6, 19]])
In [14]: u[1:]-u[:-1]
Out[14]:
array([[-12, 3, 16, -7],
[ 9, -2, -11, 15]])
For k 1 sigma is still 0, but for k 2:
k 2
i 1
j 1
j 2
[[ 0. 0. 0. 0.]
[ 0. -2. -11. 0.]
[ 0. 0. 0. 0.]]
The code set the Sigma[1,1] and Sigma[1,2] values from the difference array.
Here's a sample run for a case with more rows:
In [16]: Nt,Nx = 5,4
In [17]: u = np.random.randint(0,20,(Nt,Nx))
In [18]: u
Out[18]:
array([[18, 15, 17, 3],
[ 9, 2, 5, 16],
[11, 19, 5, 2],
[13, 0, 4, 5],
[ 8, 10, 10, 0]])
In [19]: u[1:]-u[:-1]
Out[19]:
array([[ -9, -13, -12, 13],
[ 2, 17, 0, -14],
[ 2, -19, -1, 3],
[ -5, 10, 6, -5]])
In [20]: Sigma = np.zeros((Nt, Nx))
...: for k in range(1, Nt):
...: print('k',k)
...: for i in range(1, k):
...: print('i',i)
...: for j in range(1, Nx-1):
...: print('j',j)
...: Sigma[i,j] = u[i+1,j] - u[i,j]
...: print(Sigma)
...:
k 1
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
k 2
i 1
j 1
j 2
[[ 0. 0. 0. 0.]
[ 0. 17. 0. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
k 3
i 1
j 1
j 2
i 2
j 1
j 2
[[ 0. 0. 0. 0.]
[ 0. 17. 0. 0.]
[ 0. -19. -1. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
k 4
i 1
j 1
j 2
i 2
j 1
j 2
i 3
j 1
j 2
[[ 0. 0. 0. 0.]
[ 0. 17. 0. 0.]
[ 0. -19. -1. 0.]
[ 0. 10. 6. 0.]
[ 0. 0. 0. 0.]]
Let's try a case where successive k values are added to the original, rather than simply overwritting.
In [21]: Sigma = np.zeros((Nt, Nx))
...: for k in range(1, Nt):
...: print('k',k)
...: for i in range(1, k):
...: print('i',i)
...: for j in range(1, Nx-1):
...: print('j',j)
...: Sigma[i,j] += u[i+1,j] - u[i,j] # <== change here
...: print(Sigma)
...:
k 1
[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
k 2
i 1
j 1
j 2
[[ 0. 0. 0. 0.]
[ 0. 17. 0. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
k 3
i 1
j 1
j 2
i 2
j 1
j 2
[[ 0. 0. 0. 0.]
[ 0. 34. 0. 0.]
[ 0. -19. -1. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
k 4
i 1
j 1
j 2
i 2
j 1
j 2
i 3
j 1
j 2
[[ 0. 0. 0. 0.]
[ 0. 51. 0. 0.] # 3*17
[ 0. -38. -2. 0.] # 2* (-19 -1)
[ 0. 10. 6. 0.]
[ 0. 0. 0. 0.]]
Not knowing what you are aiming at, I can't say whether that makes any more sense.

How to keep a matrix unchanged

I am trying to calculate the inverse matrix using the Gauss-Jordan Method. For that, I need to find the solution X to A.X = I (A and X being N x N matrices, and I the identity matrix).
However, for every column vector of the solution matrix X I calculate in the first loop, I have to use the original matrix A, but I don't know why it keeps changing when I did a copy of it in the beginning.
def SolveGaussJordanInvMatrix(A):
N = len(A[:,0])
I = np.identity(N)
X = np.zeros([N,N], float)
A_orig = A.copy()
for m in range(N):
x = np.zeros(N, float)
v = I[:,m]
A = A_orig
for p in range(N): # Gauss-Jordan Elimination
A[p,:] /= A[p,p]
v[p] /= A[p,p]
for i in range(p): # Cancel elements above the diagonal element
v[i] -= v[p] * A[i,p]
A[i,p:] -= A[p,p:]*A[i,p]
for i in range(p+1, N): # Cancel elements below the diagonal element
v[i] -= v[p] * A[i,p]
A[i,p:] -= A[p,p:]*A[i,p]
X[:,m] = v # Add column vector to the solution matrix
return X
A = np.array([[2, 1, 4, 1 ],
[3, 4, -1, -1],
[1, -4, 7, 5],
[2, -2, 1, 3]], float)
SolveGaussJordanInvMatrix(A)
Does anyone know how turn A back to its original form after the Gauss-Elimination loop?
I'm getting
array([[ 228.1, 0. , 0. , 0. ],
[-219.9, 1. , 0. , 0. ],
[ -14.5, 0. , 1. , 0. ],
[-176.3, 0. , 0. , 1. ]])
and expect
[[ 1.36842105 -0.89473684 -1.05263158 1. ]
[-1.42105263 1.23684211 1.13157895 -1. ]
[ 0.42105263 -0.23684211 -0.13157895 -0. ]
[-2. 1.5 1.5 -1. ]]

How to call the number in a matrix

here is my matrix setup:
for a in b:
size_x = len(a) + 1
size_y = len(b) + 1
matrix = np.zeros ((size_x, size_y))
for x in range(size_x):
matrix [x, 0] = x
for y in range(size_y):
matrix [0, y] = y
for x in range(1, size_x):
for y in range(1, size_y):
if a[x-1] == b[y-1]:
matrix [x,y] = min(
matrix[x-1, y] + 1,
matrix[x-1, y-1],
matrix[x, y-1] + 1
)
else:
matrix [x,y] = min(
matrix[x-1,y] + 1,
matrix[x-1,y-1] + 1,
matrix[x,y-1] + 1
)
print(matrix)
This would give outputs such as
t e s t
[[ 0. 1. 2. 3. 4.]
t [ 1. 0. 1. 2. 3.]
e [ 2. 1. 0. 1. 2.]
x [ 3. 2. 1. 1. 2.]
t [ 4. 3. 2. 1. 1.]]
In which the bottom right-hand corner is the final value. How do I take this out and add it to a list?
You can access the ith index of an array arr by using this expression: arr[i] .
In order to answer your question -> accessing the bottom-right value of a 2d matrix simply use,
matrix[matrix.length-1][matrix[matrix.length-1].length-1]
or better
lastRowIndex = matrix.length-1;
lastColIndex = matrix[lastRowIndex].length-1;
bottomRightValue = matrix[lastRowIndex][lastColIndex]

How to nullify all entries except for argmax?

Assuming I have a matrix / array / list like a=[1,2,3,4,5] and I want to nullify all entries except for the max so it would be a=[0,0,0,0,5].
I'm using b = [val if idx == np.argmax(a) else 0 for idx,val in enumerate(a)] but is there a better (and faster) way (especially for more than 1-dim arrays...)
You can use numpy for an in-place solution. Note that the below method will make all matches for the max value equal to 0.
import numpy as np
a = np.array([1,2,3,4,5])
a[np.where(a != a.max())] = 0
# array([0, 0, 0, 0, 5])
For unique maxima, see #cᴏʟᴅsᴘᴇᴇᴅ's solution.
Rather than masking, you can create an array of zeros and set the right index appropriately?
1-D (optimised) Solution
(Setup) Convert a to a 1D array: a = np.array([1,2,3,4,5]).
To replace just one instance of the max
b = np.zeros_like(a)
i = np.argmax(a)
b[i] = a[i]
To replace all instances of the max
b = np.zeros_like(a)
m = a == a.max()
b[m] = a[m]
N-D solution
np.random.seed(0)
a = np.random.randn(5, 5)
b = np.zeros_like(a)
m = a == a.max(1, keepdims=True)
b[m] = a[m]
b
array([[0. , 0. , 0. , 2.2408932 , 0. ],
[0. , 0.95008842, 0. , 0. , 0. ],
[0. , 1.45427351, 0. , 0. , 0. ],
[0. , 1.49407907, 0. , 0. , 0. ],
[0. , 0. , 0. , 0. , 2.26975462]])
Works for all instances of max per row.

Constructing a matrix based on different lists with Python

I am going to make the following matrix:
s= [[s11 s12 s13]
[s21 s22 s23]
[s31 s32 s33]]
where I can obtain each array of the matrix s by:
sii = a(i) ; for s11, s22, and s33
sij = a(i)**2 + 10 ; for s12=s21, s23=s32, and s13=s31
here, ai is a list of data:
a = [0.1, 0.25, 0.12]
So when I use the following:
import numpy as np
s = np.ones([3,3])
def matrix(s):
a = [0.1, 0.25, 0.12]
s[np.diag_indices_from(s)] = ai
s[~np.eye(s.shape[0],dtype=bool)] = ai**2 + 10
It gives me an error. How can I solve this problem? Thanks.
Here is a hint for you on how to manipulate the diagonal and non-diagonal values.
import numpy as np
s = np.ones([3,3])
def matrix(s):
a = [1,2,3]
for i in range(len(a)):
s[i,i] = a[i] # sii = a(i)
rc = (i + 1) % len(a)
val = a[i] ** 2 + 10
s[i, rc] = val # sij = a(i)**2 + 10
s[rc, i] = val # sij = a(i)**2 + 10
return s
print(matrix(s))
input:
[[ 1. 1. 1.]
[ 1. 1. 1.]
[ 1. 1. 1.]]
output:
[[ 1. 11. 19.]
[ 11. 2. 14.]
[ 19. 14. 3.]]

Categories

Resources