"Permutation matrices have the useful property that they are their own inverse, so we can write\n",

"Permutation matrices have the useful property that their inverse is their transpose, and beyond that, permutations involving a single swap are their own inverse (e.g. a permutation that swaps row 1 and 2 and keeps row 3 the same, will be inverted by swapping rows 1 and 2 again).\n",

" s2 = sum(U[k, j] * L[i, k] for k in range(j))\n",

" L[i, j] = (PM[i, j] - s2) / U[j, j]\n",

"\n",

" # Initialize U as a copy of the permuted matrix, and\n",

" # ensure it's storing floats rather than integers as\n",

" # this can lead to some unexpected results otherwise.\n",

" U = 1.0 * PM.copy()\n",

" \n",

" for k in range(n-1):\n",

" for j in range(k+1, n):\n",

" L[j, k] = U[j, k] / U[k, k]\n",

" U[j, k:] = U[j, k:] - L[j, k] * U[k, k:]\n",

" return (P, L, U)"

]

},

{

"cell_type": "markdown",

"metadata": {},

"source": [

"If you're still somewhat confused by how the code here corresponds to what we did previously, you should be able to find more detailed descriptions in a textbook on numerical linear algebra. For example, the chapter at <http://www.math.iit.edu/~fass/477577_Chapter_7.pdf> covers this nicely."

"If you examine our Python implementation above, you'll see we have a triple nested loop. This means we likely have $O(n^3)$ scaling. Let's use `%timeit -o` to generate output we can save and try to see this ourselves. This generates a \"TimeitResult\" object which has few methods available for examining the output. We'll save the average of each run (these are in seconds)."

"If you examine our Python implementation above, you'll see we have a triple nested loop (two explicit and one implicit). This means we likely have $O(n^3)$ scaling. Let's use `%timeit -o` to generate output we can save and try to see this ourselves. This generates a \"TimeitResult\" object which has few methods available for examining the output. We'll save the average of each run (these are in seconds)."

"For example, any problem with a 1D system where we approximate some piece of it as only interacting with its left and right neighbours can usually be cast as the solution of a tridiagonal matrix problem. (And by extension if we also included interactions with next-nearest neighbours it would be a banded matrix with $k_l = k_u = 2$.\n",

"For example, any problem with a 1D system where we approximate some piece of it as only interacting with its left and right neighbours can usually be cast as the solution of a tridiagonal matrix problem. (And by extension if we also included interactions with next-nearest neighbours it would be a banded matrix with $k_l = k_u = 2$.)\n",

"\n",

"For these types of matrices there are better approaches we can use than doing e.g. a full LU factorisation using Gaussian elimination. If the matrix is diagonal, we can obtain the solutions directly. The usual method used for tridiagonal matrices is known as the Thomas algorithm, which is composed of a forward sweep which converts the matrix to upper triangular in a single pass, and is followed by backward substitution sweep to produce the result.\n",