{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Playing with matrices\n", "(and if you get the subliminal message about abstractions, we'll be thrilled!)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia is a dynamic language. You don't need type declarations, and can change variable types dynamically and interactively.\n", "\n", "For working with simple numbers, arrays, and strings, its syntax is *superficially* similar to Matlab, Python, and other popular languages.\n", "\n", "In order to execute the `In` cells, select the cell and press `Shift-Enter`, or press the `Play` button above. To run the entire notebook, navigate to the `Cell` menu and then `Run All`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# We tell beginners, you don't need types!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "typeof(1.0)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "typeof(1)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "S = \"Hello Julia Class\"\n", "typeof(S)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Exercise fix my spelling in the cell above\n", "S" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Now forget all that (for now): Julia is not so different from your favorite dynamic language" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "1 + 1 # shift + enter to run" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = rand(5, 5)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using LinearAlgebra # A LinearAlgebra standard package contains structured matrices\n", "A = SymTridiagonal(rand(6), rand(5))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "b = rand(6)\n", "A \\ b" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = fill(3.15, 5, 5) # Fill a 5x5 array with 3.15's" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Let's create some addition tables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First let's use a nested for loop to fill in a matrix that's initially zero:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = zeros(5, 5)\n", "\n", "for i in 1:5\n", " for j in 1:5\n", " A[i, j] = i+j # Square brackets for indices. Also: indices start at 1, not 0.\n", " end\n", "end\n", "\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can abbreviate this using a double `for` loop:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for i in 1:5, j in 1:5\n", " A[i, j] = i+j # Square brackets for indices. Also: indices start at 1, not 0.\n", "end\n", "\n", "A" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The Julia way would be to use a so-called **array comprehension**:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[i+j for i in 1:5, j in 1:5]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Equivalently,\n", "[i+j for i = 1:5, j = 1:5]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Explore**: What does the following do? " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[i for i in (1:7).^2]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# What happens when we remove the dot syntax?\n", "[i for i in (1:7)^2]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "[i^2 for i in 1:7]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Explore**: What does the following do? " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sort(unique(x^2 + y^2 for x in 1:5, y in 1:5)) # The inner parentheses define a **generator**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Suppose we want to see $n \\times n$ multiplication tables for $n=1,2,3,4,5$:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for n in 1:5\n", " display([i*j for i=1:n, j=1:n])\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# `Interact.jl` is a Julia *package* for interacting with data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's way more fun to **interact** with our data.\n", "We install the `Interact.jl` package as follows; this needs to be executed only once for any given Julia installation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# using Pkg; Pkg.add(\"Interact\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we load the package with the following `using` command, in each Julia session:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "using Interact" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The package contains a `@manipulate` macro, that is wrapped around a `for` loop:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@manipulate for n in 1:1000\n", " n\n", "end" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@manipulate for n in 1:20\n", " [i*j for i in 1:n, j in 1:n]\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We use a double `for` loop to get a double slider!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@manipulate for n in 3:10, i in 1:9\n", " A = fill(0, n, n)\n", " A[1:3, 1:3] .= i # fill a sub-block\n", "A\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia is built around functions: all \"commands\" or \"operations\" in Julia are functions:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# verbose form:\n", "function f(x)\n", " x^2\n", "end\n", "\n", "# one-line form:\n", "f2(x) = x^2\n", "\n", "# anonymous form:\n", "f3 = x -> x^2;" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Functions just work, as long as they make sense:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The square of a matrix is unambiguously defined\n", "f(rand(3, 3))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# What the 'square of a vector' means is ambiguous\n", "f(rand(3))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# In the definition below, `a` and `power` are optional arguments to `f`, supplied with default values.\n", "function f(x, a=1, power=2)\n", " a*x^power\n", "end" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# `a` defaults to 1 and `power` to 2\n", "f(7)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The first optional argument passed is assigned to the local variable `a`\n", "# `power` defaults to 2\n", "f(10, 3)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "f(10, 3, 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's define a function to insert a block in a matrix:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "function insert_block(A, i, j, what=7)\n", " B = A[:,:] # B is a copy of A \n", " B[i:i+2, j:j+2] = fill(what, 3, 3)\n", " \n", " return B # the `return` keyword is optional\n", "end" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = fill(0, 9, 9)\n", "insert_block(A, 3, 5) # this returns the new matrix" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = fill(0, 9, 9)\n", "insert_block(A, 3, 5, 2) # Use 2 instead of 7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can move the block around:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = fill(0, 10, 10)\n", "n = size(A, 1)\n", "\n", "@manipulate for i in 1:n-2, j in 1:n-2\n", " insert_block(A, i, j)\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Strings" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia can manipulate strings easily:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "S = \"Hello\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "replace(S, \"H\" => \"J\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = 3" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "string(S, \" \", S, \" \", \"Julia; a = \", a) # build a string by concatenating things" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More about strings: Julia Doc on Strings " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Functions in Julia try to be **generic**, i.e. to work with as many kinds of object as possible:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "A = fill(\"Julia\", 5, 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia allows us to display objects in different ways. For example, the following code displays a matrix of strings\n", "in the notebook using an HTML representation:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "function Base.show(io::IO, ::MIME\"text/html\", M::Matrix{T}) where {T<:String}\n", " max_length = maximum(length.(M))\n", " dv=\"