using SymPy
# Create an n x n (default 3), i,j elimination matrix
function E(i,j,n=3)
E = sympy"eye"(n) # start with the identity
E[i,j] = - symbols("m$i$j") # insert the negative multiplier
E
end
E (generic function with 2 methods)
# Create a symbolic 3x3 A matrix
A = [symbols("A$i$j") for i=1:3, j=1:3]
E(2,1)
using Interact
@manipulate for i=1:3, j=1:3
E(i,j)
end
E(2,1)
inv(E(2,1))
subtract m21 times the first row from the second row
E(2,1) * A
do the row operation twice
E(2,1)^2 * A
subtract m32 times the second row from the third row
E(3,2) * A
Note that after computing E(3,2) * A, the first row is untouched so we can apply E(2,1) without any interference from row 2
E(2,1) * E(3,2) * A
However, in the below, row 2 has changed
E(2,1) * A
so if apply E(3,2)
(meaning: subtract m32 times the second row from the third row)
it will happen with the UPDATED row 2
E(3,2) * E(2,1) * A
The row interpretation of matrix muliply correctly acounts for m32 times the second row and m21 x m32 times the first row -- the combined effect
E(3,2) * E(2,1)
E(2,1) * E(3,2)
Let's move to 5x5 matrices
E(2,1,5)
E1 = E(2,1,5) * E(3,1,5) * E(4,1,5)* E(5,1,5)
E(3,1,5) * E(2,1,5) * E(5,1,5)* E(4,1,5) # Why does the order not matter
E1 # subtracts multiples of the first row
inv(E1) # INVERSE means add back the same multiples of the first row
E(3,2,5)
inv(E(3,2,5))
E1 * E(3,2,5)
inv(E1) * inv(E(3,2,5))
E1 * E(3,2,5) # Why does this have a simple looking answer?
inv(E1) * inv(E(3,2,5)) # Why does this have an even slightly simpler looking answer?
E(3,2,5) * E1 # Why doesn't this have a simple looking answer?
E2 = prod( E(i,2,5) for i=3:5)
E3 = prod( E(i,3,5) for i=4:5)
E4 = E(5,4,5)
E1 * E2 * E3 * E4 # Why is this simple?
L = inv(E1) * inv(E2) * inv(E3) * inv(E4) # Why is this simple? This is the L Matrix!!
E4 * E3 * E2 * E1 # Why is this a mess? Good thing we don't need it in Gaussian Elimination
inv(L) # Right this is the inv(L) , not how the inverse of a triangular matrix isn't so pretty in general
L
Notice that the exact answer gets messy:
b(i) = symbols("b$i")
b (generic function with 1 method)
[b(i) for i=1:5]
y = L\[b(i) for i=1:5]
but if you compute the answer the obvious way, it is really straightforward.
y[1] = b(1)
L[2,1]*y[1] + y[2] = b(2) implies
y[2] = b(2) - L[2,1]*y[1]
L[3,1]y[1] + L[3,2]y[2] + y[3] = b(3) implies
y[3] = b(3) - L[3,1]*y[1] - L[3,2]*y[2]
Putting this all together
y = [b(i) for i=1:5]
for i = 1:5, j=1:i-1
y[i] -= L[i,j]*y[j]
end
y
For upper triangular matrices with arbitrary diagonal there is an analagous algorithm known as "back substitution." Since the diagonal is not 1, there is one more divide by the diagonal in the obvious alagorithm. In the solution to Ax=b, the pivots are on the diagonal and we divide by those.