{ "cells": [ { "cell_type": "markdown", "metadata": { "toc": "true" }, "source": [ "# Table of Contents\n", "

1  Introduction to
1.1  Types of computer languages
1.2  Messages
1.3  What's Julia?
1.4  R is great, but...
1.5  Language features of R, Matlab and Julia
1.6  Benchmark
1.7  Gibbs sampler example by Doug Bates
1.8  Learning resources
1.9  Julia REPL (Read-Evaluation-Print-Loop)
1.10  Seek help
1.11  Which IDE?
1.12  Julia package system
1.13  Calling R from Julia
1.14  Some basic Julia code
1.15  Timing and benchmark
1.16  Matrices and vectors
1.16.1  Dimensions
1.16.2  Indexing
1.16.3  Concatenate matrices
1.16.4  Dot operation
1.16.5  Basic linear algebra
1.16.6  Sparse matrices
1.17  Control flow and loops
1.18  Functions
1.19  Type system
1.20  Multiple dispatch
1.21  Just-in-time compilation (JIT)
1.22  Profiling Julia code
1.23  Memory profiling
1.24  Type stability
1.25  Plotting in Julia
" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Introduction to\n", "\n", "\"Julia" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Types of computer languages\n", "\n", "* **Compiled languages**: C/C++, Fortran, ... \n", " - Directly compiled to machine code that is executed by CPU \n", " - Pros: fast, memory efficient\n", " - Cons: longer development time, hard to debug\n", "\n", "* **Interpreted language**: R, Matlab, Python, SAS IML, JavaScript, ... \n", " - Interpreted by interpreter\n", " - Pros: fast prototyping\n", " - Cons: excruciatingly slow for loops\n", "\n", "* Mixed (dynamic) languages: Matlab (JIT), R (`compiler` package), Julia, Cython, JAVA, ...\n", " - Pros and cons: between the compiled and interpreted languages\n", "\n", "* Script languages: Linux shell scripts, Perl, ...\n", " - Extremely useful for some data preprocessing and manipulation\n", "\n", "* Database languages: SQL, Hadoop. \n", " - Data analysis *never* happens if we do not know how to retrieve data from databases " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Messages\n", "\n", "* To be versatile in the big data era, master at least one language in each category.\n", "\n", "* To improve efficiency of interpreted languages such as R or Matlab, conventional wisdom is to avoid loops as much as possible. Aka, **vectorize code**\n", "> The only loop you are allowed to have is that for an iterative algorithm.\n", "\n", "* When looping is necessary, need to code in C, C++, or Fortran. \n", "Success stories: the popular `glmnet` package in R is coded in Fortran.\n", "\n", "* Modern languages such as Julia tries to solve the **two language problem**. That is to achieve efficiency without vectorizing code." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## What's Julia?\n", "\n", "> Julia is a high-level, high-performance dynamic programming language for technical computing, with syntax that is familiar to users of other technical computing environments\n", "\n", "* Started in 2009. First public release in 2012. \n", " - Creators: Jeff Bezanson, Alan Edelman, Stefan Karpinski, Viral Shah\n", " - Current release v0.6.2\n", " - v1.0 is staged to release in 2018\n", "\n", "* Aim to solve the notorious **two language problem**:\n", " - Prototype code goes into high-level languages like R/Python, production code goes into low-level language like C/C++\n", "> Walks like Python. Runs like C.\n", "\n", "\n", "\n", "* Write high-level, abstract code that closely resembles mathematical formulas\n", " - yet produces fast, low-level machine code that has traditionally only been generated by static languages.\n", "\n", "* Julia is more than just \"Fast R\" or \"Fast Matlab\"\n", " - Performance comes from features that work well together. \n", " - You can't just take the magic dust that makes Julia fast and sprinkle it on [language of choice]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## R is great, but...\n", "\n", "* It's not meant for high performance computing\n", " - http://adv-r.had.co.nz/Performance.html\n", " - Section on performance starts with \"Why is R slow?\" \n", "\n", "* Deficiencies in the core language \n", " - Many fixed with packages (`devtools`, `roxygen2`, `Matrix`)\n", " - Others harder to fix (R uses an old version of BLAS)\n", " - Some impossible to fix (clunky syntax, poor design choices)\n", "\n", "* Only 6 active developers left (out of 20 R-Core members)\n", " - JuliaLang organization has 74 members, with 567 total contributors (as of 3/3/17)\n", " - https://github.com/JuliaLang/julia/graphs/contributors\n", "\n", "* Doug Bates (member of R-Core, `Matrix` and `lme4`)\n", " - Getting Doug on board was a big win for statistics with Julia, as he brought a lot of knowledge about the history of R development and design choices\n", " - https://github.com/dmbates/MixedModels.jl\n", " \n", " > As some of you may know, I have had a (rather late) mid-life crisis and run off with another language called Julia. \n", " >\n", " > -- Doug Bates (on the `knitr` Google Group)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Language features of R, Matlab and Julia\n", "\n", "| Features | R | Matlab | Julia |\n", "|:---------------------:|:------------------------:|:--------------:|:-------------------:|\n", "| Open source | 👍 | 👎 | 👍 |\n", "| IDE | RStudio 👍 👍 👍 | 👍 👍 👍 | Atom+Juno 👎 |\n", "| Dynamic document | RMarkdown 👍 👍 👍 | 👍 👍 | Jupyter 👍 👍 |\n", "| Multi-threading | `parallel` 👎 | 👍 | 👍 👍 [see docs](http://docs.julialang.org/en/stable/manual/parallel-computing/) |\n", "| JIT | `compiler` 👎 | 👍 👍 | 👍 👍 👍 |\n", "| Call C/Fortran | wrapper, `Rcpp` | wrapper | [no glue code needed](http://docs.julialang.org/en/stable/manual/calling-c-and-fortran-code/) |\n", "| Call shared library | wrapper | wrapper | [no glue code needed](http://docs.julialang.org/en/stable/manual/calling-c-and-fortran-code/) |\n", "| Type system | 👎 | 👍 👍 | 👍 👍 👍 |\n", "| Pass by reference | 👎 | 👎 | 👍 👍 👍 |\n", "| Linear algebra | 👎 | MKL, Arpack | OpenBLAS, eigpack, or MKL |\n", "| Distributed computing | 👎 | 👍 | 👍 👍 👍 |\n", "| Sparse linear algebra | `Matrix` package 👎 | 👍 👍 👍 | 👍 👍 👍 |\n", "| Documentation | 👍 | 👍 👍 👍 | 👍 |\n", "| Profiler | 👍 👍 | 👍 👍 👍 | 👍 |" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Benchmark\n", "\n", "* Benchmark code `R-benchmark-25.R` from [http://r.research.att.com/benchmarks/R-benchmark-25.R](http://r.research.att.com/benchmarks/R-benchmark-25.R) covers many commonly used numerical operations used in statistics. \n", "\n", "* We ported to [Matlab](./benchmark_matlab.m) and [Julia](./benchmark_julia.jl) and report the run times (averaged over 5 runs) here.\n", "\n", "| Test | R 3.4.3 | Matlab R2017a | Julia 0.6.2 |\n", "|:-------------------------------------------------- |:-------:|:-------------:|:-----------:|\n", "| Matrix creation, trans., deform. (2500 x 2500) | 0.65 | **0.13** | 0.21 |\n", "| Power of matrix (2400 x 2400, `A.^1000`) | 0.18 | **0.10** | 0.18 |\n", "| Quick sort ($n = 7 \\times 10^6$) | 0.75 | **0.30** | 0.65 |\n", "| Cross product (2800 x 2800, $A^TA$) | 15.02 | **0.18** | 0.23 |\n", "| LS solution ($n = p = 2000$) | 7.00 | 0.07 | **0.06** |\n", "| FFT ($n = 2,400,000$) | 0.32 | **0.03** | 0.04 |\n", "| Eigen-values ($600 \\times 600$) | 0.75 | **0.22** | 0.26 |\n", "| Determinant ($2500 \\times 2500$) | 3.77 | 0.19 | **0.14** |\n", "| Cholesky ($3000 \\times 3000$) | 5.54 | **0.08** | 0.17 |\n", "| Matrix inverse ($1600 \\times 1600$) | 4.13 | **0.11** | 0.14 |\n", "| Fibonacci (vector calculation) | 0.23 | **0.16** | 0.27 |\n", "| Hilbert (matrix calculation) | 0.27 | 0.07 | **0.06** |\n", "| GCD (recursion) | 0.42 | **0.09** | 0.16 |\n", "| Toeplitz matrix (loops) | 0.32 | 0.0012 | **0.0007** |\n", "| Escoufiers (mixed) | 0.30 | 0.15 | **0.14** |\n", "\n", "Machine specs: Intel i7 @ 2.9GHz (4 physical cores, 8 threads), 16G RAM, Mac OS 10.13.3." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Gibbs sampler example by Doug Bates\n", "\n", "* An example from Dr. Doug Bates's slides [Julia for R Programmers](http://www.stat.wisc.edu/~bates/JuliaForRProgrammers.pdf).\n", "\n", "* The task is to create a Gibbs sampler for the density \n", "$$\n", "f(x, y) = k x^2 exp(- x y^2 - y^2 + 2y - 4x), x > 0\n", "$$\n", "using the conditional distributions\n", "$$\n", "\\begin{eqnarray*}\n", " X | Y &\\sim& \\Gamma \\left( 3, \\frac{1}{y^2 + 4} \\right) \\\\\n", " Y | X &\\sim& N \\left(\\frac{1}{1+x}, \\frac{1}{2(1+x)} \\right).\n", "\\end{eqnarray*}\n", "$$\n", "\n", "* This is a Julia function for the simple Gibbs sampler:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "jgibbs (generic function with 1 method)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using Distributions\n", "\n", "function jgibbs(N, thin)\n", " mat = zeros(N, 2)\n", " x = y = 0.0\n", " for i in 1:N\n", " for j in 1:thin\n", " x = rand(Gamma(3.0, 1.0 / (y * y + 4.0)))\n", " y = rand(Normal(1.0 / (x + 1.0), 1.0 / sqrt(2.0(x + 1.0))))\n", " end\n", " mat[i, 1] = x\n", " mat[i, 2] = y\n", " end\n", " mat\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Generate a bivariate sample of size 10,000 with a thinning of 500. How long does it take?" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.381052949" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "jgibbs(100, 5); # warm-up\n", "@elapsed jgibbs(10000, 500)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* R solution. The `RCall.jl` package allows us to execute R code without leaving the `Julia` environment. We first define an R function `Rgibbs()`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RCall.RObject{RCall.ClosSxp}\n", "function (N, thin) \n", "{\n", " mat <- matrix(0, nrow = N, ncol = 2)\n", " x <- y <- 0\n", " for (i in 1:N) {\n", " for (j in 1:thin) {\n", " x <- rgamma(1, 3, y * y + 4)\n", " y <- rnorm(1, 1/(x + 1), 1/sqrt(2 * (x + 1)))\n", " }\n", " mat[i, ] <- c(x, y)\n", " }\n", " mat\n", "}\n" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using RCall\n", "\n", "R\"\"\"\n", "library(Matrix)\n", "Rgibbs <- function(N, thin) {\n", " mat <- matrix(0, nrow=N, ncol=2)\n", " x <- y <- 0\n", " for (i in 1:N) {\n", " for (j in 1:thin) {\n", " x <- rgamma(1, 3, y * y + 4) # 3rd arg is rate\n", " y <- rnorm(1, 1 / (x + 1), 1 / sqrt(2 * (x + 1)))\n", " }\n", " mat[i,] <- c(x, y)\n", " }\n", " mat\n", "}\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and then generate the same number of samples" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "18.415733088" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# benchmark\n", "@elapsed R\"\"\"\n", "system.time(Rgibbs(10000, 500))\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see 40-80 fold speed up of `Julia` over `R` on this example, **without extra coding effort**!" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Learning resources\n", "\n", "0. [Intro to Julia](https://www.youtube.com/watch?v=4igzy3bGVkQ) (1h40m), by Jane Herriman (Dec 19, 2017), and next (monthly) tutorial \n", "[Intro to Julia](https://www.youtube.com/watch?v=JserqX6hbYw), by Jane Herriman on April 6, 2018 at 10AM PDT. \n", "0. Cheat sheet: [The Fast Track to Julia](https://juliadocs.github.io/Julia-Cheat-Sheet/). \n", "0. Browse the `Julia` [documentation](http://docs.julialang.org/en/stable/). \n", "0. For Matlab users, read [Noteworthy Differences From Matlab](http://docs.julialang.org/en/stable/manual/noteworthy-differences/?highlight=matlab#noteworthy-differences-from-matlab). \n", "For R users, read [Noteworthy Differences From R](http://docs.julialang.org/en/stable/manual/noteworthy-differences/?highlight=matlab#noteworthy-differences-from-r). \n", "For Python users, read [Noteworthy Differences From Python](http://docs.julialang.org/en/stable/manual/noteworthy-differences/?highlight=matlab#noteworthy-differences-from-python). \n", "0. The [Learning page](http://julialang.org/learning/) on Julia's website has pointers to many other learning resources. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Julia REPL (Read-Evaluation-Print-Loop)\n", "\n", "The `Julia` REPL, or `Julia` shell, has four main modes.\n", "\n", "0. Default mode is the Julian prompt `julia>`. Type backspace in other modes to enter default mode. \n", "\n", "0. Help mode `help?>`. Type `?` to enter help mode. `?search_term` does a fuzzy search for `search_term`. \n", "\n", "0. Shell mode `shell>`. Type `;` to enter shell mode. \n", "\n", "0. Search mode `(reverse-i-search)`. Press `ctrl+R` to enter search model. \n", "\n", "0. With **`RCall.jl`** package installed, we can enter the R mode by typing `$` (shift+4) at Julia REPL.\n", "\n", "Some survival commands in Julia REPL: \n", "0. `quit()` or `Ctrl+D`: exit Julia.\n", "\n", "0. `Ctrl+C`: interrupt execution.\n", "\n", "0. `Ctrl+L`: clear screen.\n", "\n", "0. `whos()`: list all variables in current workspace.\n", "\n", "0. `workspace()`: clear all variables and reset session.\n", "\n", "0. Append `;` (semi-colon) to suppress displaying output from a command.\n", "\n", "0. `include(\"filename.jl\")` to source a Julia code file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Seek help\n", "\n", "* Online help from REPL: `?function_name`.\n", "\n", "* Google (of course).\n", "\n", "* Julia documentation: .\n", "\n", "* Look up source code: `@edit func(x)`.\n", "\n", "* .\n", "\n", "* Friends." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Which IDE?\n", "\n", "* I highly recommend the editor [Atom](https://atom.io) with packages `julia-client`, `language-julia`, and `latex-completions` installed. \n", "\n", "* If you want RStudio- or Matlab- like IDE, install the `uber-juno` package in Atom. Follow instructions at [https://github.com/JunoLab/uber-juno/blob/master/setup.md](https://github.com/JunoLab/uber-juno/blob/master/setup.md).\n", "\n", "* [**JuliaPro**](https://juliacomputing.com/products/juliapro.html) bundles Julia, Atom, Juno, and many commonly used packages.\n", "\n", "* For homework, I recommend [**Jupyter Notebook**](http://jupyter.org).\n", "\n", " Also worth trying is [JupyterLab](http://jupyterlab.readthedocs.io/en/stable/), which is supposed to replace Jupyter Notebook after it reaches v1.0." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Julia package system\n", "\n", "\n", "\n", "* Each Julia package is a Git repository. Each Julia package name ends with `.jl`. E.g., `Distributions.jl` package lives at . \n", "Google search with `PackageName.jl` usually leads to the package on github.com. \n", "\n", "* The package ecosystem is rapidly maturing; a complete list of **registered** packages (which are required to have a certain level of testing and documentation) is at [http://pkg.julialang.org/](http://pkg.julialang.org/).\n", "\n", "* For example, the package called `Distributions.jl` is added with\n", "```julia\n", "Pkg.add(\"Distributions\") # no .jl \n", "```\n", "and \"removed\" (although not completely deleted) with\n", "```julia\n", "Pkg.rm(\"Distributions\")\n", "```\n", "* The package manager provides a dependency solver that determines which packages are actually required to be installed.\n", "\n", "* **Non-registered** packages are added by cloning the relevant Git repository. E.g.,\n", "```julia\n", "Pkg.clone(\"git@github.com:OpenMendel/SnpArrays.jl.git\")\n", "```\n", "\n", "* A package needs only be added once, at which point it is downloaded into your local `.julia/vx.x` directory in your home directory. " ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "total 16\n", "drwxr-xr-x 11 huazhou staff 352 Nov 24 21:46 ASTInterpreter2\n", "drwxr-xr-x 11 huazhou staff 352 Feb 28 18:40 AbstractFFTs\n", "drwxr-xr-x 11 huazhou staff 352 Dec 22 06:47 Atom\n", "drwxr-xr-x 15 huazhou staff 480 Apr 2 08:12 Automa\n", "drwxr-xr-x 10 huazhou staff 320 Aug 9 2017 AxisAlgorithms\n", "drwxr-xr-x 11 huazhou staff 352 Feb 7 07:03 AxisArrays\n", "drwxr-xr-x 11 huazhou staff 352 Aug 12 2017 BGZFStreams\n", "drwxr-xr-x 13 huazhou staff 416 Feb 7 07:03 BenchmarkTools\n", "drwxr-xr-x 11 huazhou staff 352 Jan 31 09:03 BinDeps\n", "drwxr-xr-x 13 huazhou staff 416 Mar 15 10:37 BinaryProvider\n", "drwxr-xr-x 14 huazhou staff 448 Feb 19 11:35 BioCore\n", "drwxr-xr-x 14 huazhou staff 448 Mar 4 17:31 BioSequences\n", "drwxr-xr-x 12 huazhou staff 384 Mar 22 07:14 BioSymbols\n", "drwxr-xr-x 13 huazhou staff 416 Feb 14 12:35 Blink\n", "drwxr-xr-x 12 huazhou staff 384 Apr 7 16:15 Blosc\n", "drwxr-xr-x 13 huazhou staff 416 Mar 4 17:31 BufferedStreams\n", "drwxr-xr-x 11 huazhou staff 352 Jan 10 12:56 CPLEX\n", "drwxr-xr-x 14 huazhou staff 448 Oct 24 10:48 CSDP\n", "drwxr-xr-x 13 huazhou staff 416 Apr 13 15:31 CSV\n" ] } ], "source": [ ";ls -l /Users/huazhou/.julia/v0.6 \"|\" head -20" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Directory of a specific package can be queried by `Pkg.dir()`:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"/Users/huazhou/.julia/v0.6/Distributions\"" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Pkg.dir(\"Distributions\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* If you start having problems with packages that seem to be unsolvable, you can try just deleting your .julia directory and reinstalling all your packages. \n", "\n", "* For **JuliaPro**, the packages are downloaed to directory `/Applications/JuliaPro-0.6.2.2.app/Contents/Resources/pkgs-0.6.2.2/v0.6/`.\n", "\n", "* Periodically, one should run `Pkg.update()`, which checks for, downloads and installs updated versions of all the packages you currently have installed.\n", "\n", "* `Pkg.status()` lists the status of all installed packages.\n", "\n", "* Using functions in package.\n", "```julia\n", "using Distributions\n", "```\n", "This pulls all of the *exported* functions in the module into your local namespace, as you can check using the `whos()` command. An alternative is\n", "```julia\n", "import Distributions\n", "```\n", "Now, the functions from the Distributions package are available only using \n", "```julia\n", "Distributions.\n", "```\n", "All functions, not only exported functions, are always available like this." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Calling R from Julia\n", "\n", "* The [`RCall.jl`](https://github.com/JuliaInterop/RCall.jl) package allows you to embed R code inside of Julia.\n", "\n", "* There are also `PyCall`, `MATLAB`, `JavaCall`, `CxxWrap` packages." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "image/png": "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" }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "RCall.RObject{RCall.VecSxp}\n", "$breaks\n", " [1] -4.0 -3.5 -3.0 -2.5 -2.0 -1.5 -1.0 -0.5 0.0 0.5 1.0 1.5 2.0 2.5 3.0\n", "\n", "$counts\n", " [1] 2 1 6 22 55 93 158 184 166 165 85 45 15 3\n", "\n", "$density\n", " [1] 0.004 0.002 0.012 0.044 0.110 0.186 0.316 0.368 0.332 0.330 0.170 0.090\n", "[13] 0.030 0.006\n", "\n", "$mids\n", " [1] -3.75 -3.25 -2.75 -2.25 -1.75 -1.25 -0.75 -0.25 0.25 0.75 1.25 1.75\n", "[13] 2.25 2.75\n", "\n", "$xname\n", "[1] \"`#JL`$x\"\n", "\n", "$equidist\n", "[1] TRUE\n", "\n", "attr(,\"class\")\n", "[1] \"histogram\"\n" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Pkg.add(\"RCall\")\n", "using RCall\n", "\n", "x = randn(1000)\n", "R\"\"\"\n", "hist($x, main=\"I'm plotting a Julia vector\")\n", "\"\"\"" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "image/png": "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" }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/plain": [ "RCall.RObject{RCall.VecSxp}\n" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" }, { "name": "stderr", "output_type": "stream", "text": [ "\u001b[1m\u001b[33mWARNING: \u001b[39m\u001b[22m\u001b[33mRCall.jl: `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.\u001b[39m\n" ] } ], "source": [ "R\"\"\"\n", "library(ggplot2)\n", "qplot($x)\n", "\"\"\"" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RCall.RObject{RCall.RealSxp}\n", " [1] 0.51015405 -0.84060075 -0.99595630 0.33365937 -0.23253892 -1.37439873\n", " [7] -0.59601796 1.76222020 0.06672042 1.70257300\n" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = R\"\"\"\n", "rnorm(10)\n", "\"\"\"" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10-element Array{Float64,1}:\n", " 0.510154 \n", " -0.840601 \n", " -0.995956 \n", " 0.333659 \n", " -0.232539 \n", " -1.3744 \n", " -0.596018 \n", " 1.76222 \n", " 0.0667204\n", " 1.70257 " ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = collect(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Access Julia variables in R REPL mode:\n", "```julia\n", "julia> x = rand(5) # Julia variable\n", "R> y <- $x\n", "```\n", "\n", "* Pass Julia expression in R REPL mode:\n", "```julia\n", "R> y <- $(rand(5))\n", "```\n", "\n", "* Put Julia variable into R environment:\n", "```julia\n", "julia> @rput x\n", "R> x\n", "```\n", "\n", "* Get R variable into Julia environment:\n", "```julia\n", "R> r <- 2\n", "Julia> @rget r\n", "```\n", "\n", "* If you want to call Julia within R, check out the [`XRJulia`](https://cran.r-project.org/web/packages/XRJulia/) package by John Chambers." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Some basic Julia code" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Int64" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = 1\n", "typeof(y) # same as int in R" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Float64" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = 1.0\n", "typeof(y) # same as double in R" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "π = 3.1415926535897..." ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Greek letters: `\\pi`\n", "π" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4.141592653589793" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Greek letters: `\\theta`\n", "θ = y + π" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.0" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# emoji! `\\:kissing_cat:`\n", "😽 = 5.0" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "π = 3.1415926535897..." ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "α̂ = π" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Float64,1}:\n", " 0.0\n", " 0.0\n", " 0.0\n", " 0.0\n", " 0.0" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# vector of Float64 0s\n", "x = zeros(5)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Int64,1}:\n", " 0\n", " 0\n", " 0\n", " 0\n", " 0" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# vector Int64 0s\n", "x = zeros(Int, 5)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 0.0 0.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", " 0.0 0.0 0.0" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# matrix of Float64 0s\n", "x = zeros(5, 3)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 1.0 1.0 1.0\n", " 1.0 1.0 1.0\n", " 1.0 1.0 1.0\n", " 1.0 1.0 1.0\n", " 1.0 1.0 1.0" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# matrix of Float64 1s\n", "x = ones(5, 3)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 2.34728e-314 2.34728e-314 0.0 \n", " 2.34728e-314 2.34728e-314 0.0 \n", " 2.34728e-314 0.0 2.34728e-314\n", " 2.34728e-314 0.0 2.34728e-314\n", " 2.34728e-314 2.34728e-314 0.0 " ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# define array without initialization\n", "x = Array{Float64}(5, 3)" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 0.0 0.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", " 0.0 0.0 0.0" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# fill a matrix by 0s\n", "fill!(x, 0)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Irrational{:π},2}:\n", " π = 3.1415926535897... π = 3.1415926535897... π = 3.1415926535897...\n", " π = 3.1415926535897... π = 3.1415926535897... π = 3.1415926535897...\n", " π = 3.1415926535897... π = 3.1415926535897... π = 3.1415926535897...\n", " π = 3.1415926535897... π = 3.1415926535897... π = 3.1415926535897...\n", " π = 3.1415926535897... π = 3.1415926535897... π = 3.1415926535897..." ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# initialize an array to be 2.5\n", "fill(π, (5, 3))" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3//5" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 3//5" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Rational{Int64}" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(a)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3//7" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = 3//7" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "36//35" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a + b" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 0.626985 0.16688 0.796883\n", " 0.83137 0.82617 0.109381\n", " 0.718678 0.820927 0.858267\n", " 0.344819 0.527395 0.848442\n", " 0.52959 0.379976 0.953298" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# uniform [0, 1) random numbers\n", "x = rand(5, 3)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float16,2}:\n", " 0.56543 0.99023 0.92969\n", " 0.83594 0.58398 0.72461\n", " 0.50488 0.09668 0.82129\n", " 0.15234 0.34375 0.79297\n", " 0.77441 0.96094 0.17676" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# uniform random numbers (in single precision)\n", "x = rand(Float16, 5, 3)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Int64,2}:\n", " 1 1 3\n", " 1 5 4\n", " 2 2 2\n", " 4 4 5\n", " 2 4 3" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# random numbers from {1,...,5}\n", "x = rand(1:5, 5, 3)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " -1.1904 -0.435366 -0.0241442\n", " 0.185787 3.06241 2.37215 \n", " 0.928226 0.582092 1.31479 \n", " 0.570806 0.072032 0.138682 \n", " 0.700352 1.17485 -1.78152 " ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# standard normal random numbers\n", "x = randn(5, 3)" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1:10" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# range\n", "1:10" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "UnitRange{Int64}" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(1:10)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1:2:9" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1:2:10" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "StepRange{Int64,Int64}" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(1:2:10)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10-element Array{Int64,1}:\n", " 1\n", " 2\n", " 3\n", " 4\n", " 5\n", " 6\n", " 7\n", " 8\n", " 9\n", " 10" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# integers 1-10\n", "x = collect(1:10)" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10-element Array{Float64,1}:\n", " 1.0\n", " 2.0\n", " 3.0\n", " 4.0\n", " 5.0\n", " 6.0\n", " 7.0\n", " 8.0\n", " 9.0\n", " 10.0" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Float64 numbers 1-10\n", "x = collect(1.0:10)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10-element Array{Float64,1}:\n", " 1.0\n", " 2.0\n", " 3.0\n", " 4.0\n", " 5.0\n", " 6.0\n", " 7.0\n", " 8.0\n", " 9.0\n", " 10.0" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# convert to a specific type\n", "convert(Vector{Float64}, 1:10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Timing and benchmark" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`@time`, `@elapsed`, `@allocated` macros:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.035783 seconds (9.90 k allocations: 546.145 KiB)\n", " 0.000011 seconds (5 allocations: 176 bytes)\n" ] }, { "data": { "text/plain": [ "117.13472277852794" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "srand(123) # seed\n", "x = randn(10000)\n", "@time sum(x) # first run includes compilation time\n", "@time sum(x) # no compilation time after first run" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9.925e-6" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@elapsed sum(x)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "16" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@allocated sum(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use `BenchmarkTools.jl` for more robust benchmarking. Analog of `microbenchmark` package in R." ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "BenchmarkTools.Trial: \n", " memory estimate: 16 bytes\n", " allocs estimate: 1\n", " --------------\n", " minimum time: 2.688 μs (0.00% GC)\n", " median time: 2.773 μs (0.00% GC)\n", " mean time: 2.851 μs (0.00% GC)\n", " maximum time: 13.655 μs (0.00% GC)\n", " --------------\n", " samples: 10000\n", " evals/sample: 9" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using BenchmarkTools\n", "\n", "@benchmark sum(x)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "RCall.RObject{RCall.VecSxp}\n", "Unit: microseconds\n", " expr min lq mean median uq max neval\n", " sum(`#JL`$x) 11.599 11.649 11.85592 11.6935 11.7555 24.952 100\n" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "R\"\"\"\n", "library(microbenchmark)\n", "microbenchmark(sum($x))\n", "\"\"\"" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Matrices and vectors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dimensions" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 0.897602 -2.00348 -0.205452 \n", " 0.206015 0.166398 -0.0682929\n", " -0.553413 0.687318 0.0969771\n", " 0.422711 0.835254 -0.329361 \n", " 2.41808 0.650404 0.0921857" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = randn(5, 3)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(5, 3)" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "size(x)" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "size(x, 1) # nrow() in R" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "size(x, 2)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "15" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# total number of elements\n", "length(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Indexing" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×5 Array{Float64,2}:\n", " -0.263946 0.306647 1.3524 -1.54503 0.733192\n", " -1.58659 1.09814 -0.995732 -1.72885 0.332798\n", " 1.32131 0.708017 -1.18628 0.853263 0.149281\n", " -1.45118 0.0445099 0.799792 -0.0403455 1.11101 \n", " -1.41587 -0.59818 -0.202697 1.48291 1.07844 " ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 5 × 5 matrix of random Normal(0, 1)\n", "x = randn(5, 5)" ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Float64,1}:\n", " -0.263946\n", " -1.58659 \n", " 1.32131 \n", " -1.45118 \n", " -1.41587 " ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# first column\n", "x[:, 1]" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Float64,1}:\n", " -0.263946\n", " 0.306647\n", " 1.3524 \n", " -1.54503 \n", " 0.733192" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# first row\n", "x[1, :]" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 Array{Float64,2}:\n", " 0.306647 1.3524 \n", " 1.09814 -0.995732" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# sub-array\n", "x[1:2, 2:3]" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2×2 SubArray{Float64,2,Array{Float64,2},Tuple{UnitRange{Int64},UnitRange{Int64}},false}:\n", " 0.306647 1.3524 \n", " 1.09814 -0.995732" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# getting a subset of a matrix creates a copy, but you can also create \"views\"\n", "z = view(x, 1:2, 2:3)" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z[2, 2] = 0.0" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×5 Array{Float64,2}:\n", " -0.263946 0.306647 1.3524 -1.54503 0.733192\n", " -1.58659 1.09814 0.0 -1.72885 0.332798\n", " 1.32131 0.708017 -1.18628 0.853263 0.149281\n", " -1.45118 0.0445099 0.799792 -0.0403455 1.11101 \n", " -1.41587 -0.59818 -0.202697 1.48291 1.07844 " ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×5 Array{Float64,2}:\n", " -0.263946 0.306647 1.3524 -1.54503 0.733192\n", " -1.58659 1.09814 0.0 -1.72885 0.332798\n", " 1.32131 0.708017 -1.18628 0.853263 0.149281\n", " -1.45118 0.0445099 0.799792 -0.0403455 1.11101 \n", " -1.41587 -0.59818 -0.202697 1.48291 1.07844 " ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# y points to same data as x\n", "y = x" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(Ptr{Float64} @0x000000011d409a90, Ptr{Float64} @0x000000011d409a90)" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# x and y point to same data\n", "pointer(x), pointer(y)" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×5 Array{Float64,2}:\n", " 0.0 0.306647 1.3524 -1.54503 0.733192\n", " 0.0 1.09814 0.0 -1.72885 0.332798\n", " 0.0 0.708017 -1.18628 0.853263 0.149281\n", " 0.0 0.0445099 0.799792 -0.0403455 1.11101 \n", " 0.0 -0.59818 -0.202697 1.48291 1.07844 " ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# changing y also changes x\n", "y[:, 1] = 0\n", "x" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×5 Array{Float64,2}:\n", " 0.0 0.306647 1.3524 -1.54503 0.733192\n", " 0.0 1.09814 0.0 -1.72885 0.332798\n", " 0.0 0.708017 -1.18628 0.853263 0.149281\n", " 0.0 0.0445099 0.799792 -0.0403455 1.11101 \n", " 0.0 -0.59818 -0.202697 1.48291 1.07844 " ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# create a new copy of data\n", "z = copy(x)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(Ptr{Float64} @0x000000011d409a90, Ptr{Float64} @0x000000011b049fd0)" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pointer(x), pointer(z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Concatenate matrices" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×5 Array{Float64,2}:\n", " -1.06006 1.85635 -0.758945 -1.85887 -1.82908 \n", " 0.726803 0.266693 -1.76046 1.64189 0.0684493\n", " -0.196268 0.0508897 -1.02538 0.6103 0.0964332\n", " 0.311368 0.550423 0.0771399 -0.320978 -0.29254 \n", " -0.042317 0.0801359 0.0557964 0.924308 -0.484027 " ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x, y, z = randn(5, 3), randn(5, 2), randn(3, 5)\n", "M = [x y]" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8×5 Array{Float64,2}:\n", " -1.06006 1.85635 -0.758945 -1.85887 -1.82908 \n", " 0.726803 0.266693 -1.76046 1.64189 0.0684493\n", " -0.196268 0.0508897 -1.02538 0.6103 0.0964332\n", " 0.311368 0.550423 0.0771399 -0.320978 -0.29254 \n", " -0.042317 0.0801359 0.0557964 0.924308 -0.484027 \n", " -0.946129 -1.88499 -0.715218 -0.538779 1.02781 \n", " -0.247229 0.746322 -2.80947 -0.659972 0.374257 \n", " -1.86926 0.858622 0.573011 -0.886174 -0.188567 " ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "M = [x y; z]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dot operation\n", "\n", "Dot operation in Julia is elementwise operation." ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " -0.37393 0.553298 -1.01762 \n", " 1.08906 0.990025 0.389225\n", " 0.193156 -2.01294 1.87758 \n", " 0.715318 0.143813 1.20991 \n", " -0.91177 -0.0733957 0.722246" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = randn(5, 3)" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 1.0 1.0 1.0\n", " 1.0 1.0 1.0\n", " 1.0 1.0 1.0\n", " 1.0 1.0 1.0\n", " 1.0 1.0 1.0" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = ones(5, 3)" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " -0.37393 0.553298 -1.01762 \n", " 1.08906 0.990025 0.389225\n", " 0.193156 -2.01294 1.87758 \n", " 0.715318 0.143813 1.20991 \n", " -0.91177 -0.0733957 0.722246" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x .* y # same x * y in R" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 7.15186 3.26649 0.965667\n", " 0.843133 1.02025 6.60081 \n", " 26.8031 0.246795 0.283664\n", " 1.95435 48.3509 0.683112\n", " 1.2029 185.635 1.91704 " ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x .^ (-2)" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " -0.365277 0.525496 -0.850861\n", " 0.886192 0.83604 0.379472\n", " 0.191957 -0.903835 0.953311\n", " 0.655858 0.143318 0.935585\n", " -0.790589 -0.0733298 0.661071" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sin.(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Basic linear algebra" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Float64,1}:\n", " 0.98846 \n", " -0.804474 \n", " 0.0199801\n", " -1.32858 \n", " 1.67995 " ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = randn(5)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.492388424401137" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# vector L2 norm\n", "vecnorm(x)" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.22692502538805792" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "y = randn(5) # another vector\n", "# dot product\n", "dot(x, y) # x' * y" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.22692502538805792" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x'y" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×2 Array{Float64,2}:\n", " 1.26739 -0.422013\n", " 1.07959 -1.25078 \n", " -0.870933 -2.30028 \n", " -0.788011 -0.927073\n", " -1.80023 1.44096 " ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x, y = randn(5, 3), randn(3, 2)\n", "# matrix multiplication, same as %*% in R\n", "x * y" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 Array{Float64,2}:\n", " -1.64764 0.689513 0.206809\n", " 0.488763 -0.668832 0.991291\n", " -0.482333 -0.726196 -0.145318" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = randn(3, 3)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3×3 Array{Float64,2}:\n", " -1.64764 0.488763 -0.482333\n", " 0.689513 -0.668832 -0.726196\n", " 0.206809 0.991291 -0.145318" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# conjugate transpose\n", "x'" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3-element Array{Float64,1}:\n", " -0.701955\n", " -0.153764\n", " 0.297224" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "b = rand(3)\n", "x'b # same as x' * b" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-2.4617868414631436" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "trace(x)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-1.7670510948981355" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "det(x)" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rank(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sparse matrices" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10×10 SparseMatrixCSC{Float64,Int64} with 10 stored entries:\n", " [5 , 1] = -0.185574\n", " [8 , 2] = 2.72401\n", " [9 , 2] = -0.396973\n", " [4 , 4] = 1.67252\n", " [1 , 5] = 0.178644\n", " [8 , 6] = -0.575279\n", " [2 , 7] = 0.870975\n", " [6 , 7] = -0.53665\n", " [10, 8] = -0.796616\n", " [2 , 10] = -1.10258" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# 10-by-10 sparse matrix with sparsity 0.1\n", "X = sprandn(10, 10, .1)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10×10 Array{Float64,2}:\n", " 0.0 0.0 0.0 0.0 … 0.0 0.0 0.0 0.0 \n", " 0.0 0.0 0.0 0.0 0.870975 0.0 0.0 -1.10258\n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", " 0.0 0.0 0.0 1.67252 0.0 0.0 0.0 0.0 \n", " -0.185574 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", " 0.0 0.0 0.0 0.0 … -0.53665 0.0 0.0 0.0 \n", " 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", " 0.0 2.72401 0.0 0.0 0.0 0.0 0.0 0.0 \n", " 0.0 -0.396973 0.0 0.0 0.0 0.0 0.0 0.0 \n", " 0.0 0.0 0.0 0.0 0.0 -0.796616 0.0 0.0 " ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# convert to dense matrix; be cautious when dealing with big data\n", "Xfull = full(X)" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10×10 SparseMatrixCSC{Float64,Int64} with 10 stored entries:\n", " [5 , 1] = -0.185574\n", " [8 , 2] = 2.72401\n", " [9 , 2] = -0.396973\n", " [4 , 4] = 1.67252\n", " [1 , 5] = 0.178644\n", " [8 , 6] = -0.575279\n", " [2 , 7] = 0.870975\n", " [6 , 7] = -0.53665\n", " [10, 8] = -0.796616\n", " [2 , 10] = -1.10258" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# convert a dense matrix to sparse matrix\n", "sparse(Xfull)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10-element Array{Float64,1}:\n", " 0.178644\n", " -0.231604\n", " 0.0 \n", " 1.67252 \n", " -0.185574\n", " -0.53665 \n", " 0.0 \n", " 2.14873 \n", " -0.396973\n", " -0.796616" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# syntax for sparse linear algebra is same as dense linear algebra\n", "β = ones(10)\n", "X * β" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10×1 Array{Float64,2}:\n", " 0.178644\n", " -0.231604\n", " 0.0 \n", " 1.67252 \n", " -0.185574\n", " -0.53665 \n", " 0.0 \n", " 2.14873 \n", " -0.396973\n", " -0.796616" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# many functions apply to sparse matrices as well\n", "sum(X, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Control flow and loops" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* if-elseif-else-end\n", "```julia\n", "if condition1\n", " # do something\n", "elseif condition2\n", " # do something\n", "else\n", " # do something\n", "end\n", "```\n", "\n", "* `for` loop\n", "```julia\n", "for i in 1:10\n", " println(i)\n", "end\n", "```\n", "\n", "* Nested `for` loop:\n", "```julia\n", "for i in 1:10\n", " for j in 1:5\n", " println(i * j)\n", " end\n", "end\n", "```\n", "Same as\n", "```julia\n", "for i in 1:10, j in 1:5\n", " println(i * j)\n", "end\n", "```\n", "\n", "* Exit loop:\n", "```julia\n", "for i in 1:10\n", " # do something\n", " if condition1\n", " exit # skip remaining loop\n", " end\n", "end\n", "```\n", "\n", "* Exit iteration: \n", "```julia\n", "for i in 1:10\n", " # do something\n", " if condition1\n", " continue # skip to next iteration\n", " end\n", " # do something\n", "end\n", "```" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Functions " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* In Julia, all arguments to functions are **passed by reference**, in contrast to R and Matlab.\n", "\n", "* Function names ending with `!` indicates that function mutates at least one argument, typically the first.\n", "```julia\n", "sort!(x) # vs sort(x)\n", "```\n", "\n", "* Function definition\n", "```julia\n", "function func(req1, req2; key1=dflt1, key2=dflt2)\n", " # do stuff\n", " return out1, out2, out3\n", "end\n", "```\n", "**Required arguments** are separated with a comma and use the positional notation. \n", "**Optional arguments** need a default value in the signature. \n", "**Semicolon** is not required in function call. \n", "**return** statement is optional. \n", "Multiple outputs can be returned as a **tuple**, e.g., `return out1, out2, out3`. \n", "\n", "* Anonymous functions, e.g., `x -> x^2`, is commonly used in collection function or list comprehensions.\n", "```julia\n", "map(x -> x^2, y) # square each element in x\n", "```\n", "\n", "* Functions can be nested:\n", "```julia\n", "function outerfunction()\n", " # do some outer stuff\n", " function innerfunction()\n", " # do inner stuff\n", " # can access prior outer definitions\n", " end\n", " # do more outer stuff\n", "end\n", "```\n", "\n", "* Functions can be vectorized using the Dot syntax:" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 0.179536 0.0661559 0.0298338\n", " 0.794468 0.139424 0.561255 \n", " 0.209101 0.294354 0.769808 \n", " 0.000414421 0.255897 0.351913 \n", " 0.021371 0.38493 0.663682 " ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function myfunc(x)\n", " return sin(x^2)\n", "end\n", "\n", "x = randn(5, 3)\n", "myfunc.(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* **Collection function** (think this as the series of `apply` functions in R).\n", "\n", " Apply a function to each element of a collection:\n", "```julia\n", "map(f, coll) # or\n", "map(coll) do elem\n", " # do stuff with elem\n", " # must contain return\n", "end\n", "```" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 0.179536 0.0661559 0.0298338\n", " 0.794468 0.139424 0.561255 \n", " 0.209101 0.294354 0.769808 \n", " 0.000414421 0.255897 0.351913 \n", " 0.021371 0.38493 0.663682 " ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "map(x -> sin(x^2), x)" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 0.179536 0.0661559 0.0298338\n", " 0.794468 0.139424 0.561255 \n", " 0.209101 0.294354 0.769808 \n", " 0.000414421 0.255897 0.351913 \n", " 0.021371 0.38493 0.663682 " ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "map(x) do elem\n", " elem = elem^2\n", " return sin(elem)\n", "end" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4.722142023166778" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Mapreduce\n", "mapreduce(x -> sin(x^2), +, x)" ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4.722142023166778" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# same as\n", "sum(x -> sin(x^2), x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* List **comprehension**" ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5×3 Array{Float64,2}:\n", " 0.14112 -0.756802 -0.958924\n", " -0.958924 -0.279415 0.656987\n", " 0.656987 0.989358 0.412118\n", " 0.412118 -0.544021 -0.99999 \n", " -0.99999 -0.536573 0.420167" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[sin(2i + j) for i in 1:5, j in 1:3] # similar to Python" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Type system\n", "\n", "* When thinking about types, think about sets.\n", "\n", "* Everything is a subtype of the abstract type `Any`.\n", "\n", "* An abstract type defines a set of types\n", " - Consider types in Julia that are a `Number`:\n", "\n", "\n", "\n", "* You can explore type hierarchy with `typeof()`, `supertype()`, and `subtypes()`." ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(Float64, Int64)" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "typeof(1.0), typeof(1)" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "AbstractFloat" ] }, "execution_count": 91, "metadata": {}, "output_type": "execute_result" } ], "source": [ "supertype(Float64)" ] }, { "cell_type": "code", "execution_count": 92, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "4-element Array{Union{DataType, UnionAll},1}:\n", " BigFloat\n", " Float16 \n", " Float32 \n", " Float64 " ] }, "execution_count": 92, "metadata": {}, "output_type": "execute_result" } ], "source": [ "subtypes(AbstractFloat)" ] }, { "cell_type": "code", "execution_count": 93, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 93, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Is Float64 a subtype of AbstractFloat?\n", "Float64 <: AbstractFloat" ] }, { "cell_type": "code", "execution_count": 94, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "true" ] }, "execution_count": 94, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# On 64bit machine, Int == Int64\n", "Int == Int64" ] }, { "cell_type": "code", "execution_count": 95, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1.0" ] }, "execution_count": 95, "metadata": {}, "output_type": "execute_result" } ], "source": [ "convert(Float64, 1)" ] }, { "cell_type": "code", "execution_count": 96, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Float32,1}:\n", " -0.510323\n", " -0.361734\n", " 0.719499\n", " 0.392724\n", " -0.15274 " ] }, "execution_count": 96, "metadata": {}, "output_type": "execute_result" } ], "source": [ "randn(Float32, 5)" ] }, { "cell_type": "code", "execution_count": 97, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Float64,1}:\n", " -1.00066 \n", " -0.852047\n", " 0.052183\n", " 0.935551\n", " -0.547745" ] }, "execution_count": 97, "metadata": {}, "output_type": "execute_result" } ], "source": [ "convert(Vector{Float64}, randn(Float32, 5))" ] }, { "cell_type": "code", "execution_count": 98, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 98, "metadata": {}, "output_type": "execute_result" } ], "source": [ "convert(Int, 1.0)" ] }, { "cell_type": "code", "execution_count": 99, "metadata": {}, "outputs": [ { "ename": "LoadError", "evalue": "\u001b[91mInexactError()\u001b[39m", "output_type": "error", "traceback": [ "\u001b[91mInexactError()\u001b[39m", "", "Stacktrace:", " [1] \u001b[1mconvert\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::Type{Int64}, ::Float64\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./float.jl:679\u001b[22m\u001b[22m", " [2] \u001b[1minclude_string\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::String, ::String\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./loading.jl:522\u001b[22m\u001b[22m" ] } ], "source": [ "convert(Int, 1.5) # should use round(1.5)" ] }, { "cell_type": "code", "execution_count": 100, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 100, "metadata": {}, "output_type": "execute_result" } ], "source": [ "round(Int, 1.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multiple dispatch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Multiple dispatch lies in the core of Julia design. It allows built-in and user-defined functions to be overloaded for different combinations of argument types.\n", "\n", "* Let's consider a simple \"doubling\" function:" ] }, { "cell_type": "code", "execution_count": 101, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "g (generic function with 1 method)" ] }, "execution_count": 101, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(x) = x + x" ] }, { "cell_type": "code", "execution_count": 102, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.0" ] }, "execution_count": 102, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(1.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This definition is too broad, since some things can't be added " ] }, { "cell_type": "code", "execution_count": 103, "metadata": {}, "outputs": [ { "ename": "LoadError", "evalue": "\u001b[91mMethodError: no method matching +(::String, ::String)\u001b[0m\nClosest candidates are:\n +(::Any, ::Any, \u001b[91m::Any\u001b[39m, \u001b[91m::Any...\u001b[39m) at operators.jl:424\u001b[39m", "output_type": "error", "traceback": [ "\u001b[91mMethodError: no method matching +(::String, ::String)\u001b[0m\nClosest candidates are:\n +(::Any, ::Any, \u001b[91m::Any\u001b[39m, \u001b[91m::Any...\u001b[39m) at operators.jl:424\u001b[39m", "", "Stacktrace:", " [1] \u001b[1mg\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::String\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./In[101]:1\u001b[22m\u001b[22m", " [2] \u001b[1minclude_string\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::String, ::String\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./loading.jl:522\u001b[22m\u001b[22m" ] } ], "source": [ "g(\"hello world\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* This definition is correct but too restrictive, since any `Number` can be added." ] }, { "cell_type": "code", "execution_count": 104, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "g (generic function with 2 methods)" ] }, "execution_count": 104, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(x::Float64) = x + x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* This will automatically work on the entire type tree above!" ] }, { "cell_type": "code", "execution_count": 105, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "g (generic function with 3 methods)" ] }, "execution_count": 105, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(x::Number) = x + x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is a lot nicer than \n", "```julia\n", "function g(x)\n", " if isa(x, Number)\n", " return x + x\n", " else\n", " throw(ArgumentError(\"x should be a number\"))\n", " end\n", "end\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `methods(func)` function display all methods defined for `func`." ] }, { "cell_type": "code", "execution_count": 106, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3 methods for generic function g:
  • g(x::Float64) at In[104]:1
  • g(x::Number) at In[105]:1
  • g(x) at In[101]:1
" ], "text/plain": [ "# 3 methods for generic function \"g\":\n", "g(x::Float64) in Main at In[104]:1\n", "g(x::Number) in Main at In[105]:1\n", "g(x) in Main at In[101]:1" ] }, "execution_count": 106, "metadata": {}, "output_type": "execute_result" } ], "source": [ "methods(g)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `@which func(x)` marco tells which method is being used for argument signature `x`." ] }, { "cell_type": "code", "execution_count": 107, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Int64" ] }, "execution_count": 107, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = 1\n", "typeof(x)" ] }, { "cell_type": "code", "execution_count": 108, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 108, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(x)" ] }, { "cell_type": "code", "execution_count": 109, "metadata": {}, "outputs": [ { "data": { "text/html": [ "g(x::Number) at In[105]:1" ], "text/plain": [ "g(x::Number) in Main at In[105]:1" ] }, "execution_count": 109, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@which g(x)" ] }, { "cell_type": "code", "execution_count": 110, "metadata": {}, "outputs": [ { "data": { "text/html": [ "g(x) at In[101]:1" ], "text/plain": [ "g(x) in Main at In[101]:1" ] }, "execution_count": 110, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x = randn(5)\n", "@which g(x)" ] }, { "cell_type": "code", "execution_count": 111, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5-element Array{Float64,1}:\n", " 3.90284 \n", " 2.33685 \n", " -0.460847\n", " -0.411265\n", " 0.775717" ] }, "execution_count": 111, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(x)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Just-in-time compilation (JIT)\n", "\n", "Following figures and some examples are taken from Arch D. Robinson's slides [Introduction to Writing High Performance Julia](https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxibG9uem9uaWNzfGd4OjMwZjI2YTYzNDNmY2UzMmE).\n", "\n", "| \"Julia | \"Julia |\n", "|----------------------------------|------------------------------------|\n", "|||" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* `Julia`'s efficiency results from its capabilities to infer the types of **all** variables within a function and then call LLVM to generate optimized machine code at run-time. " ] }, { "cell_type": "code", "execution_count": 112, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "g (generic function with 1 method)" ] }, "execution_count": 112, "metadata": {}, "output_type": "execute_result" } ], "source": [ "workspace() # clear previous definition of g\n", "g(x::Number) = x + x" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This function will work on **any** type which has a method for `+`." ] }, { "cell_type": "code", "execution_count": 113, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "g(2) = 4\n", "g(2.0) = 4.0\n" ] } ], "source": [ "@show g(2)\n", "@show g(2.0);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is the [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree)." ] }, { "cell_type": "code", "execution_count": 114, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "CodeInfo(:(begin \n", " nothing\n", " return x + x\n", " end))" ] }, "execution_count": 114, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@code_lowered g(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Type inference:" ] }, { "cell_type": "code", "execution_count": 115, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Variables:\n", " #self# \n", " x::Int64\n", "\n", "Body:\n", " begin \n", " return (Base.add_int)(x::Int64, x::Int64)::Int64\n", " end::Int64\n" ] } ], "source": [ "@code_warntype g(2)" ] }, { "cell_type": "code", "execution_count": 116, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Variables:\n", " #self# \n", " x::Float64\n", "\n", "Body:\n", " begin \n", " return (Base.add_float)(x::Float64, x::Float64)::Float64\n", " end::Float64\n" ] } ], "source": [ "@code_warntype g(2.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Peek at the compiled **LLVM bitcode** with `@code_llvm`" ] }, { "cell_type": "code", "execution_count": 117, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "define i64 @julia_g_64864(i64) #0 !dbg !5 {\n", "top:\n", " %1 = shl i64 %0, 1\n", " ret i64 %1\n", "}\n" ] } ], "source": [ "@code_llvm g(2)" ] }, { "cell_type": "code", "execution_count": 118, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "define double @julia_g_64870(double) #0 !dbg !5 {\n", "top:\n", " %1 = fadd double %0, %0\n", " ret double %1\n", "}\n" ] } ], "source": [ "@code_llvm g(2.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We didn't provide a type annotation. But different LLVM code gets generated depending on the argument type!\n", "\n", "* In R or Python, `g(2)` and `g(2.0)` would use the same code for both.\n", " \n", "* In Julia, `g(2)` and `g(2.0)` dispatches to optimized code for `Int64` and `Float64`, respectively.\n", "\n", "* For integer input `x`, LLVM compiler is smart enough to know `x + x` is simple shifting `x` by 1 bit, which is faster than addition.\n", " \n", "Lowest level is the **assembly code**, which is machine dependent." ] }, { "cell_type": "code", "execution_count": 119, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\t.section\t__TEXT,__text,regular,pure_instructions\n", "Filename: In[112]\n", "\tpushq\t%rbp\n", "\tmovq\t%rsp, %rbp\n", "Source line: 2\n", "\tleaq\t(%rdi,%rdi), %rax\n", "\tpopq\t%rbp\n", "\tretq\n", "\tnopw\t(%rax,%rax)\n" ] } ], "source": [ "@code_native g(2)" ] }, { "cell_type": "code", "execution_count": 120, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\t.section\t__TEXT,__text,regular,pure_instructions\n", "Filename: In[112]\n", "\tpushq\t%rbp\n", "\tmovq\t%rsp, %rbp\n", "Source line: 2\n", "\taddsd\t%xmm0, %xmm0\n", "\tpopq\t%rbp\n", "\tretq\n", "\tnopw\t(%rax,%rax)\n" ] } ], "source": [ "@code_native g(2.0)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Profiling Julia code\n", "\n", "Julia has several built-in tools for profiling. The `@time` marco outputs run time and heap allocation." ] }, { "cell_type": "code", "execution_count": 121, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.011205 seconds (31.33 k allocations: 538.291 KiB)\n" ] }, { "data": { "text/plain": [ "4981.321489638115" ] }, "execution_count": 121, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function tally(x)\n", " s = 0\n", " for v in x\n", " s += v\n", " end\n", " s\n", "end\n", "\n", "a = rand(10000)\n", "@time tally(a) # first run: include compile time" ] }, { "cell_type": "code", "execution_count": 122, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0.000253 seconds (30.00 k allocations: 468.906 KiB)\n" ] }, { "data": { "text/plain": [ "4981.321489638115" ] }, "execution_count": 122, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@time tally(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more robust benchmarking, the [BenchmarkTools.jl](https://github.com/JuliaCI/BenchmarkTools.jl) package is highly recommended." ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "WARNING: Method definition endof(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.\n", "WARNING: Method definition ==(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:880 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:880.\n", "WARNING: Method definition get(Union{Function, Type{T} where T}, Main.Base.EnvHash, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1036 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1036.\n", "WARNING: Method definition ceil(Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:734 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:734.\n", "WARNING: Method definition ceil(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Type{P<:Main.Base.Dates.Period}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:789 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:789.\n", "WARNING: Method definition in(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:889 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:889.\n", "WARNING: Method definition #cov(Array{Any, 1}, typeof(Main.Base.cov), AbstractArray{T, 1} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:530 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:530.\n", "WARNING: Method definition #cov(Array{Any, 1}, typeof(Main.Base.cov), AbstractArray{T, 1} where T, AbstractArray{T, 1} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:531 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:531.\n", "WARNING: Method definition #cov(Array{Any, 1}, typeof(Main.Base.cov), AbstractArray{T, 2} where T, Int64) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:534 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:534.\n", "WARNING: Method definition #cov(Array{Any, 1}, typeof(Main.Base.cov), Union{AbstractArray{T, 1}, AbstractArray{T, 2}} where T, Union{AbstractArray{T, 1}, AbstractArray{T, 2}} where T, Int64) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:537 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:537.\n", "WARNING: Method definition length(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.\n", "WARNING: Method definition findlast(AbstractString, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1563 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1563.\n", "WARNING: Method definition done(Main.Base.Cmd, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419.\n", "WARNING: Method definition _redirect_stdin(IO) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18.\n", "WARNING: Method definition findfirst(Main.Base.Regex, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1532 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1532.\n", "WARNING: Method definition findfirst(AbstractString, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1547 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1547.\n", "WARNING: Method definition #replace(Array{Any, 1}, typeof(Main.Base.replace), AbstractString, Main.Base.Pair{A, B} where B where A) in module Compat overwritten in module Compat.\n", "WARNING: Method definition _redirect_stdout(IO) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18.\n", "WARNING: Method definition spdiagm(Main.Base.Pair{A, B} where B where A...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:942 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:942.\n", "WARNING: Method definition _redirect_stderr(IO) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:18.\n", "WARNING: Method definition getindex(Main.Base.Cmd, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419.\n", "WARNING: Method definition convert(Type{Void}, Void) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1145 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1145.\n", "WARNING: Method definition convert(Type{Void}, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1144 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1144.\n", "WARNING: Method definition convert(Type{Union{T, Void}}, Any) in module Compat at string:8 overwritten in module Compat at string:8.\n", "WARNING: Method definition include_string(Module, String, String) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:71 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:71.\n", "WARNING: Method definition include_string(Module, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:73 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:73.\n", "WARNING: Method definition include_string(Module, AbstractString, AbstractString) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:73 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:73.\n", "WARNING: Method definition start(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.\n", "WARNING: Method definition expand(Module, ANY) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:69 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:69.\n", "WARNING: Method definition floor(Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, T<:Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:705 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:705.\n", "WARNING: Method definition floor(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Type{P<:Main.Base.Dates.Period}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:788 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:788.\n", "WARNING: Method definition ntuple(F, Main.Base.Val{N}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:445 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:445.\n", "WARNING: Method definition reshape(AbstractArray{T, N} where N where T, Main.Base.Val{N}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:443 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:443.\n", "WARNING: Method definition values(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:614 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:614.\n", "WARNING: Method definition findnext(Main.Base.Regex, AbstractString, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1531 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1531.\n", "WARNING: Method definition findnext(AbstractString, AbstractString, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1546 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1546.\n", "WARNING: Method definition contains(AbstractString, Main.Base.Regex) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1197 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1197.\n", "WARNING: Method definition repr(Union{AbstractString, Main.Base.MIME{mime} where mime}, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1576 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1576.\n", "WARNING: Method definition eachindex(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.\n", "WARNING: Method definition macroexpand(Module, ANY) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:70 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:70.\n", "WARNING: Method definition eltype(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.\n", "WARNING: Method definition keys(AbstractArray{T, 1} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:611 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:611.\n", "WARNING: Method definition keys(AbstractArray{T, N} where N where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:610 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:610.\n", "WARNING: Method definition keys(Main.Base.IndexStyle, AbstractArray{T, N} where N where T, AbstractArray{T, N} where N where T...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:612 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:612.\n", "WARNING: Method definition read(Main.Base.Cmd, Type{String}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:493 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:493.\n", "WARNING: Method definition read(AbstractString, Type{String}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:492 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:492.\n", "WARNING: Method definition read(IO, Type{String}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:491 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:491.\n", "WARNING: Method definition first(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.\n", "WARNING: Method definition next(Main.Base.Cmd, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:419.\n", "WARNING: Method definition replace(AbstractString, Main.Base.Pair{A, B} where B where A) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1184 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1184.\n", "WARNING: Method definition rtoldefault(Any, Any, Real) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:625 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:625.\n", "WARNING: Method definition round(Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Union{Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Rounding.RoundingMode{:NearestTiesUp}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:778 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:778.\n", "WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Dates.Period) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:787 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:787.\n", "WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Dates.Period, Main.Base.Rounding.RoundingMode{:Down}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:783 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:783.\n", "WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Dates.Period, Main.Base.Rounding.RoundingMode{:Up}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:784 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:784.\n", "WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Main.Base.Dates.Period, Main.Base.Rounding.RoundingMode{T} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:786 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:786.\n", "WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Type{P<:Main.Base.Dates.Period}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:791 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:791.\n", "WARNING: Method definition round(Union{Main.Base.Dates.TimeType, Main.Base.Dates.TimePeriod, Main.Base.Dates.Week, Main.Base.Dates.Day}, Type{P<:Main.Base.Dates.Period}, Main.Base.Rounding.RoundingMode{T} where T) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:791 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:791.\n", "WARNING: Method definition last(Main.Base.Cmd) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:416.\n", "WARNING: Method definition isequal(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:884 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:884.\n", "WARNING: Method definition *(Union{Char, AbstractString}, Union{Char, AbstractString}...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:929 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:929.\n", "WARNING: Method definition findprev(AbstractString, AbstractString, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1562 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1562.\n", "WARNING: Method definition diagm(Main.Base.Pair{A, B} where B where A...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:952 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:952.\n", "WARNING: Method definition logdet(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:451 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:451.\n", "WARNING: Method definition chol(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Any...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:458 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:458.\n", "WARNING: Method definition chol!(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:457 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:457.\n", "WARNING: Method definition (::Type{DomainError})(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:503 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:503.\n", "WARNING: Method definition (::Type{DomainError})(Any, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:504 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:504.\n", "WARNING: Method definition (::Type{OverflowError})(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:509 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:509.\n", "WARNING: Method definition (::Type{InexactError})(Symbol, Any, Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:498 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:498.\n", "WARNING: Method definition (::Type{Main.Base.IOContext{IO_t} where IO_t<:IO})(Main.Base.IOContext{IO_t} where IO_t<:IO, Main.Base.Pair{A, B} where B where A, Main.Base.Pair{A, B} where B where A) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1003 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1003.\n", "WARNING: Method definition (::Type{Main.Base.IOContext{IO_t} where IO_t<:IO})(IO, Main.Base.Pair{A, B} where B where A, Main.Base.Pair{A, B} where B where A, Main.Base.Pair{A, B} where B where A...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1001 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:1001.\n", "WARNING: Method definition (::Type{Main.Base.Val{T} where T})(Any) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:440 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:440.\n", "WARNING: Method definition (::Type{Array{T, 2}})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Tuple{Int64, Int64}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:973 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:973.\n", "WARNING: Method definition (::Type{Array{T, 2}})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Integer, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:974 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:974.\n", "WARNING: Method definition (::Type{Main.Base.SparseArrays.SparseMatrixCSC{Tv, Ti} where Ti<:Integer})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Integer, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:977 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:977.\n", "WARNING: Method definition (::Type{Main.Base.SparseArrays.SparseMatrixCSC{Tv, Ti}})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Integer, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:976 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:976.\n", "WARNING: Method definition (::Type{Main.Base.SparseArrays.SparseMatrixCSC{Tv, Ti} where Ti<:Integer})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Tuple{Int64, Int64}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:978 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:978.\n", "WARNING: Method definition (::Type{Main.Base.SparseArrays.SparseMatrixCSC{Tv, Ti}})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Tuple{Int64, Int64}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:980 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:980.\n", "WARNING: Method definition (::Type{Array{T, N} where N})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Tuple{Int64, Int64}) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:992 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:992.\n", "WARNING: Method definition (::Type{Array{T, N} where N})(Main.Base.LinAlg.UniformScaling{T} where T<:Number, Integer, Integer) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:993 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:993.\n", "WARNING: Method definition (::Type{Array{T, 2} where T})(Main.Base.LinAlg.UniformScaling{T}, Any...) in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:996 overwritten in module Compat at /Users/huazhou/.julia/v0.6/Compat/src/Compat.jl:996.\n" ] }, { "data": { "text/plain": [ "BenchmarkTools.Trial: \n", " memory estimate: 468.75 KiB\n", " allocs estimate: 30000\n", " --------------\n", " minimum time: 174.517 μs (0.00% GC)\n", " median time: 181.316 μs (0.00% GC)\n", " mean time: 210.472 μs (8.43% GC)\n", " maximum time: 1.517 ms (78.50% GC)\n", " --------------\n", " samples: 10000\n", " evals/sample: 1" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using BenchmarkTools\n", "\n", "@benchmark tally(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see the memory allocation (468.75 KiB, average 10.73% GC) is suspiciously high.\n", "\n", "The `Profile` module gives line by line profile results." ] }, { "cell_type": "code", "execution_count": 124, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Count File Line Function \n", " 2 ./abstractarray.jl 573 copy!(::Array{Any,1}, ::Core.Infere... \n", " 2 ./array.jl 396 _collect(::Type{Any}, ::Core.Infere... \n", " 2 ./array.jl 393 collect(::Type{Any}, ::Core.Inferen... \n", " 2 ./generator.jl 45 next(::Core.Inference.Generator{Arr... \n", " 1 ./inference.jl 4224 #198 \n", " 2 ./inference.jl 1897 abstract_call(::Any, ::Array{Any,1}... \n", " 2 ./inference.jl 1420 abstract_call_gf_by_type(::Any, ::A... \n", " 4 ./inference.jl 1950 abstract_eval(::Any, ::Array{Any,1}... \n", " 2 ./inference.jl 1901 abstract_eval_call(::Expr, ::Array{... \n", " 2 ./inference.jl 1927 abstract_eval_call(::Expr, ::Array{... \n", " 1 ./inference.jl 4224 inline_worthy(::Expr, ::Int64) \n", " 1 ./inference.jl 2885 isinlineable(::Method, ::CodeInfo) \n", " 3 ./inference.jl 3290 occurs_more(::Any, ::Core.Inference... \n", " 1 ./inference.jl 3297 occurs_more(::Any, ::Core.Inference... \n", " 1 ./inference.jl 2963 optimize(::Core.Inference.Inference... \n", " 2 ./inference.jl 2787 typeinf(::Core.Inference.InferenceS... \n", " 1 ./inference.jl 2816 typeinf(::Core.Inference.InferenceS... \n", " 1 ./inference.jl 2583 typeinf_code(::Core.MethodInstance,... \n", " 2 ./inference.jl 2535 typeinf_edge(::Method, ::Any, ::Sim... \n", " 1 ./inference.jl 2622 typeinf_ext(::Core.MethodInstance, ... \n", " 1 ./inference.jl 2504 typeinf_frame(::Core.MethodInstance... \n", " 2 ./inference.jl 2722 typeinf_work(::Core.Inference.Infer... \n", " 1 ./loading.jl 522 include_string(::String, ::String) \n", " 1 ./task.jl 335 (::LastMain.IJulia.##14#17)() \n", " 1 ....6/Compat/src/Compat.jl 71 include_string(::Module, ::String, ... \n", " 1 ....6/Compat/src/Compat.jl 385 (::LastMain.Compat.#inner#17{Array{... \n", " 1 ...IJulia/src/eventloop.jl 8 eventloop(::LastMain.ZMQ.Socket) \n", " 1 .../src/execute_request.jl 158 execute_request(::LastMain.ZMQ.Sock... \n" ] } ], "source": [ "Profile.clear()\n", "@profile tally(a)\n", "Profile.print(format=:flat)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One can use [`ProfileView`](https://github.com/timholy/ProfileView.jl) package for better visualization of profile data:\n", "\n", "```julia\n", "using ProfileView\n", "\n", "ProfileView.view()\n", "```" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Variables:\n", " #self# \n", " x::Array{Float64,1}\n", " v::Float64\n", " #temp#@_4::Int64\n", " s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m\n", " #temp#@_6::Core.MethodInstance\n", " #temp#@_7::Float64\n", "\n", "Body:\n", " begin \n", " s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m = 0 # line 3:\n", " #temp#@_4::Int64 = 1\n", " 4: \n", " unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)((Base.arraylen)(x::Array{Float64,1})::Int64, 1)::Int64)::Bool)::Bool goto 29\n", " SSAValue(2) = (Base.arrayref)(x::Array{Float64,1}, #temp#@_4::Int64)::Float64\n", " SSAValue(3) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64\n", " v::Float64 = SSAValue(2)\n", " #temp#@_4::Int64 = SSAValue(3) # line 4:\n", " unless (s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m isa Int64)::Bool goto 14\n", " #temp#@_6::Core.MethodInstance = MethodInstance for +(::Int64, ::Float64)\n", " goto 23\n", " 14: \n", " unless (s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m isa Float64)::Bool goto 18\n", " #temp#@_6::Core.MethodInstance = MethodInstance for +(::Float64, ::Float64)\n", " goto 23\n", " 18: \n", " goto 20\n", " 20: \n", " #temp#@_7::Float64 = (s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m + v::Float64)::Float64\n", " goto 25\n", " 23: \n", " #temp#@_7::Float64 = $(Expr(:invoke, :(#temp#@_6), :(Main.+), :(s), :(v)))\n", " 25: \n", " s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m = #temp#@_7::Float64\n", " 27: \n", " goto 4\n", " 29: # line 6:\n", " return s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m\n", " end\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m\n" ] } ], "source": [ "@code_warntype tally(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Memory profiling\n", "\n", "Detailed memory profiling requires a detour. First let's write a script [`bar.jl`](./bar.jl), which contains the workload function `tally` and a wrapper for profiling." ] }, { "cell_type": "code", "execution_count": 126, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "function tally(x)\n", " s = 0\n", " for v in x\n", " s += v\n", " end\n", " s\n", "end\n", "\n", "# call workload from wrapper to avoid misattribution bug\n", "function wrapper()\n", " y = rand(10000)\n", " # force compilation\n", " println(tally(y))\n", " # clear allocation counters\n", " Profile.clear_malloc_data()\n", " # run compiled workload\n", " println(tally(y))\n", "end\n", "\n", "wrapper()\n" ] } ], "source": [ ";cat bar.jl" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, in terminal, we run the script with `--track-allocation=user` option." ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "5022.074157476412\n", "5022.074157476412\n" ] } ], "source": [ ";julia --track-allocation=user bar.jl" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The profiler outputs a file `bar.jl.mem`." ] }, { "cell_type": "code", "execution_count": 128, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " - function tally(x)\n", " 0 s = 0\n", " 0 for v in x\n", " 479984 s += v\n", " - end\n", " 0 s\n", " - end\n", " - \n", " - # call workload from wrapper to avoid misattribution bug\n", " - function wrapper()\n", " 0 y = rand(10000)\n", " - # force compilation\n", " 0 println(tally(y))\n", " - # clear allocation counters\n", " 0 Profile.clear_malloc_data()\n", " - # run compiled workload\n", " 192 println(tally(y))\n", " - end\n", " - \n", " - wrapper()\n", " - \n" ] } ], "source": [ ";cat bar.jl.mem" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see line 4 is allocating suspicious amount of heap memory. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Type stability\n", "\n", "The key to writing performant Julia code is to be [**type stable**](https://docs.julialang.org/en/stable/manual/performance-tips/#Write-\"type-stable\"-functions-1), such that `Julia` is able to infer types of all variables and output of a function from the types of input arguments. \n", "\n", "Is the `tally` function type stable? How to diagnose and fix it?" ] }, { "cell_type": "code", "execution_count": 129, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Variables:\n", " #self# \n", " x::Array{Float64,1}\n", " v::Float64\n", " #temp#@_4::Int64\n", " s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m\n", " #temp#@_6::Core.MethodInstance\n", " #temp#@_7::Float64\n", "\n", "Body:\n", " begin \n", " s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m = 0 # line 3:\n", " #temp#@_4::Int64 = 1\n", " 4: \n", " unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)((Base.arraylen)(x::Array{Float64,1})::Int64, 1)::Int64)::Bool)::Bool goto 29\n", " SSAValue(2) = (Base.arrayref)(x::Array{Float64,1}, #temp#@_4::Int64)::Float64\n", " SSAValue(3) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64\n", " v::Float64 = SSAValue(2)\n", " #temp#@_4::Int64 = SSAValue(3) # line 4:\n", " unless (s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m isa Int64)::Bool goto 14\n", " #temp#@_6::Core.MethodInstance = MethodInstance for +(::Int64, ::Float64)\n", " goto 23\n", " 14: \n", " unless (s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m isa Float64)::Bool goto 18\n", " #temp#@_6::Core.MethodInstance = MethodInstance for +(::Float64, ::Float64)\n", " goto 23\n", " 18: \n", " goto 20\n", " 20: \n", " #temp#@_7::Float64 = (s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m + v::Float64)::Float64\n", " goto 25\n", " 23: \n", " #temp#@_7::Float64 = $(Expr(:invoke, :(#temp#@_6), :(Main.+), :(s), :(v)))\n", " 25: \n", " s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m = #temp#@_7::Float64\n", " 27: \n", " goto 4\n", " 29: # line 6:\n", " return s\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m\n", " end\u001b[1m\u001b[91m::Union{Float64, Int64}\u001b[39m\u001b[22m\n" ] } ], "source": [ "@code_warntype tally(rand(100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, Julia fails to infer the type of the reduction variable `s`, which has to be **boxed** in heap memory at run time.\n", "\n", "\n", "\n", "\n", "\n", "This is the generated LLVM bitcode, which is unsually long and contains lots of _box_:" ] }, { "cell_type": "code", "execution_count": 130, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "define { i8**, i8 } @julia_tally_64973([8 x i8]* noalias nocapture, i8** dereferenceable(40)) #0 !dbg !5 {\n", "top:\n", " %2 = call i8**** @jl_get_ptls_states() #5\n", " %3 = alloca [8 x i8**], align 8\n", " %.sub = getelementptr inbounds [8 x i8**], [8 x i8**]* %3, i64 0, i64 0\n", " %4 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 5\n", " %5 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 2\n", " %6 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 4\n", " %7 = bitcast i8*** %4 to i8*\n", " call void @llvm.memset.p0i8.i32(i8* %7, i8 0, i32 24, i32 8, i1 false)\n", " %8 = bitcast [8 x i8**]* %3 to i64*\n", " %9 = bitcast i8*** %5 to i8*\n", " call void @llvm.memset.p0i8.i64(i8* %9, i8 0, i64 16, i32 8, i1 false)\n", " store i64 12, i64* %8, align 8\n", " %10 = bitcast i8**** %2 to i64*\n", " %11 = load i64, i64* %10, align 8\n", " %12 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 1\n", " %13 = bitcast i8*** %12 to i64*\n", " store i64 %11, i64* %13, align 8\n", " store i8*** %.sub, i8**** %2, align 8\n", " store i8** null, i8*** %6, align 8\n", " %14 = getelementptr inbounds i8*, i8** %1, i64 1\n", " %15 = bitcast i8** %14 to i64*\n", " %16 = load i64, i64* %15, align 8\n", " %17 = icmp eq i64 %16, 0\n", " br i1 %17, label %L29, label %if.lr.ph\n", "\n", "if.lr.ph: ; preds = %top\n", " %18 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 3\n", " %19 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 7\n", " %20 = getelementptr [8 x i8**], [8 x i8**]* %3, i64 0, i64 6\n", " %21 = getelementptr i8*, i8** %1, i64 3\n", " %22 = bitcast i8** %21 to i64*\n", " %23 = bitcast i8** %1 to double**\n", " %24 = bitcast i8**** %2 to i8*\n", " br label %if\n", "\n", "if: ; preds = %if.lr.ph, %L25\n", " %.033 = phi i8 [ 1, %if.lr.ph ], [ 2, %L25 ]\n", " %\"#temp#.032\" = phi i64 [ 1, %if.lr.ph ], [ %40, %L25 ]\n", " %s.sroa.0.031 = phi i64 [ 0, %if.lr.ph ], [ %\"#temp#2.sroa.0.0\", %L25 ]\n", " %25 = add i64 %\"#temp#.032\", -1\n", " %26 = load i64, i64* %22, align 8\n", " %27 = icmp ult i64 %25, %26\n", " br i1 %27, label %idxend, label %oob\n", "\n", "L29.loopexit: ; preds = %L25\n", " br label %L29\n", "\n", "L29: ; preds = %L29.loopexit, %top\n", " %s.sroa.0.0.lcssa = phi i64 [ 0, %top ], [ %\"#temp#2.sroa.0.0\", %L29.loopexit ]\n", " %.0.lcssa = phi i8 [ 1, %top ], [ 2, %L29.loopexit ]\n", " %trunc16 = trunc i8 %.0.lcssa to i2\n", " switch i2 %trunc16, label %union_move_skip11 [\n", " i2 1, label %union_move13\n", " i2 -2, label %union_move14\n", " ]\n", "\n", "oob: ; preds = %if\n", " %28 = alloca i64, align 8\n", " store i64 %\"#temp#.032\", i64* %28, align 8\n", " call void @jl_bounds_error_ints(i8** nonnull %1, i64* nonnull %28, i64 1)\n", " unreachable\n", "\n", "idxend: ; preds = %if\n", " %29 = load double*, double** %23, align 8\n", " %30 = getelementptr double, double* %29, i64 %25\n", " %31 = bitcast double* %30 to i64*\n", " %32 = load i64, i64* %31, align 8\n", " %33 = icmp eq i8 %.033, 1\n", " %. = select i1 %33, i8** inttoptr (i64 4780560272 to i8**), i8** inttoptr (i64 4459704976 to i8**)\n", " store i8** %., i8*** %5, align 8\n", " store i8** inttoptr (i64 4417465624 to i8**), i8*** %4, align 8\n", " %trunc = trunc i8 %.033 to i2\n", " switch i2 %trunc, label %box_union_isboxed [\n", " i2 1, label %box_union\n", " i2 -2, label %box_union4\n", " ]\n", "\n", "box_union_isboxed: ; preds = %idxend\n", " call void @llvm.trap()\n", " unreachable\n", "\n", "box_union: ; preds = %idxend\n", " %34 = call i8** @jl_box_int64(i64 signext %s.sroa.0.031)\n", " br label %L25\n", "\n", "box_union4: ; preds = %idxend\n", " %35 = call i8** @jl_gc_pool_alloc(i8* %24, i32 1384, i32 16)\n", " %36 = getelementptr i8*, i8** %35, i64 -1\n", " %37 = bitcast i8** %36 to i8***\n", " store i8** inttoptr (i64 4417870416 to i8**), i8*** %37, align 8\n", " %38 = bitcast i8** %35 to i64*\n", " store i64 %s.sroa.0.031, i64* %38, align 8\n", " br label %L25\n", "\n", "L25: ; preds = %box_union, %box_union4\n", " %39 = phi i8** [ %34, %box_union ], [ %35, %box_union4 ]\n", " %40 = add i64 %\"#temp#.032\", 1\n", " store i8** %39, i8*** %20, align 8\n", " %41 = call i8** @jl_gc_pool_alloc(i8* %24, i32 1384, i32 16)\n", " %42 = getelementptr i8*, i8** %41, i64 -1\n", " %43 = bitcast i8** %42 to i8***\n", " store i8** inttoptr (i64 4417870416 to i8**), i8*** %43, align 8\n", " %44 = bitcast i8** %41 to i64*\n", " store i64 %32, i64* %44, align 8\n", " store i8** %41, i8*** %19, align 8\n", " %45 = call i8** @jl_invoke(i8** %., i8*** %4, i32 3)\n", " store i8** %45, i8*** %18, align 8\n", " %\"#temp#2.sroa.0.0.in\" = bitcast i8** %45 to i64*\n", " %\"#temp#2.sroa.0.0\" = load i64, i64* %\"#temp#2.sroa.0.0.in\", align 8\n", " %46 = load i64, i64* %15, align 8\n", " %47 = icmp eq i64 %\"#temp#.032\", %46\n", " br i1 %47, label %L29.loopexit, label %if\n", "\n", "union_move_skip11: ; preds = %L29\n", " call void @llvm.trap()\n", " unreachable\n", "\n", "post_union_move12: ; preds = %union_move14, %union_move13\n", " %48 = bitcast [8 x i8]* %0 to i8**\n", " %49 = insertvalue { i8**, i8 } undef, i8** %48, 0\n", " %50 = insertvalue { i8**, i8 } %49, i8 %.0.lcssa, 1\n", " %51 = load i64, i64* %13, align 8\n", " store i64 %51, i64* %10, align 8\n", " ret { i8**, i8 } %50\n", "\n", "union_move13: ; preds = %L29\n", " %52 = bitcast [8 x i8]* %0 to i64*\n", " store i64 %s.sroa.0.0.lcssa, i64* %52, align 1\n", " br label %post_union_move12\n", "\n", "union_move14: ; preds = %L29\n", " %53 = bitcast [8 x i8]* %0 to i64*\n", " store i64 %s.sroa.0.0.lcssa, i64* %53, align 1\n", " br label %post_union_move12\n", "}\n" ] } ], "source": [ "@code_llvm tally(rand(100))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What's the fix?" ] }, { "cell_type": "code", "execution_count": 131, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "tally2 (generic function with 1 method)" ] }, "execution_count": 131, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function tally2(x)\n", " s = zero(eltype(x))\n", " for v in x\n", " s += v\n", " end\n", " s\n", "end" ] }, { "cell_type": "code", "execution_count": 132, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "BenchmarkTools.Trial: \n", " memory estimate: 16 bytes\n", " allocs estimate: 1\n", " --------------\n", " minimum time: 10.588 μs (0.00% GC)\n", " median time: 10.639 μs (0.00% GC)\n", " mean time: 11.652 μs (0.00% GC)\n", " maximum time: 38.698 μs (0.00% GC)\n", " --------------\n", " samples: 10000\n", " evals/sample: 1" ] }, "execution_count": 132, "metadata": {}, "output_type": "execute_result" } ], "source": [ "@benchmark tally2(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Much shorter LLVM bitcode:" ] }, { "cell_type": "code", "execution_count": 133, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "define double @julia_tally2_65300(i8** dereferenceable(40)) #0 !dbg !5 {\n", "top:\n", " %1 = getelementptr inbounds i8*, i8** %0, i64 1\n", " %2 = bitcast i8** %1 to i64*\n", " %3 = load i64, i64* %2, align 8\n", " %4 = icmp eq i64 %3, 0\n", " br i1 %4, label %L14, label %if.lr.ph\n", "\n", "if.lr.ph: ; preds = %top\n", " %5 = getelementptr i8*, i8** %0, i64 3\n", " %6 = bitcast i8** %5 to i64*\n", " %7 = load i64, i64* %6, align 8\n", " %8 = bitcast i8** %0 to double**\n", " %9 = load double*, double** %8, align 8\n", " br label %if\n", "\n", "if: ; preds = %if.lr.ph, %idxend\n", " %s.06 = phi double [ 0.000000e+00, %if.lr.ph ], [ %16, %idxend ]\n", " %\"#temp#.05\" = phi i64 [ 1, %if.lr.ph ], [ %15, %idxend ]\n", " %10 = add i64 %\"#temp#.05\", -1\n", " %11 = icmp ult i64 %10, %7\n", " br i1 %11, label %idxend, label %oob\n", "\n", "L14.loopexit: ; preds = %idxend\n", " br label %L14\n", "\n", "L14: ; preds = %L14.loopexit, %top\n", " %s.0.lcssa = phi double [ 0.000000e+00, %top ], [ %16, %L14.loopexit ]\n", " ret double %s.0.lcssa\n", "\n", "oob: ; preds = %if\n", " %12 = alloca i64, align 8\n", " store i64 %\"#temp#.05\", i64* %12, align 8\n", " call void @jl_bounds_error_ints(i8** nonnull %0, i64* nonnull %12, i64 1)\n", " unreachable\n", "\n", "idxend: ; preds = %if\n", " %13 = getelementptr double, double* %9, i64 %10\n", " %14 = load double, double* %13, align 8\n", " %15 = add i64 %\"#temp#.05\", 1\n", " %16 = fadd double %s.06, %14\n", " %17 = icmp eq i64 %\"#temp#.05\", %3\n", " br i1 %17, label %L14.loopexit, label %if\n", "}\n" ] } ], "source": [ "@code_llvm tally2(a)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Plotting in Julia\n", "\n", "The three most popular options (as far as I know) in Julia are\n", "\n", "- [Gadfly.jl](https://github.com/GiovineItalia/Gadfly.jl)\n", " - Julia equivalent of `ggplot2` in R\n", " \n", " \n", "- [PyPlot.jl](https://github.com/JuliaPy/PyPlot.jl)\n", " - Wrapper for Python's matplotlib\n", " \n", " \n", "- [Plots.jl](https://github.com/JuliaPlots/Plots.jl)\n", " - Defines an unified interface for plotting\n", " - maps arguments to different plotting \"backends\"\n", " - PyPlot, GR, PlotlyJS, and many more\n", " \n", "We demonstrate Plots.jl below:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Pkg.add(\"Plots\")\n", "using Plots\n", "\n", "x = cumsum(randn(50, 2), 1);" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "image/png": "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" }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Pkg.add(\"PyPlot\")\n", "pyplot() # set the backend to PyPlot\n", "plot(x, title=\"Random walk\", xlab=\"time\")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", "\n", "\n", "

Plotly javascript loaded.

\n", "

To load again call

init_notebook(true)

\n", "\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.plotly.v1+json": { "data": [ { "fields": { "colorbar": { "title": "" }, "line": { "color": "rgba(0, 154, 250, 1.000)", "dash": "solid", "shape": "linear", "width": 1 }, "mode": "lines", "name": "y1", "showlegend": true, "type": "scatter", "x": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 ], "xaxis": "x1", "y": [ 1.3921307108173968, 1.0652558608388614, -0.9886899126432904, -0.8385949812484722, -0.6634303756762613, 0.11831769167296868, -0.9115487556183135, -0.009175254637361574, 0.9098876723856789, 0.4735105275746837, -0.6521504592157917, -1.6283232364105624, -2.6257081989458566, -1.59507450435543, -3.0191737271622383, -3.638298207259495, -3.4029910528353473, -4.017332278589937, -5.6587712681857285, -6.151082333071111, -5.6605169581444335, -5.187017459601302, -4.348006145962882, -3.435152183672455, -3.1914224866167316, -3.6225490081771556, -4.261208989008605, -3.4498989472371724, -4.154113197558089, -4.298781482272807, -5.155488720758118, -3.6839767178621794, -3.6486229536032946, -4.7788867701135995, -5.628158372623902, -6.0146514875355175, -7.295774888009117, -6.052945533969334, -5.569641207344041, -5.29740103612891, -5.293072227176147, -4.902599318907711, -3.8006666438987273, -4.718498401463764, -5.854673719085409, -6.425977944165969, -7.51101223229312, -6.864403070002812, -7.142994267446119, -7.4682297183430935 ], "yaxis": "y1" } }, { "fields": { "colorbar": { "title": "" }, "line": { "color": "rgba(227, 111, 71, 1.000)", "dash": "solid", "shape": "linear", "width": 1 }, "mode": "lines", "name": "y2", "showlegend": true, "type": "scatter", "x": [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 ], "xaxis": "x1", "y": [ 1.3451241767424507, 2.1441869713661292, 3.2242073080228844, 1.8590306799944039, 0.8962719367895697, 0.6933016703447114, 1.609825038238971, 0.7694638589385342, -0.2602199168342555, -0.16973629861981465, 1.1386746821959415, 3.689805935808535, 3.612080549804806, 3.631923913683005, 4.31379744580791, 5.279634495969921, 4.77897464329353, 4.0583968659256415, 5.229993277509245, 4.345918562889203, 4.594131760128548, 3.0078549831828667, 2.946260064316165, 2.4240135817008577, 1.202741020285026, 0.5132427644511994, 1.624278262304437, -0.6508822475692215, -0.7413574657319525, -1.11957083434201, -1.852952385783913, -3.2409829013555305, -2.4717486426745507, -2.5844904091175582, -2.9892640963798045, -3.0484552747005362, -4.334450164245254, -4.489079623776927, -2.6132272882710073, -2.1049608690154313, -1.9676602397795107, -2.7664338114291875, -5.324464775457212, -5.969446149290054, -5.700766324487425, -6.930309554369748, -7.412201712486577, -6.8391178036633695, -6.551932979180394, -5.327156144758428 ], "yaxis": "y1" } } ], "layout": { "fields": { "annotations": [ { "font": { "color": "rgba(0, 0, 0, 1.000)", "family": "sans-serif", "size": 20 }, "rotation": 0, "showarrow": false, "text": "Random walk", "x": 0.5148148148148148, "xanchor": "center", "xref": "paper", "y": 1, "yanchor": "top", "yref": "paper" } ], "height": 400, "legend": { "bgcolor": "rgba(255, 255, 255, 1.000)", "bordercolor": "rgba(0, 0, 0, 1.000)", "font": { "color": "rgba(0, 0, 0, 1.000)", "family": "sans-serif", "size": 11 }, "x": 1, "y": 1 }, "margin": { "b": 20, "l": 0, "r": 0, "t": 20 }, "paper_bgcolor": "rgba(255, 255, 255, 1.000)", "plot_bgcolor": "rgba(255, 255, 255, 1.000)", "showlegend": true, "width": 600, "xaxis1": { "anchor": "y1", "domain": [ 0.03619130941965587, 0.9934383202099738 ], "gridcolor": "rgba(0, 0, 0, 0.100)", "gridwidth": 0.5, "linecolor": "rgba(0, 0, 0, 1.000)", "mirror": false, "range": [ 1, 50 ], "showgrid": true, "showline": true, "showticklabels": true, "tickangle": 0, "tickcolor": "rgb(0, 0, 0)", "tickfont": { "color": "rgba(0, 0, 0, 1.000)", "family": "sans-serif", "size": 11 }, "tickmode": "array", "ticks": "inside", "ticktext": [ "10", "20", "30", "40", "50" ], "tickvals": [ 10, 20, 30, 40, 50 ], "title": "time", "titlefont": { "color": "rgba(0, 0, 0, 1.000)", "family": "sans-serif", "size": 15 }, "type": "-", "visible": true, "zeroline": false, "zerolinecolor": "rgba(0, 0, 0, 1.000)" }, "yaxis1": { "anchor": "x1", "domain": [ 0.07581474190726165, 0.9415463692038496 ], "gridcolor": "rgba(0, 0, 0, 0.100)", "gridwidth": 0.5, "linecolor": "rgba(0, 0, 0, 1.000)", "mirror": false, "range": [ -7.51101223229312, 5.279634495969921 ], "showgrid": true, "showline": true, "showticklabels": true, "tickangle": 0, "tickcolor": "rgb(0, 0, 0)", "tickfont": { "color": "rgba(0, 0, 0, 1.000)", "family": "sans-serif", "size": 11 }, "tickmode": "array", "ticks": "inside", "ticktext": [ "-6", "-4", "-2", "0", "2", "4" ], "tickvals": [ -6, -4, -2, 0, 2, 4 ], "title": "", "titlefont": { "color": "rgba(0, 0, 0, 1.000)", "family": "sans-serif", "size": 15 }, "type": "-", "visible": true, "zeroline": false, "zerolinecolor": "rgba(0, 0, 0, 1.000)" } } } }, "text/html": [ "
\n", "\n", "\n" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Pkg.add(\"PlotlyJS\")\n", "plotlyjs() # change backend to Plotly\n", "plot(x, title=\"Random walk\", xlab=\"time\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", " \n", " \n", " \n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "10\n", "\n", "\n", "20\n", "\n", "\n", "30\n", "\n", "\n", "40\n", "\n", "\n", "50\n", "\n", "\n", "-6\n", "\n", "\n", "-4\n", "\n", "\n", "-2\n", "\n", "\n", "0\n", "\n", "\n", "2\n", "\n", "\n", "4\n", "\n", "\n", "Random walk\n", "\n", "\n", "time\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "y1\n", "\n", "\n", "\n", "y2\n", "\n", "\n" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "gr() # change backend to GR\n", "plot(x, title=\"Random walk\", xlab=\"time\")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "\u001b[1m\u001b[36mINFO: \u001b[39m\u001b[22m\u001b[36mSaved animation to /Users/huazhou/Documents/github.com/Hua-Zhou.github.io/teaching/biostatm280-2018spring/slides/02-juliaintro/tmp.gif\n", "\u001b[39m" ] }, { "data": { "text/html": [ "\" />" ], "text/plain": [ "Plots.AnimatedGif(\"/Users/huazhou/Documents/github.com/Hua-Zhou.github.io/teaching/biostatm280-2018spring/slides/02-juliaintro/tmp.gif\")" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "gr()\n", "@gif for i in 1:20\n", " plot(x -> sin(x) / (.2i), 0, i, xlim=(0, 20), ylim=(-.75, .75))\n", " scatter!(x -> cos(x) * .01 * i, 0, i, m=1)\n", "end" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Julia Version 0.6.2\n", "Commit d386e40c17 (2017-12-13 18:08 UTC)\n", "Platform Info:\n", " OS: macOS (x86_64-apple-darwin14.5.0)\n", " CPU: Intel(R) Core(TM) i7-6920HQ CPU @ 2.90GHz\n", " WORD_SIZE: 64\n", " BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Haswell)\n", " LAPACK: libopenblas64_\n", " LIBM: libopenlibm\n", " LLVM: libLLVM-3.9.1 (ORCJIT, skylake)\n" ] } ], "source": [ "versioninfo()" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 0.6.2", "language": "julia", "name": "julia-0.6" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "0.6.2" }, "livereveal": { "scroll": true, "start_slideshow_at": "selected" }, "toc": { "colors": { "hover_highlight": "#DAA520", "running_highlight": "#FF0000", "selected_highlight": "#FFD700" }, "moveMenuLeft": true, "nav_menu": { "height": "512px", "width": "252px" }, "navigate_menu": true, "number_sections": true, "sideBar": true, "threshold": 4, "toc_cell": true, "toc_section_display": "block", "toc_window_display": false, "widenNotebook": false } }, "nbformat": 4, "nbformat_minor": 2 }