HSG-MCS-HS21_Julia/JuliaTutorial-master/Tutorial_99_TrickyStuff.ipynb
2021-11-15 21:14:51 +01:00

643 lines
16 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Tricky Stuff\n",
"\n",
"This file highlights some tricky aspects of Julia (from the perspective of a Matlab user)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Load Packages and Extra Functions"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"printyellow (generic function with 1 method)"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"using Printf, LinearAlgebra\n",
"\n",
"include(\"jlFiles/printmat.jl\") #function for prettier matrix printing"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# An Nx1 Array Is Not a Vector\n",
"\n",
"and it sometimes matters. \n",
"\n",
"Julia has both vectors and Nx1 arrays (the latter being a special case of NxM arrays). They can often be used interchangeably, but not always."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"v and v2 look similar:\n",
" 1 \n",
" 1 \n",
"\n",
" 1 \n",
" 1 \n",
"\n",
"but they have different sizes: (2,) (2, 1)\n"
]
}
],
"source": [
"v = ones(Int,2) #a vector with two elements\n",
"v2 = ones(Int,2,1) #a 2x1 matrix (Array)\n",
"\n",
"println(\"v and v2 look similar:\")\n",
"printmat(v)\n",
"printmat(v2)\n",
"println(\"but they have different sizes: \",size(v),\" \",size(v2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# X[1,:] Gives a (Flat) Vector\n",
"\n",
"If `X` is a $T\\times n$ matrix, then `X[1,:]` gives a flat vector, *not* a $1xn$ matrix (or row vector)."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"size of X[1,:]: (2,)\n",
" 11 \n",
" 12 \n",
"\n",
"size of X[1:1,:]: (1, 2)\n",
" 11 12 \n",
"\n"
]
}
],
"source": [
"X = [11 12;21 22]\n",
"\n",
"x1 = X[1,:] #this gives a flat vector\n",
"println(\"size of X[1,:]: \", size(x1))\n",
"printmat(x1)\n",
"\n",
"x1b = X[1:1,:] #this gives a 1x2 matrix\n",
"println(\"size of X[1:1,:]: \", size(x1b))\n",
"printmat(x1b)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Array .+ Scalar Requires a Dot (.)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 2 \n",
" 3 \n",
"\n"
]
}
],
"source": [
"y = [1;2] .+ 1 #do not forget the dot (.)\n",
"printmat(y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Creating Variables in a Loop"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Tor: 0.28366218546322625\n",
"Oden: 0.28366218546322625\n"
]
}
],
"source": [
"for i = 1:5\n",
" global Tor #without this, Tor is not seen outside the loop\n",
" Tor = cos(i)\n",
"end\n",
"println(\"Tor: $Tor\")\n",
"\n",
"\n",
"Oden = Inf\n",
"for i = 1:5\n",
" #global Oden #only needed in script\n",
" Oden = cos(i) #will overwrite an existing value \n",
"end\n",
"println(\"Oden: $Oden\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Threads.@threads and Variable Scope\n",
"\n",
"Code like this\n",
"```\n",
"v = 1:2\n",
"Threads.@threads for i = 1:N\n",
" v = something \n",
" x = SomeFunction(v)\n",
"end\n",
"```\n",
"can create unexpected results since the threads are sharing `v`. This is solved by declaring `v` inside the loop to be `local`. (Clearly, this only happens when you have configured Julia to use several threads.)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"this should always be zero. Run a few times to check if that is true.\n",
"\n",
"[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0, 15.0, 11.0, 16.0, 0.0, 16.0, 16.0, 0.0, 0.0, 6.0, 0.0, 0.0, 19.0, 20.0, 17.0, 0.0, 22.0, 9.0, 0.0, 0.0, 20.0, 23.0, 18.0, 32.0, 22.0, 28.0, 16.0, 28.0, 40.0, 0.0, 20.0, 21.0, 35.0, 30.0, 26.0, 33.0, 27.0, 27.0, 19.0, 47.0, 12.0, 0.0, 35.0, 0.0, 31.0, 51.0, 50.0, 31.0, 29.0, 0.0, 50.0, 47.0, 39.0, 53.0, 49.0, 19.0, 66.0, 59.0, 46.0, 38.0, 50.0, 66.0, 39.0, 67.0, 40.0, 0.0, 69.0, 36.0, 68.0, 73.0, 43.0, 67.0, 77.0, 65.0, 78.0, 47.0, 80.0, 56.0, 32.0, 61.0, 81.0, 70.0, 67.0, 76.0, 88.0, 90.0, 81.0]\n"
]
}
],
"source": [
"function f2(N)\n",
" v = falses(N+1)\n",
" x = zeros(Int,N,N)\n",
" Threads.@threads for i = 1:N\n",
" #local v #uncomment to solve the problem\n",
" v = falses(N)\n",
" v[i] = true\n",
" x[v,i] .= i\n",
" end\n",
" return x\n",
"end\n",
"\n",
"println(\"this should always be zero. Run a few times to check if that is true.\\n\")\n",
"M = 100\n",
"dev = zeros(M)\n",
"for i = 1:M\n",
" dev[i] = maximum(abs,f2(i) - diagm(1:i))\n",
"end\n",
"println(dev)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# A Heterogeneous Array"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To create a 'cell array' (a heterogeneous Array), use `[x1,x2,...]`\n",
"\n",
"Alternatively, you can preallocate as in `B = Array{Any}(undef,3)` and then fill by, for instance, `B[1] = [11 12]`"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"The array A: \n",
" 11 12 \n",
" 21 22 \n",
"\n",
"A nice dog\n",
"\n",
" 27 \n",
"\n",
"\n",
"The array B: \n",
" 11 12 \n",
" 21 22 \n",
"\n",
"A nice dog\n",
"\n",
" 27 \n",
"\n"
]
}
],
"source": [
"A = [[11 12;21 22],\"A nice dog\",27]\n",
"\n",
"println(\"\\nThe array A: \")\n",
"foreach(i->printmat(A[i]),1:length(A)) #print each element of A\n",
"\n",
"B = Array{Any}(undef,3)\n",
"B[1] = [11 12]\n",
"B[2] = \"A bad cat\"\n",
"B[3] = pi\n",
"\n",
"println(\"\\nThe array B: \")\n",
"foreach(i->printmat(A[i]),1:length(A))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# An Array of Arrays\n",
"\n",
"can be initialized by comprehension (see below). (Do *not* use fill. See \"A Reshaped Array\" for why.)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x[1]\n",
" -99.000 0.000\n",
" 0.000 0.000\n",
"\n",
"x[2]\n",
" 0.000 0.000\n",
" 0.000 0.000\n",
"\n"
]
}
],
"source": [
"x = [zeros(2,2) for i=1:2] #a vector of two matrices\n",
"x[1][1,1] = -99 #change an element of x[1]\n",
"\n",
"println(\"x[1]\")\n",
"printmat(x[1])\n",
"\n",
"println(\"x[2]\")\n",
"printmat(x[2])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Arrays are Different...\n",
"\n",
"Vectors and matrices (arrays) can take lots of memory space, so **Julia is designed to avoid unnecessary copies of arrays**."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Issue 1. B = A Creates Two Names of the Same Array\n",
"\n",
"If A is an array, then\n",
"```\n",
"B = A\n",
"```\n",
"creates two names of the *same* array. If you later change A, then B is changed automatically. (Similarly, if you change B, then A is changed automatically.) To avoid this, do\n",
"```\n",
"C = copy(A)\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"old A,B,C (each is a column): \n",
" A B C\n",
" 1 1 1\n",
" 2 2 2\n",
"\n",
"after changing A[2] to -999, A,B,C are:\n",
" A B C\n",
" 1 1 1\n",
" -999 -999 2\n",
"\n",
"\n",
"\u001b[34m\u001b[1mNotice that B changed, but C did not.\u001b[22m\u001b[39m\n"
]
}
],
"source": [
"A = [1,2]\n",
"B = A #A and B are the same\n",
"C = copy(A) #A and C are not the same\n",
"println(\"old A,B,C (each is a column): \")\n",
"printmat([A B C],colNames=[\"A\",\"B\",\"C\"],prec=0)\n",
"\n",
"A[2] = -999\n",
"println(\"after changing A[2] to -999, A,B,C are:\")\n",
"printmat([A B C],colNames=[\"A\",\"B\",\"C\"],prec=0)\n",
"\n",
"printblue(\"\\nNotice that B changed, but C did not.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Issue 2. A Reshaped Array Still Refers to the Original Array\n",
"\n",
"Let `A` be an array. If you create a reshaped array by either \n",
"```\n",
"B = reshape(A,n,m)\n",
"C = vec(A)\n",
"D = A'\n",
"E = fill(A,2)\n",
"```\n",
"then A, B, C, D and E contain the same values. Changing one changes the others automatically.\n",
"\n",
"This can be 'fixed' by using `copy()`\n",
"```\n",
"B = copy(reshape(A,n,m))\n",
"C = copy(vec(A))\n",
"D = copy(A')\n",
"E = [copy(A) for i=1:2]\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"original A: \n",
" 1 2 \n",
"\n",
"old B, C and D (each is a column): \n",
" B C D\n",
" 1 1 1\n",
" 2 2 2\n",
"\n",
"B, C and D after changing element A[2] to -999\n",
" B C D\n",
" 1 1 1\n",
" -999 -999 -999\n",
"\n",
"E[1] and E[2] after changing element A[2] to -999\n",
" 1 -999 \n",
"\n",
" 1 -999 \n",
"\n",
"\n",
"\u001b[34m\u001b[1mNotice that B, C, D and E also changed\u001b[22m\u001b[39m\n"
]
}
],
"source": [
"A = [1 2]\n",
"println(\"original A: \")\n",
"printmat(A)\n",
"\n",
"B = reshape(A,2,1)\n",
"C = vec(A)\n",
"D = A'\n",
"E = fill(A,2)\n",
"\n",
"println(\"old B, C and D (each is a column): \")\n",
"printmat([B C D],colNames=[\"B\",\"C\",\"D\"],prec=0)\n",
"\n",
"A[2] = -999\n",
"println(\"B, C and D after changing element A[2] to -999\")\n",
"printmat([B C D],colNames=[\"B\",\"C\",\"D\"],prec=0)\n",
"println(\"E[1] and E[2] after changing element A[2] to -999\")\n",
"printmat(E[1])\n",
"printmat(E[2])\n",
"\n",
"printblue(\"\\nNotice that B, C, D and E also changed\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Issue 3. Changing an Array Inside a Function Can Have Effects *Outside* the Function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When you use an array as a function argument, then it is passed as a reference.\n",
"\n",
"This means that if you change some elements of the array (`A[1] = A[1]/2`, say) inside the function, then it will also affect the array outside the function (even if they have different names). \n",
"\n",
"In contrast, if you change the entire array (`A/2`, say) inside the function, then that does not affect the array outside the function.\n",
"\n",
"If you really need an independent copy of an array, create it by\n",
"\n",
"`B = copy(A)`"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"original x:\n",
" 1.000 2.000\n",
"\n",
"x (outside function) after calling f1(x):\n",
" 0.500 2.000\n",
"\n",
"\n",
"original x:\n",
" 1.000 2.000\n",
"\n",
"x (outside function) after calling f2(x):\n",
" 1.000 2.000\n",
"\n",
"\n",
"\u001b[34m\u001b[1mNotice that f1() changed x also outside the function, but f2() did not.\u001b[22m\u001b[39m\n"
]
}
],
"source": [
"function f1(A)\n",
" A[1] = A[1]/2 #changes ELEMENTS of A, affects outside value\n",
" return A\n",
"end\n",
"function f2(A)\n",
" A = A/2 #changes all of A, does not affect outside value\n",
" return A\n",
"end\n",
"\n",
"x = [1.0 2.0]\n",
"printlnPs(\"original x:\")\n",
"printmat(x)\n",
"\n",
"y1 = f1(x)\n",
"printlnPs(\"x (outside function) after calling f1(x):\")\n",
"printmat(x)\n",
"\n",
"x = [1.0 2.0]\n",
"printlnPs(\"\\noriginal x:\")\n",
"printmat(x)\n",
"\n",
"y2 = f2(x)\n",
"printlnPs(\"x (outside function) after calling f2(x):\")\n",
"printmat(x)\n",
"\n",
"printblue(\"\\nNotice that f1() changed x also outside the function, but f2() did not.\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Issue 4. Arrays in Arrays Still Refer to the Underlying Arrays\n",
"\n",
"An array `a` inside another array `A`is really just a reference to the existing `a`. Changing elements of `a` will change `A`.\n",
"\n",
"Again, this can be 'fixed' by using `copy(a)` when creating `A`."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"A:\n",
" 11 12 \n",
" 21 22 \n",
"\n",
"A nice dog\n",
"\n",
" 27 \n",
"\n",
"\n",
"\u001b[34m\u001b[1mChange a[1,1] to -999 and notice that also A changes\u001b[22m\u001b[39m\n",
"A:\n",
" -999 12 \n",
" 21 22 \n",
"\n",
"A nice dog\n",
"\n",
" 27 \n",
"\n"
]
}
],
"source": [
"a = [11 12;21 22]\n",
"A = [a,\"A nice dog\",27] #try copy(a) instead\n",
"println(\"A:\")\n",
"foreach(i->printmat(A[i]),1:length(A))\n",
"\n",
"printblue(\"\\nChange a[1,1] to -999 and notice that also A changes\")\n",
"a[1,1] = -999\n",
"println(\"A:\")\n",
"foreach(i->printmat(A[i]),1:length(A))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"@webio": {
"lastCommId": null,
"lastKernelId": null
},
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Julia 1.6.1",
"language": "julia",
"name": "julia-1.6"
},
"language_info": {
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.6.1"
}
},
"nbformat": 4,
"nbformat_minor": 1
}