{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "0b5a661b", "metadata": {}, "outputs": [], "source": [ "using LinearAlgebra" ] }, { "cell_type": "code", "execution_count": 2, "id": "d0c55435", "metadata": {}, "outputs": [], "source": [ "using PyPlot" ] }, { "cell_type": "markdown", "id": "4c7e6acf", "metadata": {}, "source": [ "## Linear Algebra\n", "\n", "One of the most important problems (and most studied problems) is that of solving $Ax = b$. It's actually surprisingly difficult for various reasons (speed, cutoff error, stability, etc...)\n", "\n", "Let's look at one such problem with stability:" ] }, { "cell_type": "code", "execution_count": 3, "id": "cf428b8e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 Matrix{Float64}:\n", " 2.0 0.9\n", " 1.2 0.54" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = [ [2.0, 1.2] [0.9, 0.54+5e-10] ]" ] }, { "cell_type": "code", "execution_count": 4, "id": "a0d33246", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 Matrix{Float64}:\n", " 5.4e8 -9.0e8\n", " -1.2e9 2.0e9" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "inv(A)" ] }, { "cell_type": "code", "execution_count": 5, "id": "5ff0b108", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element Vector{Float64}:\n", " 100.1\n", " 200.2" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = [ 100.1, 200.2 ]" ] }, { "cell_type": "code", "execution_count": 6, "id": "def792a1", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element Vector{Float64}:\n", " -1.261259895142388e11\n", " 2.8027997680953076e11" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = inv(A)*b" ] }, { "cell_type": "code", "execution_count": 7, "id": "3a797e1d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.00012207031251136867" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "norm( A * x - b )" ] }, { "cell_type": "markdown", "id": "31c05161", "metadata": {}, "source": [ "So the norm is clearly not zero, so this did not solve $Ax=b$ properly... What happened?\n", "\n", "The answer is that the above matrix is extremely close to being singular:" ] }, { "cell_type": "code", "execution_count": 8, "id": "44dd7f72", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.000000082740371e-9" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "det(A)" ] }, { "cell_type": "markdown", "id": "3b463ff8", "metadata": {}, "source": [ "This is one of the key points of numerical algorithms - stability. We are not going to go into a lot of detail here at all, but the closer your matrix is to being singular, the worse numerically it will perform. This is __entirely__ due to numerical cutoff, algebraically everything is still exact.\n", "\n", "How can we quantify how bad? That is how close to being singular? This is the concept of a condition number:" ] }, { "cell_type": "code", "execution_count": 9, "id": "d1c38906", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.541601390762973e9" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "cond(A)" ] }, { "cell_type": "markdown", "id": "a5968a6e", "metadata": {}, "source": [ "The larger the condition number the closer to being singular. The optimal is 1, the identity matrix. There are ways to get around inverting this that are more stable:" ] }, { "cell_type": "code", "execution_count": 10, "id": "8b501542", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2-element Vector{Float64}:\n", " -1.2612598951423882e11\n", " 2.802799768095307e11" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x2 = A \\ b" ] }, { "cell_type": "code", "execution_count": 11, "id": "43628c77", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.364787585194269e-5" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "norm( A*x2 - b )" ] }, { "cell_type": "markdown", "id": "f0cba39b", "metadata": {}, "source": [ "This is better but still not amazing - there are fancier methods to do this (outside scope of this course) such as preconditioning etc.\n", "\n", "Moral of the story is ill-conditioned matrices lead to bad numerical answers and that some algorithms fare better in bad cases than others." ] }, { "cell_type": "markdown", "id": "5a018d41", "metadata": {}, "source": [ "## Polynomial Interpolation\n", "\n", "This is related to your hw problem but will need to be modified. The problem is this: given $n$ pairs of x,y coordinates, can we interpolate a polynomial through these points? (Hint: the answer is yes)\n", "\n", "This is actually a linear algebra problem.\n", "\n", "Say we have $n=1$ points. Then hopefully should be easy to see the interpolating polynomial here is the constant one through that point.\n", "\n", "Say we have 3 points (x1,y1), (x2,y2), (x3,y3). Then we can interpolate a polynomial through this of the form \n", "$p(x) = ax^2 + bx + c$. Why? 3 degrees of freedom.\n", "\n", "Thus we need to satisfy:\n", "$$ p(x_1) = y_1 $$\n", "$$ p(x_2) = y_2 $$\n", "$$ p(x_3) = y_3 $$\n", "\n", "This is same as the following linear system:" ] }, { "cell_type": "code", "execution_count": 12, "id": "22d6f413", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0.6640692954012222, 0.7365261153556082, 0.8843757675554409]\n", "[0.27962598475420775, 0.39883581471865637, 0.5611293551730343]\n" ] } ], "source": [ "x = rand(3); y = rand(3);\n", "println(x)\n", "println(y)" ] }, { "cell_type": "code", "execution_count": 13, "id": "e26f6749", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 Matrix{Float64}:\n", " 1.0 0.664069 0.440988\n", " 1.0 0.736526 0.542471\n", " 1.0 0.884376 0.78212" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "A = [ [1,1,1] [x[1], x[2], x[3]] [x[1]^2, x[2]^2, x[3]^2] ]" ] }, { "cell_type": "code", "execution_count": 14, "id": "b259cc92", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Vector{Float64}:\n", " -2.0285798858832567\n", " 5.126360756738726\n", " -2.4854482043710666" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "coeffs = A \\ y" ] }, { "cell_type": "code", "execution_count": 15, "id": "33894885", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "p (generic function with 1 method)" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "p(x) = coeffs[1] + coeffs[2]*x + coeffs[3]*x^2" ] }, { "cell_type": "code", "execution_count": 16, "id": "cd088cdf", "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "Figure(PyObject
)" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "1-element Vector{PyCall.PyObject}:\n", " PyObject " ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "xpts = LinRange(0,1,20)\n", "ypts = [p(x) for x in xpts]\n", "plot( xpts, ypts )\n", "plot( x[1], y[1], \"ko\")\n", "plot( x[2], y[2], \"ko\")\n", "plot( x[3], y[3], \"ko\")" ] }, { "cell_type": "markdown", "id": "0e634e5a", "metadata": {}, "source": [ "The matrix $A$ is called a Vandermonde matrix. It turns out that they can be very ill-conditioned in practice but there are fancy ways to deal with this." ] }, { "cell_type": "markdown", "id": "cccdeff8", "metadata": {}, "source": [ "## String Processing\n", "\n", "Strings are just words basically:" ] }, { "cell_type": "code", "execution_count": 17, "id": "6219fc7d", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello, I am a string\n" ] } ], "source": [ "println( \"hello, I am a string\" )" ] }, { "cell_type": "markdown", "id": "ec8b401d", "metadata": {}, "source": [ "Special characters are indicated with a backslash, for example `\\n` is string for newline, `\\t` for tab" ] }, { "cell_type": "code", "execution_count": 18, "id": "bec5f7cf", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "hello \n", " I am a string\n" ] } ], "source": [ "println( \"hello \\n I am a string\" )" ] }, { "cell_type": "markdown", "id": "6e788541", "metadata": {}, "source": [ "To put two strings together" ] }, { "cell_type": "code", "execution_count": 19, "id": "73085cfa", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"Hi I am a string\"" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "string( \"Hi\", \" I am a string\")" ] }, { "cell_type": "markdown", "id": "8ef84190", "metadata": {}, "source": [ "To add in other types as arguments to strings, use the `$` operator" ] }, { "cell_type": "code", "execution_count": 20, "id": "efa907c5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Vandermonde A: [1.0 0.6640692954012222 0.44098802909467566; 1.0 0.7365261153556082 0.5424707186008226; 1.0 0.8843757675554409 0.7821204982392752]\n" ] } ], "source": [ "println( \"Vandermonde A: $A\" )" ] }, { "cell_type": "markdown", "id": "e3371527", "metadata": {}, "source": [ "Strings are honestly just arrays of characters, so can index into them as such" ] }, { "cell_type": "code", "execution_count": 21, "id": "0f1310d8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'n': ASCII/Unicode U+006E (category Ll: Letter, lowercase)" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"Vandermonde\"[3]" ] }, { "cell_type": "markdown", "id": "77d32717", "metadata": {}, "source": [ "To convert string input into other types, use the `parse` command" ] }, { "cell_type": "code", "execution_count": 22, "id": "8127b4e3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1234.0" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "parse( Float64, \"1234\")" ] }, { "cell_type": "markdown", "id": "f1df7e23", "metadata": {}, "source": [ "This is really helpful when reading in data, as __ALL__ data read in from any file is always in string format. To read in a file/writing from a file, you need to open the file, then you can read it in line by line" ] }, { "cell_type": "markdown", "id": "9d0ed00a", "metadata": {}, "source": [ "`\n", "f = open(\"filename.ext\")\n", "str = readline(f)\n", "`" ] }, { "cell_type": "markdown", "id": "79ee7f47", "metadata": {}, "source": [ "Obviously the filename needs to be correct. To write similarly" ] }, { "cell_type": "markdown", "id": "a1efd114", "metadata": {}, "source": [ "`\n", "write(f, \"some string\")\n", "`" ] }, { "cell_type": "markdown", "id": "860ff19c", "metadata": {}, "source": [ "Lastly you can also search for patterns in strings:" ] }, { "cell_type": "code", "execution_count": 23, "id": "211a97d6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8:10" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "str = \"Hello, World! These are my words.\"\n", "pattern = \"wor\"\n", "idx1 = findfirst(pattern, lowercase(str))" ] }, { "cell_type": "markdown", "id": "bdea2329", "metadata": {}, "source": [ "Which will give you the position of the desired pattern. One useful tool for this is regex (regular expressions)" ] }, { "cell_type": "code", "execution_count": 24, "id": "37c7f923", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10:19" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "str1 = \"birthday=01/01/2000\"\n", "pattern = r\"\\d{2}/\\d{2}/\\d{4}\"\n", "idx1 = findfirst(pattern, str1)" ] }, { "cell_type": "markdown", "id": "ec85b73f", "metadata": {}, "source": [ "You can read more about this yourself online or come ask me in OH, but the way it works for the above is `\\d` is any digit 0-9, {2} means it appears twice. So we are looking for something of the format dd/mm/yyyy. It will then return the position of anything that matches this pattern. Here is a list of regex commands (common ones)" ] }, { "cell_type": "markdown", "id": "4794636a", "metadata": {}, "source": [ "\\d is digits = [0-9]\n", "\n", "\\w is alphabetical letter = [a-z]\n", "\n", "capital letters [A-Z]\n", "\n", "any letter [A-Za-z]\n", "\n", "how many times it appear, put {number} afterwards\n", "\n", "\\s is whitespace" ] }, { "cell_type": "code", "execution_count": 25, "id": "5334c0db", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"asdasdnq192371jajs12\"" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "randstring = \"asdasdnq192371jajs12\"" ] }, { "cell_type": "code", "execution_count": 26, "id": "cfbe0393", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "r\"\\w\\d{3}\"" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pattern4 = r\"\\w\\d{3}\"" ] }, { "cell_type": "code", "execution_count": 27, "id": "e6da744b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1-element Vector{UnitRange{Int64}}:\n", " 8:11" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "findall( pattern4, randstring )" ] }, { "cell_type": "code", "execution_count": 28, "id": "8049a9e6", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"q192\"" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "randstring[ findfirst( pattern4, randstring ) ]" ] }, { "cell_type": "code", "execution_count": null, "id": "88b84100", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Julia 1.6.5", "language": "julia", "name": "julia-1.6" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.6.5" } }, "nbformat": 4, "nbformat_minor": 5 }