ktongue's picture
download
raw
15 kB
{
"metadata": {
"name": "Matrices"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Matrices"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"import sys\n",
"import os\n",
"sys.path.insert(1, os.path.join(os.path.pardir, \"ipython_doctester\"))\n",
"from sympy import *\n",
"from ipython_doctester import test\n",
"# Work around a bug in IPython. This will disable the ability to paste things with >>>\n",
"def notransform(line): return line\n",
"from IPython.core import inputsplitter\n",
"inputsplitter.transform_classic_prompt = notransform"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Use `row_del` and `row_insert` to go from one Matrix to the other."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"@test\n",
"def matrix1(M):\n",
" \"\"\"\n",
" >>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
" >>> M\n",
" [1, 2, 3]\n",
" [4, 5, 6]\n",
" [7, 8, 9]\n",
" >>> matrix1(M)\n",
" [4, 5, 6]\n",
" [0, 0, 0]\n",
" [7, 8, 9]\n",
" \"\"\"\n"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Matrix Constructors"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Use the matrix constructors to construct the following matrices. There may be more than one correct answer."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\\left[\\begin{array}{ccc}4 & 0 & 0\\\\\\\\ \n",
"0 & 4 & 0\\\\\\\\ \n",
"0 & 0 & 4\\end{array}\\right]$$"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"@test\n",
"def matrix2():\n",
" \"\"\"\n",
" >>> matrix2()\n",
" [4, 0, 0]\n",
" [0, 4, 0]\n",
" [0, 0, 4]\n",
" \"\"\"\n"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\\left[\\begin{array}{}1 & 1 & 1 & 0\\\\\\\\1 & 1 & 1 & 0\\\\\\\\0 & 0 & 0 & 1\\end{array}\\right]$$"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"@test\n",
"def matrix3():\n",
" \"\"\"\n",
" >>> matrix3()\n",
" [1, 1, 1, 0]\n",
" [1, 1, 1, 0]\n",
" [0, 0, 0, 1]\n",
" \"\"\"\n"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"$$\\left[\\begin{array}{}-1 & -1 & -1 & 0 & 0 & 0\\\\\\\\-1 & -1 & -1 & 0 & 0 & 0\\\\\\\\-1 & -1 & -1 & 0 & 0 & 0\\\\\\\\0 & 0 & 0 & 0 & 0 & 0\\\\\\\\0 & 0 & 0 & 0 & 0 & 0\\\\\\\\0 & 0 & 0 & 0 & 0 & 0\\end{array}\\right]$$"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"@test\n",
"def matrix4():\n",
" \"\"\"\n",
" >>> matrix4()\n",
" [-1, -1, -1, 0, 0, 0]\n",
" [-1, -1, -1, 0, 0, 0]\n",
" [-1, -1, -1, 0, 0, 0]\n",
" [ 0, 0, 0, 0, 0, 0]\n",
" [ 0, 0, 0, 0, 0, 0]\n",
" [ 0, 0, 0, 0, 0, 0]\n",
" \"\"\"\n"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Recall that if $f$ is an analytic function, then we can define $f(M)$ for any square matrix $M$ by \"plugging\" $M$ into the power series formula for $f(x)$. In other words, if $$f(x) = \\sum_{n=0}^\\infty a_n x^n,$$ then we define $f(M)$ by $$f(M) = \\sum_{n=0}^\\infty a_n M^n,$$ where $M^0$ is $I$, the identity matrix. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Furthermore, if $M$ is a diagonalizable matrix, that is, $M=PDP^{-1}$, where $D$ is diagonal, then $M^n = PD^nP^{-1}$ (because $M^n = \\left(PDP^{-1}\\right)\\left(PDP^{-1}\\right)\\cdots\\left(PDP^{-1}\\right)=PD\\left(P^{-1}P\\right)D\\left(P^{-1}P\\right)\\cdots DP^{-1} = PD^nP^{-1}$).\n",
"\n",
"But if \n",
"\n",
"$$ D = \\begin{bmatrix}\n",
" d_1 & 0 & \\cdots & 0 \\\\\\\\\n",
" 0 & d_2 & \\cdots & 0 \\\\\\\\\n",
" \\vdots & \\vdots & \\ddots & \\vdots \\\\\\\\\n",
" 0 & 0 & \\cdots & d_n\n",
" \\end{bmatrix}\n",
"$$\n",
"\n",
"is a diagonal matrix, then \n",
"\n",
"$$ D^n = \\begin{bmatrix}\n",
" d_1^n & 0 & \\cdots & 0 \\\\\\\\\n",
" 0 & d_2^n & \\cdots & 0 \\\\\\\\\n",
" \\vdots & \\vdots & \\ddots & \\vdots \\\\\\\\\n",
" 0 & 0 & \\cdots & d_n^n\n",
" \\end{bmatrix}\n",
"$$\n",
"\n",
"so that \n",
"\n",
"$$\n",
"\\sum_{n=0}^\\infty a_n M^n = \\sum_{n=0}^\\infty a_n PD^nP^{-1} = P\\cdot\\begin{bmatrix}\n",
" \\sum_{n=0}^\\infty a_n d_1^n & 0 & \\cdots & 0 \\\\\\\\\n",
" 0 & \\sum_{n=0}^\\infty a_n d_2^n & \\cdots & 0 \\\\\\\\\n",
" \\vdots & \\vdots & \\ddots & \\vdots \\\\\\\\\n",
" 0 & 0 & \\cdots & \\sum_{n=0}^\\infty a_n d_n^n\n",
"\\end{bmatrix}\\cdot P^{-1} = P\\cdot\\begin{bmatrix}\n",
" f(d_1) & 0 & \\cdots & 0 \\\\\\\\\n",
" 0 & f(d_2) & \\cdots & 0 \\\\\\\\\n",
" \\vdots & \\vdots & \\ddots & \\vdots \\\\\\\\\n",
" 0 & 0 & \\cdots & f(d_n)\n",
"\\end{bmatrix}\\cdot P^{-1}\n",
"$$"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's create some square matrices, which we will use throughout the exercises."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"x = symbols('x')\n",
"A = Matrix([[1, 1], [1, 0]])\n",
"M = Matrix([[3, 10, -30], [0, 3, 0], [0, 2, -3]])\n",
"N = Matrix([[-1, -2, 0, 2], [-1, -1, 2, 1], [0, 0, 2, 0], [-1, -2, 2, 2]])"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First, verify that these matrices are indeed diagonalizable."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"print A.is_diagonalizable()\n",
"print M.is_diagonalizable()\n",
"print N.is_diagonalizable()"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, we want to write a function that computes $f(M)$, for diagonalizable matrix $M$ and analytic function $f$.\n",
"\n",
"However, there is one complication. We can use `diagonalize` to get `P` and `D`, but we need to apply the function to the diagonal of `D`. We might think that we could use `eigenvals` to get the eigenvalues of the matrix, since the diagonal values of `D` are just the eigenvalues of `M`, but the issue is that they could be in any order in `D`.\n",
"\n",
"Instead, we can use matrix slicing to get the diagonal values (or indeed, any value) of a matrix. There is not enough time in this tutorial (or room in this document) to discuss the full details of matrix slicing. For now, we just note that `M[i, j]` returns the element at position `i, j` (which is the `i + 1, j + 1`th element of the matrix, due to Python's 0-indexing). For example"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"M"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"M[0, 1]"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"That should be enough information to write the following function."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"@test\n",
"def matrix_func(M, func):\n",
" \"\"\"\n",
" Computes M at func. Assumes that M is square diagonalizable.\n",
"\n",
" >>> matrix_func(M, exp)\n",
" [exp(3), -5*exp(-3)/3 + 5*exp(3)/3, -5*exp(3) + 5*exp(-3)]\n",
" [ 0, exp(3), 0]\n",
" [ 0, -exp(-3)/3 + exp(3)/3, exp(-3)]\n",
"\n",
" Note that for the function exp, we can also just use M.exp()\n",
"\n",
" >>> matrix_func(M, exp) == M.exp()\n",
" True\n",
"\n",
" But for other functions, we have to do it this way.\n",
"\n",
" >>> M.sin()\n",
" Traceback (most recent call last):\n",
" ...\n",
" AttributeError: Matrix has no attribute sin.\n",
" >>> matrix_func(N, sin)\n",
" [-sin(1), -2*sin(1), 0, 2*sin(1)]\n",
" [-sin(1), -sin(1), sin(2), sin(1)]\n",
" [ 0, 0, sin(2), 0]\n",
" [-sin(1), -2*sin(1), sin(2), 2*sin(1)]\n",
" \n",
" Note that we could also use this to compute the series expansion of a matrix, \n",
" if we know the closed form of that expansion. For example, suppose we wanted to compute\n",
" \n",
" I + M + M**2 + M**3 + \u2026\n",
"\n",
" The series\n",
"\n",
" 1 + x + x**2 + x**3 + \u2026\n",
"\n",
" is equal to the function 1/(1 - x).\n",
"\n",
" >>> matrix_func(M, Lambda(x, 1/(1 - x))) # Note, Lambda works just like lambda, but is symbolic\n",
" [-1/2, -5/4, 15/4]\n",
" [ 0, -1/2, 0]\n",
" [ 0, -1/4, 1/4]\n",
" \"\"\"\n"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now lets investigate how this works in relation to the series expansion definition. Write a function that uses `matrix_func` and `series` to compute the approximation of a matrix evaluated at a function up to $O(M^n)$."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"@test\n",
"def matrix_func_series(M, func, n):\n",
" \"\"\"\n",
" Computes the approximation of the func(M) using the series definition up to O(M**n).\n",
"\n",
" >>> matrix_func_series(M, exp, 10)\n",
" [22471/1120, 14953/448, -44859/448]\n",
" [ 0, 22471/1120, 0]\n",
" [ 0, 14953/2240, 83/2240]\n",
" >>> matrix_func_series(M, exp, 10).evalf()\n",
" [20.0633928571429, 33.3772321428571, -100.131696428571]\n",
" [ 0, 20.0633928571429, 0]\n",
" [ 0, 6.67544642857143, 0.0370535714285714]\n",
" >>> matrix_func(M, exp).evalf()\n",
" [20.0855369231877, 33.3929164246997, -100.178749274099]\n",
" [ 0, 20.0855369231877, 0]\n",
" [ 0, 6.67858328493993, 0.0497870683678639]\n",
"\n",
" It's pretty close. Basically what we might expect for those values up to O(x**10).\n",
"\n",
" >>> matrix_func_series(N, sin, 3)\n",
" [-1, -2, 0, 2]\n",
" [-1, -1, 2, 1]\n",
" [ 0, 0, 2, 0]\n",
" [-1, -2, 2, 2]\n",
" >>> matrix_func(N, sin).evalf()\n",
" [-0.841470984807897, -1.68294196961579, 0, 1.68294196961579]\n",
" [-0.841470984807897, -0.841470984807897, 0.909297426825682, 0.841470984807897]\n",
" [ 0, 0, 0.909297426825682, 0]\n",
" [-0.841470984807897, -1.68294196961579, 0.909297426825682, 1.68294196961579]\n",
"\n",
" It's not as close, because we used O(x**3), but clearly still the same thing.\n",
"\n",
" >>> matrix_func_series(M, Lambda(x, 1/(1 - x)), 10)\n",
" [29524, 73810, -221430]\n",
" [ 0, 29524, 0]\n",
" [ 0, 14762, -14762]\n",
" >>> matrix_func(M, Lambda(x, 1/(1 - x)))\n",
" [-1/2, -5/4, 15/4]\n",
" [ 0, -1/2, 0]\n",
" [ 0, -1/4, 1/4]\n",
"\n",
" Woah! That one's not close at all. What is happening here? Let's try more terms\n",
"\n",
" >>> matrix_func_series(M, Lambda(x, 1/(1 - x)), 100)\n",
" [257688760366005665518230564882810636351053761000, 644221900915014163795576412207026590877634402500, -1932665702745042491386729236621079772632903207500]\n",
" [ 0, 257688760366005665518230564882810636351053761000, 0]\n",
" [ 0, 128844380183002832759115282441405318175526880500, -128844380183002832759115282441405318175526880500]\n",
" \n",
" It just keeps getting bigger. In fact, the series diverges. Recall that \n",
" 1/(1 - x) = 1 + x + x**2 + x**3 + \u2026 *only if* |x| < 1. But the eigenvalues \n",
" of M are bigger than 1 in absolute value.\n",
" \n",
" >>> M.eigenvals()\n",
" {3: 2, -3: 1}\n",
"\n",
" In fact, 1/(1 - M) is mathematically defined via the analytic continuation \n",
" of the series expansion 1 + x + x**2 + \u2026, which is just 1/(1 - x). This is\n",
" well-defined as long as none of the eigenvalues of M are equal to 1. Let's\n",
" try it on N.\n",
"\n",
" >>> matrix_func(N, Lambda(x, 1/(1 - x)))\n",
" [nan, -oo, nan, oo]\n",
" [nan, nan, nan, nan]\n",
" [nan, nan, nan, nan]\n",
" [nan, -oo, nan, oo]\n",
" \n",
" That didn't work. What are the eigenvalues of N?\n",
"\n",
" >>> N.eigenvals()\n",
" {1: 1, 2: 1, -1: 1, 0: 1}\n",
"\n",
" Ah, the first one is 1, so we cannot define 1/(1 - N). \n",
" \"\"\"\n"
],
"language": "python",
"metadata": {},
"outputs": []
},
{
"cell_type": "code",
"collapsed": false,
"input": [],
"language": "python",
"metadata": {},
"outputs": []
}
],
"metadata": {}
}
]
}

Xet Storage Details

Size:
15 kB
·
Xet hash:
b33d05026aa6d23fe25630a574b34f9dd8a10b531b9b1e3d8d6f666006743904

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.