├── .gitignore ├── Manifest.toml ├── Project.toml ├── README.md ├── ball_throw-info.txt ├── ball_throw.mp4 ├── images └── logos │ ├── insights-logo.svg │ ├── julia-logo.svg │ ├── jupyter-logo.svg │ ├── mpg-logo.svg │ ├── mpp-logo.svg │ └── tum-logo.svg ├── julia-course-example.ipynb └── julia-course-slides.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | JuliaSysimage.* 3 | -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197" 3 | ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" 4 | ArraysOfArrays = "65a8f2f4-9b39-5baf-92e2-a9cc46fdf018" 5 | BAT = "c0cd4b16-88b7-57fa-983b-ab80aecada7e" 6 | BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" 7 | CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" 8 | ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" 9 | ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" 10 | ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" 11 | Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" 12 | CurveFit = "5a033b19-8c74-5913-a970-47c3779ef25c" 13 | DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" 14 | DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" 15 | Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" 16 | ElasticArrays = "fdbdab4c-e67f-52f5-8c3f-e7b388dad3d4" 17 | EponymTuples = "97e2ac4a-e175-5f49-beb1-4d6866a6cdc3" 18 | FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" 19 | FastClosures = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" 20 | FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" 21 | FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" 22 | ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" 23 | ForwardDiffPullbacks = "450a3b6d-2448-4ee1-8e34-e4eb8713b605" 24 | GPUArrays = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" 25 | GR = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" 26 | Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" 27 | HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" 28 | ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1" 29 | Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0" 30 | Interact = "c601a237-2ae4-5e1e-952c-7a85b0c7eef1" 31 | IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" 32 | InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" 33 | LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" 34 | LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02" 35 | LsqFit = "2fda8390-95c7-5789-9bda-21331edee243" 36 | Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" 37 | Optim = "429524aa-4258-5aef-a3af-852621145aeb" 38 | OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" 39 | PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" 40 | Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" 41 | PkgTemplates = "14b8a8f1-9102-5b29-a752-f990bacb7fe1" 42 | Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" 43 | PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8" 44 | PrettyTables = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" 45 | ProfileView = "c46f51b8-102a-5cf2-8d2c-8597cb0e0da7" 46 | Random123 = "74087812-796a-5b5d-8853-05524746bad3" 47 | SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" 48 | StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" 49 | StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" 50 | StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" 51 | Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" 52 | TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9" 53 | Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" 54 | ValueShapes = "136a8f8c-c49b-4edb-8b98-f3d64d48be8f" 55 | VideoIO = "d6d074c3-1acf-5d4c-9a43-ef38773959a2" 56 | Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" 57 | 58 | [compat] 59 | ArrayInterface = "5" 60 | OrdinaryDiffEq = "6" 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction to Julia 2 | 3 | This course currently constists of 4 | 5 | * [`"julia-course-slides.ipynb"`](julia-course-slides.ipynb): Slides as as Jupyter notebook 6 | 7 | * [`"julia-course-example.ipynb"`](julia-course-example.ipynb): A practical example as a Jupyter notebook 8 | 9 | You need both Julia and a way to run Jupyter notebooks to run this course. 10 | 11 | 12 | ## Installing Julia and either Jupyter or nteract 13 | 14 | ### Installing Julia 15 | 16 | Julia is easy to install: 17 | 18 | * [Download Julia](https://julialang.org/downloads/). 19 | 20 | * Extract the archive resp. run the installer. 21 | 22 | * You may want to add the Julia "bin" directory to your `$PATH"` 23 | 24 | We highly recommend using Julia v1.9 to run the code in this course. 25 | 26 | 27 | ### Installing Jupyter 28 | 29 | If you have a working Jupyter installation, it should detect the Jupyter Julia kernel (see below on how to install it) automatically. 30 | 31 | You can also start Jupyter via Julia: This can either use existing installations of Jupyter, or install both internally by creating an internal Conda installation within `$HOME/.julia/conda`. On Linux, Julia will by default to use the Jupyter installation associated with the `jupyter` executable on your `$PATH`. On OS-X and Windows, both IJulia will by default always create a Julia-internal Conda installation (see above). To change this behavior, set the environment variable [`$JUPYTER`](https://github.com/JuliaLang/IJulia.jl#installation). For details, see the [IJulia.jl](https://github.com/JuliaLang/IJulia.jl#installation)documentation. 32 | 33 | Note: This course doesn't call on any Python packages from Julia. If you *do* call Python from Julia though (e.g. indirectly via packages like PyPlot.jl and UltraNest.jl or directly via PyCall.jl), the same default behavior occurs (the system's Python3 is used on Linux, a Julia-internal Conda environment on OS-X and Windows). To change this, set the [`$PYTHON`](https://github.com/JuliaPy/PyCall.jl#specifying-the-python-version) environment variable. For details, see the [PyCall.jl](https://github.com/JuliaPy/PyCall.jl#specifying-the-python-version) and [PyPlot.jl](https://github.com/JuliaPy/PyPlot.jl) documentation. 34 | 35 | If you want to use a standalone Jupyter/Python installation with Julia, we recommend [installing Anaconda](https://www.anaconda.com/distribution/). 36 | 37 | 38 | ### Jupyter alternative: Installing nteract 39 | 40 | On local systems, you can use the [nteract](https://nteract.io/) deskop application to run Jupyter notebooks, instead of using a Jupyter server. Like Jupyter, nteract should detect the Jupyter Julia kernel (see below) automatically. 41 | 42 | 43 | ### Environment variables 44 | 45 | You may want/need to set the following environment variables: 46 | 47 | * `$PATH`: Include the Julia `bin`-directory in your binary search path, see above. 48 | If you intend to use Jupyter, you will probably want to include the directory containing the `jupyter` binary to your `PATH` as well. 49 | 50 | * [`$JULIA_NUM_THREADS`](https://docs.julialang.org/en/v1/manual/environment-variables/#JULIA_NUM_THREADS-1): Number of threads to use for Julia multi-threading 51 | 52 | * [`$JULIA_DEPOT_PATH`](https://julialang.github.io/Pkg.jl/v1/glossary/) and [`JULIA_PKG_DEVDIR`](https://julialang.github.io/Pkg.jl/v1/managing-packages/#Developing-packages-1): If you want Julia to install packages in another location than `$HOME/.julia`. 53 | 54 | See the Julia manual for a description of [other Julia-specific environment variables](https://docs.julialang.org/en/v1/manual/environment-variables/). 55 | 56 | 57 | ### Installing the Jupyter Julia kernel 58 | 59 | First install the [IJulia Jupyter Julia kernel](https://github.com/JuliaLang/IJulia.jl), [Interact.JL](https://github.com/JuliaGizmos/Interact.jl) and [WebIO.jl](https://github.com/JuliaGizmos/WebIO.jl) in your *default* Julia project environment via 60 | 61 | ```shell 62 | julia -e 'using Pkg; Pkg.add(["IJulia", "Interact", "WebIO"]); Pkg.build("IJulia")' 63 | ``` 64 | 65 | Also run 66 | 67 | ```shell 68 | julia -e 'using WebIO; WebIO.install_jupyter_nbextension()' 69 | ``` 70 | 71 | to install a Jupyter extension that [WebIO.jl](https://github.com/JuliaGizmos/WebIO.jl) (used by Interact.jl) requires to function. 72 | 73 | To configure Julia to use multiple threads when run as a Jupyter kernel, use 74 | 75 | ```shell 76 | julia -e 'using IJulia; IJulia.installkernel("Julia", "--project=@.", "--threads=auto")' 77 | ``` 78 | 79 | On Julia versions *older than v1.6*, you need to use 80 | 81 | ```shell 82 | julia -e 'using IJulia; IJulia.installkernel("Julia", "--project=@.", env=Dict("JULIA_NUM_THREADS"=>"4"))' 83 | ``` 84 | 85 | instead. 86 | 87 | 88 | ## Setting up this course 89 | 90 | Download this course via `Git` and change into the "julia-course" directory: 91 | 92 | ```shell 93 | git clone https://github.com/oschulz/julia-course.git 94 | cd julia-course 95 | ``` 96 | 97 | Julia has a very powerful [package management system](https://julialang.github.io/Pkg.jl/v1/) that allows for using different versions of packages for different projects, layered package environments, etc. Run the shell command 98 | 99 | ```shell 100 | julia --project=. -e 'using Pkg; Pkg.instantiate(); Pkg.precompile()' 101 | ``` 102 | 103 | to instantiate the [Julia project environment](https://docs.julialang.org/en/v1/manual/code-loading/#Project-environments-1) defined by the files "Project.toml" and "Manifest.toml" in the "julia-course" directory. 104 | 105 | Note that the "IJulia" package should always be installed in the *default environment* (see above) and *not* in individual project environments, to avoid version conflicts (since the Jupyter kernel will always try to load the same one). 106 | 107 | Optional: To make this environment provided with this course your default Julia environment, typically located in `"$HOME/user/.julia/environments/v1.6"`, 108 | simply copy the files `"Project.toml"` and `"Manifest.toml"` there, and then add IJulia (see above). 109 | 110 | 111 | ## Using the Jupyter notebooks 112 | 113 | First ensure that you have the "IJulia" package installed, which provides the Jupyter Julia kernel. Test by running (should not report an error) 114 | 115 | ```shell 116 | julia -e 'using IJulia' 117 | ``` 118 | 119 | If you do *not* have a Jupyter installation on your `$PATH`, you may want to start [Jupyter via Julia](https://julialang.github.io/IJulia.jl/stable/manual/running/) or (on a desktop system) use [nteract](https://nteract.io/). 120 | 121 | If you *do* have a Jupyter installation on your `$PATH` (preferred), you can usually just start a [Jupyter notebook server](https://jupyter-notebook.readthedocs.io/en/stable/) using 122 | 123 | ```shell 124 | jupyter notebook 125 | ``` 126 | 127 | When using a Jupyter installation on your local system, your web browser will usually be started automatically and be pointed to the Jupyter notebook server instance. However, when using a software container or when starting Jupyter on a remote system using SSH port forwarding (and in some other cases), Jupyter will complain that it can't start a web browser. In these cases, run 128 | 129 | ```shell 130 | jupyter notebook --no-browser 131 | ``` 132 | 133 | Jupyter will print the URL to point your web browser too. That URL should include an authorization token (unless you configured Jupyter for [password-based access](https://jupyter-notebook.readthedocs.io/en/stable/security.html#alternatives-to-token-authentication)). 134 | 135 | Depending on where and how you run Jupyter - especially if you run in a Docker container - you may need to specify a non-standard port number and/or IP address to bind to, or allow Jupyter to run in a root user account. In such cases, additional options will be required, e.g.: 136 | 137 | ```shell 138 | jupyter notebook --no-browser --ip 0.0.0.0 --port 8888 --allow-root 139 | ``` 140 | -------------------------------------------------------------------------------- /ball_throw-info.txt: -------------------------------------------------------------------------------- 1 | Framerate: 240 fps (or exactly 29.956 fps * 8 ?) 2 | Ball diameter: 60 mm 3 | Ball weight: 7.1 g 4 | Distance camera to ball: approx. 1.72 m 5 | Distance camera to cabinet door: 1.82 m 6 | Cabinet door height: 1.83 m 7 | Cabinet width two middle doors: 0.794 m 8 | -------------------------------------------------------------------------------- /ball_throw.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oschulz/julia-course/91c6b3ff232af064f5652da67ec119c0f597bcc0/ball_throw.mp4 -------------------------------------------------------------------------------- /images/logos/insights-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xmlI 262 | N 269 | S 279 | I 286 | G 293 | H 300 | T 307 | S 314 | -------------------------------------------------------------------------------- /images/logos/julia-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /images/logos/jupyter-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | logo.svg 3 | Created using Figma 0.90 4 | 5 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /images/logos/mpg-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /images/logos/tum-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | TUM_Web_Logo_blau 7 | TUM 8 | 9 | 10 | -------------------------------------------------------------------------------- /julia-course-example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "slideshow": { 8 | "slide_type": "skip" 9 | } 10 | }, 11 | "outputs": [], 12 | "source": [ 13 | "# Check multithreading config:\n", 14 | "Base.Threads.nthreads()" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "slideshow": { 22 | "slide_type": "skip" 23 | } 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "# # Instantiate package environment for this notebook\n", 28 | "using Pkg; pkg\"instantiate\"" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": { 35 | "scrolled": true, 36 | "slideshow": { 37 | "slide_type": "skip" 38 | } 39 | }, 40 | "outputs": [], 41 | "source": [ 42 | "# Check active package versions:\n", 43 | "# using Pkg; pkg\"status\"" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": { 49 | "collapsed": true, 50 | "nbpresent": { 51 | "id": "7b7b849a-81ae-4985-92ec-78b66da75f5f" 52 | }, 53 | "slideshow": { 54 | "slide_type": "slide" 55 | } 56 | }, 57 | "source": [ 58 | "

\n", 59 | " \n", 60 | " Introduction to\n", 61 | " \n", 62 | " \n", 63 | " \"Julia\"\n", 64 | " \n", 65 | " \n", 66 | " Example\n", 67 | " \n", 68 | "

\n", 69 | "\n", 70 | "
\n", 71 | "

\n", 72 | " Oliver Schulz
\n", 73 | " \n", 74 | " Max Planck Institute for Physics
\n", 75 | " oschulz@mpp.mpg.de\n", 76 | "
\n", 77 | "

\n", 78 | "

\n", 79 | " \n", 80 | " \n", 81 | "

\n", 82 | "
\n", 83 | "\n", 84 | "

\n", 85 | " MPI for Physics, March 2021\n", 86 | "

" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## Example: Trajectory of a ball" 94 | ] 95 | }, 96 | { 97 | "cell_type": "markdown", 98 | "metadata": {}, 99 | "source": [ 100 | "Let's analyze the slow-motion video of a bouncing ball:" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "videofile = \"ball_throw.mp4\"" 110 | ] 111 | }, 112 | { 113 | "cell_type": "markdown", 114 | "metadata": {}, 115 | "source": [ 116 | "We need to load a few packages to deal with [videos](https://juliaio.github.io/VideoIO.jl), [images](https://github.com/JuliaImages/Images.jl), [colors](https://github.com/JuliaGraphics/Colors.jl) and [units](https://github.com/PainterQubits/Unitful.jl):" 117 | ] 118 | }, 119 | { 120 | "cell_type": "code", 121 | "execution_count": null, 122 | "metadata": {}, 123 | "outputs": [], 124 | "source": [ 125 | "using VideoIO, Images, Colors, Unitful" 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": {}, 131 | "source": [ 132 | "Let's get the duration of the video" 133 | ] 134 | }, 135 | { 136 | "cell_type": "code", 137 | "execution_count": null, 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "video_duration = VideoIO.get_duration(videofile) * u\"s\"" 142 | ] 143 | }, 144 | { 145 | "cell_type": "markdown", 146 | "metadata": {}, 147 | "source": [ 148 | "and open a video input stream:" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "video_input = VideoIO.openvideo(videofile)" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": {}, 163 | "source": [ 164 | "Get the video frame rate - `video_input.framerate` yields framerate * 1000 for some reason:" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "fps = VideoIO.framerate(video_input)/1000 * u\"s^-1\"" 174 | ] 175 | }, 176 | { 177 | "cell_type": "markdown", 178 | "metadata": {}, 179 | "source": [ 180 | "Reading the whole video would use a lot of RAM. Let's write a function to read single frames at a given point in time:" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "typeof(video_input)" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "function read_frame(input::VideoIO.VideoReader, t::Number)\n", 199 | " t_in_s = float(ustrip(uconvert(u\"s\", t)))\n", 200 | " seek(input, t_in_s)\n", 201 | " read(input)\n", 202 | "end" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "Ok, now we can read a frame. We'll subsample it before display, to keep it small:" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "frameimg = read_frame(video_input, 3u\"s\")\n", 219 | "frameimg[1:5:end,1:5:end]" 220 | ] 221 | }, 222 | { 223 | "cell_type": "markdown", 224 | "metadata": {}, 225 | "source": [ 226 | "Images in in Julia (more specifically, in [Images.jl](https://github.com/JuliaImages/Images.jl)) are simply arrays of color values:" 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "metadata": {}, 233 | "outputs": [], 234 | "source": [ 235 | "typeof(frameimg)" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "But oops, the video was recorded in portrait mode, so it appears rotated. Let's transpose the image array - this will also result in a mirrored image, but that doesn't matter here:" 243 | ] 244 | }, 245 | { 246 | "cell_type": "code", 247 | "execution_count": null, 248 | "metadata": {}, 249 | "outputs": [], 250 | "source": [ 251 | "frameimg[1:5:end,1:5:end]'" 252 | ] 253 | }, 254 | { 255 | "cell_type": "markdown", 256 | "metadata": {}, 257 | "source": [ 258 | "Nice, now let's load the plotting package Plots.jl" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": null, 264 | "metadata": {}, 265 | "outputs": [], 266 | "source": [ 267 | "using Plots; gr(format = :png)" 268 | ] 269 | }, 270 | { 271 | "cell_type": "markdown", 272 | "metadata": {}, 273 | "source": [ 274 | "and plot a frame every second:" 275 | ] 276 | }, 277 | { 278 | "cell_type": "code", 279 | "execution_count": null, 280 | "metadata": {}, 281 | "outputs": [], 282 | "source": [ 283 | "plot(plot.(broadcast(\n", 284 | " (input, t) -> read_frame(input, t)[1:5:end,1:5:end]',\n", 285 | " Ref(video_input),\n", 286 | " 0u\"s\":1u\"s\":video_duration\n", 287 | "), axis=nothing)...)" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "To develop a method that detects the ball, we'll need a frame with and another frame without the ball.\n", 295 | "\n", 296 | "We'll also need image coordinates, so we'll use [Plots.jl](https://github.com/JuliaPlots/Plots.jl) to plot the frames with a coordinate system. Let's load Plots and select the [GR.jl](https://github.com/jheinen/GR.jl) backend, which create plots via the [GR Framework](https://gr-framework.org/):" 297 | ] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": {}, 302 | "source": [ 303 | "We won't flip/transpose the images this time, so that we don't confuse the images axes later on:" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": null, 309 | "metadata": {}, 310 | "outputs": [], 311 | "source": [ 312 | "background_frame, ball_frame = read_frame.(Ref(video_input), [0, 3]u\"s\")\n", 313 | "\n", 314 | "plot(\n", 315 | " plot(background_frame, xlabel = \"j\", ylabel = \"i\"),\n", 316 | " plot(ball_frame, xlabel = \"j\", ylabel = \"i\"),\n", 317 | ")" 318 | ] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "metadata": {}, 323 | "source": [ 324 | "Note that Plots.jl plots images with matrix-like row/column direction.\n", 325 | "\n", 326 | "Each frame image/array is a 1080 x 1920 matrix, with indices 1:1080 for the rows and 1:1920 for the columns:" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": null, 332 | "metadata": {}, 333 | "outputs": [], 334 | "source": [ 335 | "size(background_frame)" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "metadata": {}, 342 | "outputs": [], 343 | "source": [ 344 | "axes(background_frame)" 345 | ] 346 | }, 347 | { 348 | "cell_type": "markdown", 349 | "metadata": {}, 350 | "source": [ 351 | "To find the ball in the video, we need it's color. Let's zoom into `ball_frame`:" 352 | ] 353 | }, 354 | { 355 | "cell_type": "code", 356 | "execution_count": null, 357 | "metadata": {}, 358 | "outputs": [], 359 | "source": [ 360 | "plot(ball_frame[180:260,90:170], ratio = 1)" 361 | ] 362 | }, 363 | { 364 | "cell_type": "markdown", 365 | "metadata": {}, 366 | "source": [ 367 | "And zoom in some more, until only ball color is left:" 368 | ] 369 | }, 370 | { 371 | "cell_type": "code", 372 | "execution_count": null, 373 | "metadata": {}, 374 | "outputs": [], 375 | "source": [ 376 | "plot(ball_frame[202:238,112:148], ratio = 1)" 377 | ] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": {}, 382 | "source": [ 383 | "We want the average color in that image region. To calculate means, we need the [Statistics](https://docs.julialang.org/en/v1/stdlib/Statistics) package, which is part of the Julia standard library:" 384 | ] 385 | }, 386 | { 387 | "cell_type": "code", 388 | "execution_count": null, 389 | "metadata": {}, 390 | "outputs": [], 391 | "source": [ 392 | "using Statistics" 393 | ] 394 | }, 395 | { 396 | "cell_type": "markdown", 397 | "metadata": {}, 398 | "source": [ 399 | "Since images are arrays, we can simply use the function `mean` to get the average color. We convert the color to the HSV color space, since it should be easiest to locate the ball based on color:" 400 | ] 401 | }, 402 | { 403 | "cell_type": "code", 404 | "execution_count": null, 405 | "metadata": {}, 406 | "outputs": [], 407 | "source": [ 408 | "ball_color = HSV{Float32}(mean(ball_frame[205:235,115:145]))" 409 | ] 410 | }, 411 | { 412 | "cell_type": "markdown", 413 | "metadata": {}, 414 | "source": [ 415 | "We define the distance between two colors based on the difference in hue and saturation:" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": null, 421 | "metadata": {}, 422 | "outputs": [], 423 | "source": [ 424 | "function color_dist(a::Color, b::Color)\n", 425 | " @fastmath begin\n", 426 | " ca = convert(HSV, a)\n", 427 | " cb = convert(HSV, b)\n", 428 | " sqrt((Float32(ca.h - cb.h)/360)^2 + Float32(ca.s - cb.s)^2)\n", 429 | " end\n", 430 | "end" 431 | ] 432 | }, 433 | { 434 | "cell_type": "markdown", 435 | "metadata": {}, 436 | "source": [ 437 | "Using `color_dist`, we can define the difference between two frames:" 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "execution_count": null, 443 | "metadata": {}, 444 | "outputs": [], 445 | "source": [ 446 | "framediff(f::Function, frame::AbstractArray, ref_frame::AbstractArray, ref_color::Color) =\n", 447 | " f.(color_dist.(frame, ball_color) .- color_dist.(ref_frame, ball_color))\n", 448 | "\n", 449 | "framediff(frame::AbstractArray, ref_frame::AbstractArray, ref_color::Color) =\n", 450 | " framediff(identity, frame, ref_frame, ref_color)" 451 | ] 452 | }, 453 | { 454 | "cell_type": "markdown", 455 | "metadata": {}, 456 | "source": [ 457 | "Let's see how this performs:" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": null, 463 | "metadata": {}, 464 | "outputs": [], 465 | "source": [ 466 | "typeof(similar(background_frame))" 467 | ] 468 | }, 469 | { 470 | "cell_type": "code", 471 | "execution_count": null, 472 | "metadata": {}, 473 | "outputs": [], 474 | "source": [ 475 | "heatmap(framediff(ball_frame, background_frame, ball_color))" 476 | ] 477 | }, 478 | { 479 | "cell_type": "markdown", 480 | "metadata": {}, 481 | "source": [ 482 | "Not bad - looks like a threshold of -0.4 might be a good choice to separate pixels belonging to the ball from pixels belonging to the background:" 483 | ] 484 | }, 485 | { 486 | "cell_type": "code", 487 | "execution_count": null, 488 | "metadata": {}, 489 | "outputs": [], 490 | "source": [ 491 | "heatmap(framediff(x -> x < -0.4, ball_frame, background_frame, ball_color))" 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "That looks like a clean cut. Now all we need to do is to process the whole video. We generate the pixel masks on the fly, to avoid storing the whole video in RAM. Let's define a function for this, in case it needs to be re-run this a different reference color or threshold. Also, let's use multi-threading to process video frames in parallel:" 499 | ] 500 | }, 501 | { 502 | "cell_type": "code", 503 | "execution_count": null, 504 | "metadata": {}, 505 | "outputs": [], 506 | "source": [ 507 | "using Base.Threads\n", 508 | "\n", 509 | "function process_video(input::VideoIO.VideoReader, bg_frame::AbstractMatrix, fg_color::Color, threshold::Real)\n", 510 | " seek(input, 0.0)\n", 511 | "\n", 512 | " first_frame = read(input)\n", 513 | " result = [framediff(x -> x < threshold, first_frame, bg_frame, fg_color)]\n", 514 | " \n", 515 | " result_lock = ReentrantLock()\n", 516 | "\n", 517 | " input_channel = Channel{Tuple{Int, typeof(first_frame)}}(nthreads(), spawn = true) do ch\n", 518 | " i = length(result)\n", 519 | " while !eof(input)\n", 520 | " i += 1\n", 521 | " push!(ch, (i, read(input)))\n", 522 | " end\n", 523 | " end \n", 524 | " \n", 525 | " @sync for _ in 1:nthreads()\n", 526 | " @Base.Threads.spawn for (i, frame) in input_channel\n", 527 | " r = framediff(x -> x < threshold, frame, bg_frame, fg_color)\n", 528 | " lock(result_lock) do\n", 529 | " nframes = max(length(result), i)\n", 530 | " resize!(result, nframes)\n", 531 | " result[i] = r\n", 532 | " end\n", 533 | " end\n", 534 | " end\n", 535 | " \n", 536 | " @assert all(isassigned.(Ref(result), eachindex(result)))\n", 537 | " \n", 538 | " result\n", 539 | "end" 540 | ] 541 | }, 542 | { 543 | "cell_type": "code", 544 | "execution_count": null, 545 | "metadata": {}, 546 | "outputs": [], 547 | "source": [ 548 | "diffvideo = @time process_video(video_input, background_frame, ball_color, -0.4)\n", 549 | "typeof(diffvideo), length(diffvideo)" 550 | ] 551 | }, 552 | { 553 | "cell_type": "code", 554 | "execution_count": null, 555 | "metadata": {}, 556 | "outputs": [], 557 | "source": [ 558 | "heatmap(diffvideo[50])" 559 | ] 560 | }, 561 | { 562 | "cell_type": "markdown", 563 | "metadata": {}, 564 | "source": [ 565 | "We interpret each difference frame as a matrix of weights (0 or 1) and estimate the position of the ball as the weighted mean of image coordinates/indices. [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl) will come in handy here to handle vectors of fixed size that can be stack-allocated:" 566 | ] 567 | }, 568 | { 569 | "cell_type": "code", 570 | "execution_count": null, 571 | "metadata": {}, 572 | "outputs": [], 573 | "source": [ 574 | "using StaticArrays" 575 | ] 576 | }, 577 | { 578 | "cell_type": "code", 579 | "execution_count": null, 580 | "metadata": {}, 581 | "outputs": [], 582 | "source": [ 583 | "function mean_pos(W::AbstractArray{T,N}) where {T,N}\n", 584 | " U = float(T)\n", 585 | " R = SVector{N,U}\n", 586 | " sum_pos::R = zero(R)\n", 587 | " sum_w::U = zero(U)\n", 588 | " @inbounds for idx in CartesianIndices(W)\n", 589 | " w = W[idx]\n", 590 | " sum_pos += SVector(Tuple(idx)) * w\n", 591 | " sum_w += w\n", 592 | " end\n", 593 | " sum_pos / sum_w\n", 594 | "end" 595 | ] 596 | }, 597 | { 598 | "cell_type": "markdown", 599 | "metadata": {}, 600 | "source": [ 601 | "Let's see if the is fast enough, using [BenchmarkTools.jl](https://github.com/JuliaCI/BenchmarkTools.jl):" 602 | ] 603 | }, 604 | { 605 | "cell_type": "code", 606 | "execution_count": null, 607 | "metadata": {}, 608 | "outputs": [], 609 | "source": [ 610 | "using BenchmarkTools" 611 | ] 612 | }, 613 | { 614 | "cell_type": "code", 615 | "execution_count": null, 616 | "metadata": {}, 617 | "outputs": [], 618 | "source": [ 619 | "@benchmark mean_pos($diffvideo[1])" 620 | ] 621 | }, 622 | { 623 | "cell_type": "markdown", 624 | "metadata": {}, 625 | "source": [ 626 | "That should do, speed-wise!\n", 627 | "\n", 628 | "StaticArrays.jl allows us to define custom field-vector types, we'll need something to represent 2D x/y vectors:" 629 | ] 630 | }, 631 | { 632 | "cell_type": "code", 633 | "execution_count": null, 634 | "metadata": {}, 635 | "outputs": [], 636 | "source": [ 637 | "struct Vec2D{T} <: FieldVector{2,T}\n", 638 | " x::T\n", 639 | " y::T\n", 640 | "end" 641 | ] 642 | }, 643 | { 644 | "cell_type": "markdown", 645 | "metadata": {}, 646 | "source": [ 647 | "Now we can reconstruct the ball positions, as a vector of `Vec2D`:" 648 | ] 649 | }, 650 | { 651 | "cell_type": "code", 652 | "execution_count": null, 653 | "metadata": {}, 654 | "outputs": [], 655 | "source": [ 656 | "Vec2D.(mean_pos.(diffvideo))" 657 | ] 658 | }, 659 | { 660 | "cell_type": "markdown", 661 | "metadata": {}, 662 | "source": [ 663 | "However, we'll also frequently want to access all `x` and `y` fields as separate vectors. [StructArrays.jl](https://github.com/JuliaArrays/StructArrays.jl) allows us to store this data as a [Structure of Arrays](https://en.wikipedia.org/wiki/AoS_and_SoA), with both AoS and SoA semantics:" 664 | ] 665 | }, 666 | { 667 | "cell_type": "code", 668 | "execution_count": null, 669 | "metadata": {}, 670 | "outputs": [], 671 | "source": [ 672 | "using StructArrays" 673 | ] 674 | }, 675 | { 676 | "cell_type": "code", 677 | "execution_count": null, 678 | "metadata": {}, 679 | "outputs": [], 680 | "source": [ 681 | "PV = StructArray(Vec2D.(mean_pos.(diffvideo)))\n", 682 | "typeof(PV.x), typeof(PV.y), typeof(PV[1])" 683 | ] 684 | }, 685 | { 686 | "cell_type": "markdown", 687 | "metadata": {}, 688 | "source": [ 689 | "Did we reconstruct the ball positions correctly?" 690 | ] 691 | }, 692 | { 693 | "cell_type": "code", 694 | "execution_count": null, 695 | "metadata": {}, 696 | "outputs": [], 697 | "source": [ 698 | "plot(PV.x, PV.y, yflip = true)" 699 | ] 700 | }, 701 | { 702 | "cell_type": "markdown", 703 | "metadata": {}, 704 | "source": [ 705 | "That looks promising. We also need a time axis, though - let's use a [`TypedTables.Table`](https://github.com/JuliaData/TypedTables.jl) to put it all together:" 706 | ] 707 | }, 708 | { 709 | "cell_type": "code", 710 | "execution_count": null, 711 | "metadata": {}, 712 | "outputs": [], 713 | "source": [ 714 | "using TypedTables" 715 | ] 716 | }, 717 | { 718 | "cell_type": "code", 719 | "execution_count": null, 720 | "metadata": {}, 721 | "outputs": [], 722 | "source": [ 723 | "realtime_framerate = 240\n", 724 | "raw_data = Table(xy = PV, t = (eachindex(PV) .- firstindex(PV)) / realtime_framerate)" 725 | ] 726 | }, 727 | { 728 | "cell_type": "markdown", 729 | "metadata": {}, 730 | "source": [ 731 | "Note: A [`DataFrames.DataFrame`](https://github.com/JuliaData/DataFrames.jl) would also do, we choose `TypedTables.Table` here for type stability.\n", 732 | "\n", 733 | "Let's pull in [Interact.jl](https://github.com/JuliaGizmos/Interact.jl) for interactive data exploration. We'll also use [Printf](https://docs.julialang.org/en/v1/stdlib/Printf/) from the Julia standard library for number formatting." 734 | ] 735 | }, 736 | { 737 | "cell_type": "code", 738 | "execution_count": null, 739 | "metadata": {}, 740 | "outputs": [], 741 | "source": [ 742 | "using Interact, Printf" 743 | ] 744 | }, 745 | { 746 | "cell_type": "code", 747 | "execution_count": null, 748 | "metadata": {}, 749 | "outputs": [], 750 | "source": [ 751 | "@manipulate for i in eachindex(diffvideo)\n", 752 | " white = eltype(background_frame)(colorant\"springgreen4\")\n", 753 | "\n", 754 | " plot(\n", 755 | " ((w,c) -> w ? white : c).(diffvideo[i], background_frame)',\n", 756 | " # ratio = 1\n", 757 | " )\n", 758 | "\n", 759 | " plot!(\n", 760 | " raw_data.xy.x, raw_data.xy.y,\n", 761 | " yflip = true, color = :blue, label = \"trajectory\"\n", 762 | " )\n", 763 | "\n", 764 | " scatter!(\n", 765 | " [raw_data.xy.x[i]], [raw_data.xy.y[i]],\n", 766 | " marker = (:xcross, :red),\n", 767 | " label = (@sprintf \"%.2f s\" raw_data.t[i])\n", 768 | " )\n", 769 | "end" 770 | ] 771 | }, 772 | { 773 | "cell_type": "markdown", 774 | "metadata": {}, 775 | "source": [ 776 | "In the following, we'll only analyse the fist arc of the trajectory:" 777 | ] 778 | }, 779 | { 780 | "cell_type": "code", 781 | "execution_count": null, 782 | "metadata": {}, 783 | "outputs": [], 784 | "source": [ 785 | "sel_idxs = 27:244" 786 | ] 787 | }, 788 | { 789 | "cell_type": "code", 790 | "execution_count": null, 791 | "metadata": {}, 792 | "outputs": [], 793 | "source": [ 794 | "# FileIO.save(\"background.png\", background_frame)" 795 | ] 796 | }, 797 | { 798 | "cell_type": "code", 799 | "execution_count": null, 800 | "metadata": {}, 801 | "outputs": [], 802 | "source": [ 803 | "raw_xy_shift = Vec2D(0, lastindex(axes(background_frame,2)))\n", 804 | "\n", 805 | "xy_cal_factor = 1.83 / 1559 * 1.72/1.82\n", 806 | "\n", 807 | "xy_cal = SMatrix{2,2}(\n", 808 | " xy_cal_factor, 0,\n", 809 | " 0, -xy_cal_factor\n", 810 | ")\n", 811 | "\n", 812 | "cal_data = Table(\n", 813 | " xy = StructArray(Vec2D.(Ref(xy_cal) .* (raw_data.xy .- Ref(raw_xy_shift)))),\n", 814 | " t = copy(collect(raw_data.t)),\n", 815 | ")\n", 816 | "\n", 817 | "# Fix missing frame:\n", 818 | "view(cal_data.t, 170:lastindex(cal_data.t)) .+= 1 / realtime_framerate\n", 819 | "\n", 820 | "sel_data = cal_data[sel_idxs]\n", 821 | "\n", 822 | "scatter(\n", 823 | " sel_data.xy.x, sel_data.xy.y,\n", 824 | " marker = (:circle, 2, :black, stroke(0)),\n", 825 | " xlabel = \"x [m]\", ylabel = \"y [m]\"\n", 826 | ")" 827 | ] 828 | }, 829 | { 830 | "cell_type": "code", 831 | "execution_count": null, 832 | "metadata": {}, 833 | "outputs": [], 834 | "source": [ 835 | "using CurveFit" 836 | ] 837 | }, 838 | { 839 | "cell_type": "code", 840 | "execution_count": null, 841 | "metadata": {}, 842 | "outputs": [], 843 | "source": [ 844 | "f = curve_fit(CurveFit.Polynomial, sel_data.t, sel_data.xy.y, 2)" 845 | ] 846 | }, 847 | { 848 | "cell_type": "code", 849 | "execution_count": null, 850 | "metadata": {}, 851 | "outputs": [], 852 | "source": [ 853 | "scatter(\n", 854 | " sel_data.xy.x, sel_data.xy.y,\n", 855 | " marker = (:circle, 2, :black, stroke(0)),\n", 856 | " xlabel = \"x [m]\", ylabel = \"y [m]\"\n", 857 | ")" 858 | ] 859 | }, 860 | { 861 | "cell_type": "code", 862 | "execution_count": null, 863 | "metadata": {}, 864 | "outputs": [], 865 | "source": [ 866 | "plot(sel_data.t, f.(sel_data.t), label = \"fit\")\n", 867 | "scatter!(\n", 868 | " sel_data.t, sel_data.xy.y,\n", 869 | " marker = (:circle, 2, :black, stroke(0)),\n", 870 | " xlabel = \"t [s]\", ylabel = \"y [m]\",\n", 871 | " label = \"data\"\n", 872 | ")" 873 | ] 874 | }, 875 | { 876 | "cell_type": "code", 877 | "execution_count": null, 878 | "metadata": {}, 879 | "outputs": [], 880 | "source": [ 881 | "g_curvefit = -2 * f.coeffs[3] * u\"m/s\"" 882 | ] 883 | }, 884 | { 885 | "cell_type": "markdown", 886 | "metadata": {}, 887 | "source": [ 888 | "### Bayesian inference of motion parameters" 889 | ] 890 | }, 891 | { 892 | "cell_type": "markdown", 893 | "metadata": {}, 894 | "source": [ 895 | "In the following, we'll need [LinearAlgebra](https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/) from the Julia standard library, [OrdinaryDiffEq.jl](https://github.com/JuliaDiffEq/OrdinaryDiffEq.jl) from the [Julia differential equations suite](https://docs.juliadiffeq.org/) and [ValueShapes.jl](https://github.com/oschulz/ValueShapes.jl):" 896 | ] 897 | }, 898 | { 899 | "cell_type": "code", 900 | "execution_count": null, 901 | "metadata": {}, 902 | "outputs": [], 903 | "source": [ 904 | "using LinearAlgebra, StaticArrays, OrdinaryDiffEq\n", 905 | "using Statistics, StatsBase, Distributions, ValueShapes, InverseFunctions, BAT" 906 | ] 907 | }, 908 | { 909 | "cell_type": "code", 910 | "execution_count": null, 911 | "metadata": {}, 912 | "outputs": [], 913 | "source": [ 914 | "function motion_eqn!(du::AbstractVector, u::AbstractVector, p::AbstractVector, t::Real)\n", 915 | " x, y, dx, dy = u\n", 916 | " ρ, A, m, g, C_w = p\n", 917 | "\n", 918 | " xy = SVector(x, y); d_xy = SVector(dx, dy)\n", 919 | " \n", 920 | " f_drag = - ρ * A * C_w * norm(d_xy) * d_xy / 2\n", 921 | " f_grav = m * SVector(zero(g), -g)\n", 922 | " dd_xy = (f_drag + f_grav) / m\n", 923 | "\n", 924 | " du .= (d_xy[1], d_xy[2], dd_xy[1], dd_xy[2])\n", 925 | " return du\n", 926 | "end\n", 927 | "\n", 928 | "function simulate_motion(v::NamedTuple, timesteps::AbstractVector = 0:0.05:1)\n", 929 | " u0 = [v.x, v.y, v.vx, v.vy]\n", 930 | " p = [v.ρ, v.A, v.m, v.g, v.C_w]\n", 931 | "\n", 932 | " odeprob = ODEProblem{true}(motion_eqn!, u0, (first(timesteps), last(timesteps)), p)\n", 933 | "\n", 934 | " sol = solve(odeprob, Tsit5(), saveat = timesteps)\n", 935 | " (x = sol[1,:], y = sol[2,:], t = timesteps)\n", 936 | "end\n", 937 | "\n", 938 | "\n", 939 | "likelihood = let data = (x = sel_data.xy.x, y = sel_data.xy.y, t = sel_data.t)\n", 940 | " v -> begin\n", 941 | " σ_x, σ_y = v.noise .^ 2\n", 942 | " sim_data = simulate_motion(v, data.t)\n", 943 | " (log = sum(logpdf.(Normal.(sim_data.x, σ_x), data.x)) + sum(logpdf.(Normal.(sim_data.y, σ_y), data.y)),)\n", 944 | " end\n", 945 | "end\n", 946 | "\n", 947 | "\n", 948 | "prior = NamedTupleDist(\n", 949 | " x = Normal(0, 1),\n", 950 | " y = Normal(1, 2),\n", 951 | " vx = Normal(1, 1),\n", 952 | " vy = Normal(2, 2),\n", 953 | " ρ = 1.209, # air density at 22°C and 1024 mbar, in kg/m^3\n", 954 | " A = pi * (60e-3/2)^2, # ball cross section area\n", 955 | " m = 7.1e-3, # mass of ball, in kg\n", 956 | " g = Weibull(250, 9.8), # 9.81\n", 957 | " C_w = Weibull(20, 0.5), # unitless, 0.47 would be a typical value for a sphere\n", 958 | " noise = [sqrt(0.01), sqrt(0.01)]\n", 959 | " #noise = [Weibull(1, 0.005), Weibull(1, 0.005)] # noise (stderr)\n", 960 | ")\n", 961 | "\n", 962 | "posterior = PosteriorDensity(likelihood, prior)\n", 963 | "\n", 964 | "logvalof(posterior)(rand(prior))" 965 | ] 966 | }, 967 | { 968 | "cell_type": "code", 969 | "execution_count": null, 970 | "metadata": {}, 971 | "outputs": [], 972 | "source": [ 973 | "@benchmark logvalof(posterior)(rand(prior))" 974 | ] 975 | }, 976 | { 977 | "cell_type": "code", 978 | "execution_count": null, 979 | "metadata": {}, 980 | "outputs": [], 981 | "source": [ 982 | "plt = scatter(\n", 983 | " sel_data.xy.x, sel_data.xy.y,\n", 984 | " marker = (:circle, 2, :black, stroke(0))\n", 985 | ")\n", 986 | "for xy in simulate_motion.(rand(prior, 100))\n", 987 | " plot!(xy.x, xy.y, color = :lightblue, legend = false)\n", 988 | "end\n", 989 | "plt" 990 | ] 991 | }, 992 | { 993 | "cell_type": "code", 994 | "execution_count": null, 995 | "metadata": {}, 996 | "outputs": [], 997 | "source": [ 998 | "v_guess = rand(prior)" 999 | ] 1000 | }, 1001 | { 1002 | "cell_type": "code", 1003 | "execution_count": null, 1004 | "metadata": {}, 1005 | "outputs": [], 1006 | "source": [ 1007 | "using ForwardDiff\n", 1008 | "let vs = varshape(prior)\n", 1009 | " ForwardDiff.gradient(v -> likelihood(vs(v)).log, inverse(vs)(v_guess))\n", 1010 | "end" 1011 | ] 1012 | }, 1013 | { 1014 | "cell_type": "markdown", 1015 | "metadata": {}, 1016 | "source": [ 1017 | "Simple maximum likelihood:" 1018 | ] 1019 | }, 1020 | { 1021 | "cell_type": "code", 1022 | "execution_count": null, 1023 | "metadata": {}, 1024 | "outputs": [], 1025 | "source": [ 1026 | "using Optim\n", 1027 | "let vs = varshape(prior)\n", 1028 | " r = Optim.optimize(v -> - likelihood(vs(v)).log, inverse(vs)(v_guess), Optim.LBFGS(); autodiff = :forward)\n", 1029 | " varshape(prior)(Optim.minimizer(r))\n", 1030 | "end" 1031 | ] 1032 | }, 1033 | { 1034 | "cell_type": "markdown", 1035 | "metadata": {}, 1036 | "source": [ 1037 | "Maximum posterior estimate:" 1038 | ] 1039 | }, 1040 | { 1041 | "cell_type": "code", 1042 | "execution_count": null, 1043 | "metadata": {}, 1044 | "outputs": [], 1045 | "source": [ 1046 | "findmode_ret = bat_findmode(posterior, MaxDensityLBFGS())\n", 1047 | "findmode_ret.info" 1048 | ] 1049 | }, 1050 | { 1051 | "cell_type": "code", 1052 | "execution_count": null, 1053 | "metadata": {}, 1054 | "outputs": [], 1055 | "source": [ 1056 | "mode_est = findmode_ret.result" 1057 | ] 1058 | }, 1059 | { 1060 | "cell_type": "code", 1061 | "execution_count": null, 1062 | "metadata": { 1063 | "scrolled": true 1064 | }, 1065 | "outputs": [], 1066 | "source": [ 1067 | "sim_data_bestfit = simulate_motion(mode_est, sel_data.t)\n", 1068 | "plot(\n", 1069 | " sim_data_bestfit.x, sim_data_bestfit.y,\n", 1070 | " #=marker = (:circle, 2, stroke(0)),=#\n", 1071 | " label = \"best fit\"\n", 1072 | ")\n", 1073 | "scatter!(\n", 1074 | " sel_data.xy.x, sel_data.xy.y,\n", 1075 | " marker = (:circle, 2, :black, stroke(0)),\n", 1076 | " xlabel = \"x [m]\", ylabel = \"y [m]\",\n", 1077 | " label = \"data\"\n", 1078 | ")" 1079 | ] 1080 | }, 1081 | { 1082 | "cell_type": "code", 1083 | "execution_count": null, 1084 | "metadata": {}, 1085 | "outputs": [], 1086 | "source": [ 1087 | "samling_output = @time bat_sample(posterior, MCMCSampling(mcalg = HamiltonianMC(), nchains = 4, nsteps = 10^4))\n", 1088 | "samples = samling_output.result;" 1089 | ] 1090 | }, 1091 | { 1092 | "cell_type": "code", 1093 | "execution_count": null, 1094 | "metadata": {}, 1095 | "outputs": [], 1096 | "source": [ 1097 | "plot(samples)" 1098 | ] 1099 | }, 1100 | { 1101 | "cell_type": "code", 1102 | "execution_count": null, 1103 | "metadata": {}, 1104 | "outputs": [], 1105 | "source": [ 1106 | "plot(samples, vsel = [:vx, :vy, :C_w, :g])" 1107 | ] 1108 | }, 1109 | { 1110 | "cell_type": "code", 1111 | "execution_count": null, 1112 | "metadata": {}, 1113 | "outputs": [], 1114 | "source": [ 1115 | "SampledDensity(posterior, samples)" 1116 | ] 1117 | }, 1118 | { 1119 | "cell_type": "code", 1120 | "execution_count": null, 1121 | "metadata": {}, 1122 | "outputs": [], 1123 | "source": [ 1124 | "mode_samples = bat_findmode(samples).result" 1125 | ] 1126 | }, 1127 | { 1128 | "cell_type": "code", 1129 | "execution_count": null, 1130 | "metadata": {}, 1131 | "outputs": [], 1132 | "source": [ 1133 | "mode_refined = bat_findmode(posterior, MaxDensityLBFGS(init = ExplicitInit([mode_samples]))).result" 1134 | ] 1135 | }, 1136 | { 1137 | "cell_type": "code", 1138 | "execution_count": null, 1139 | "metadata": {}, 1140 | "outputs": [], 1141 | "source": [ 1142 | "sim_data_bestfit = simulate_motion(mode_refined, sel_data.t)\n", 1143 | "plot(\n", 1144 | " sim_data_bestfit.x, sim_data_bestfit.y,\n", 1145 | " #=marker = (:circle, 2, stroke(0)),=#\n", 1146 | " label = \"best fit, refined\"\n", 1147 | ")\n", 1148 | "scatter!(\n", 1149 | " sel_data.xy.x, sel_data.xy.y,\n", 1150 | " marker = (:circle, 2, :black, stroke(0)),\n", 1151 | " xlabel = \"x [m]\", ylabel = \"y [m]\",\n", 1152 | " label = \"data\"\n", 1153 | ")" 1154 | ] 1155 | } 1156 | ], 1157 | "metadata": { 1158 | "@webio": { 1159 | "lastCommId": null, 1160 | "lastKernelId": null 1161 | }, 1162 | "celltoolbar": "Slideshow", 1163 | "hide_input": false, 1164 | "kernelspec": { 1165 | "display_name": "Julia 1.9.0", 1166 | "language": "julia", 1167 | "name": "julia-1.9" 1168 | }, 1169 | "language_info": { 1170 | "file_extension": ".jl", 1171 | "mimetype": "application/julia", 1172 | "name": "julia", 1173 | "version": "1.9.0" 1174 | }, 1175 | "livereveal": { 1176 | "start_slideshow_at": "selected", 1177 | "theme": "simple", 1178 | "transition": "none" 1179 | }, 1180 | "nbpresent": { 1181 | "slides": {}, 1182 | "themes": {} 1183 | }, 1184 | "toc": { 1185 | "base_numbering": 1, 1186 | "nav_menu": {}, 1187 | "number_sections": false, 1188 | "sideBar": true, 1189 | "skip_h1_title": false, 1190 | "title_cell": "Table of Contents", 1191 | "title_sidebar": "Contents", 1192 | "toc_cell": false, 1193 | "toc_position": {}, 1194 | "toc_section_display": true, 1195 | "toc_window_display": false 1196 | } 1197 | }, 1198 | "nbformat": 4, 1199 | "nbformat_minor": 2 1200 | } 1201 | -------------------------------------------------------------------------------- /julia-course-slides.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": { 7 | "slideshow": { 8 | "slide_type": "skip" 9 | } 10 | }, 11 | "outputs": [], 12 | "source": [ 13 | "# # Check multithreading config:\n", 14 | "Base.Threads.nthreads()" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "slideshow": { 22 | "slide_type": "skip" 23 | } 24 | }, 25 | "outputs": [], 26 | "source": [ 27 | "# # Instantiate package environment for this notebook\n", 28 | "# using Pkg; pkg\"instantiate\"" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": { 35 | "slideshow": { 36 | "slide_type": "skip" 37 | } 38 | }, 39 | "outputs": [], 40 | "source": [ 41 | "# # Check active package versions:\n", 42 | "# using Pkg; pkg\"status\"" 43 | ] 44 | }, 45 | { 46 | "cell_type": "markdown", 47 | "metadata": { 48 | "collapsed": true, 49 | "nbpresent": { 50 | "id": "7b7b849a-81ae-4985-92ec-78b66da75f5f" 51 | }, 52 | "slideshow": { 53 | "slide_type": "slide" 54 | } 55 | }, 56 | "source": [ 57 | "

\n", 58 | " \n", 59 | " Introduction to\n", 60 | " \n", 61 | " \n", 62 | " \"Julia\"\n", 63 | " \n", 64 | "

\n", 65 | "\n", 66 | "
\n", 67 | "

\n", 68 | " Oliver Schulz
\n", 69 | " \n", 70 | " Max Planck Institute for Physics
\n", 71 | " oschulz@mpp.mpg.de\n", 72 | "
\n", 73 | "

\n", 74 | "

\n", 75 | " \n", 76 | " \n", 77 | "

\n", 78 | "
\n", 79 | "\n", 80 | "

\n", 81 | " March 2023\n", 82 | "

" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": { 88 | "slideshow": { 89 | "slide_type": "slide" 90 | } 91 | }, 92 | "source": [ 93 | "## Course material\n", 94 | "\n", 95 | "**Git-clone or download the course material:**\n", 96 | "\n", 97 | "## https://github.com/oschulz/julia-course" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": { 103 | "slideshow": { 104 | "slide_type": "slide" 105 | } 106 | }, 107 | "source": [ 108 | "## Why Julia?" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "metadata": { 114 | "slideshow": { 115 | "slide_type": "slide" 116 | } 117 | }, 118 | "source": [ 119 | "### Science needs code - but how to write it?\n", 120 | "\n", 121 | "* Choice of programming language(s) matter!\n", 122 | "\n", 123 | "* Need to balance:\n", 124 | " * Learning time\n", 125 | " * Productivity\n", 126 | " * Performance\n", 127 | "\n", 128 | "* Usually involves compromises" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": { 134 | "slideshow": { 135 | "slide_type": "slide" 136 | } 137 | }, 138 | "source": [ 139 | "### Programming Language Options\n", 140 | "\n", 141 | "* C++:\n", 142 | " * Pro: Very fast (in expert hands)\n", 143 | " * Pro: Really cool new concepts (even literally) in C++11/14/17/...\n", 144 | " * Con: Complex, takes long time to learn and much longer to master\n", 145 | " * Con: Straightforward tasks often result in lengthy code\n", 146 | " * Con: No memory management (General protection faults) \n", 147 | " * Con: No universal package management\n", 148 | " * Con: Composability isn't great" 149 | ] 150 | }, 151 | { 152 | "cell_type": "markdown", 153 | "metadata": { 154 | "slideshow": { 155 | "slide_type": "slide" 156 | } 157 | }, 158 | "source": [ 159 | "### Programming Language Options\n", 160 | "\n", 161 | "* Python:\n", 162 | " * Pro: Broad user base, popular first programming language\n", 163 | " * Pro: Easy to learn, good standard library\n", 164 | " * Con: Can't write time-critical loops in Python, \n", 165 | " workarounds like Numba/Cython have\n", 166 | " [many limitations](http://www.stochasticlifestyle.com/why-numba-and-cython-are-not-substitutes-for-julia/), \n", 167 | " don't compose well\n", 168 | " * Con: Language itself fairly primitive, not very expressive\n", 169 | " * Con: Duck-Typing necessitates lots of test code\n", 170 | " * Con: No effective multi-threading\n", 171 | " * Con: Composability isn't great" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": { 177 | "slideshow": { 178 | "slide_type": "slide" 179 | } 180 | }, 181 | "source": [ 182 | "### What else is there?\n", 183 | "\n", 184 | "* Fortran:\n", 185 | " * Pro: Math can be really fast\n", 186 | " * Con: Old language, few modern concepts\n", 187 | " * Con: Shrinking user base\n", 188 | " * Con: Composability isn't great\n", 189 | " * Do you *really* want to ...?\n", 190 | "\n", 191 | "\n", 192 | "* Scala, Go, Kotlin etc.:\n", 193 | " * Pro: Lots of individual strengths\n", 194 | " * Con: Math either fast *or* generic *or* or complicated\n", 195 | " * Con: Calling C, Fortran or Phython code often difficult\n", 196 | " * Con: Composability isn't great" 197 | ] 198 | }, 199 | { 200 | "cell_type": "markdown", 201 | "metadata": { 202 | "slideshow": { 203 | "slide_type": "slide" 204 | } 205 | }, 206 | "source": [ 207 | "### The 97 and the 3 Percent\n", 208 | "\n", 209 | "> We should forget about small efficiencies, say about 97% of the time: *premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%*.\n", 210 | "\n", 211 | "Donald E. Knuth\n", 212 | "\n", 213 | "* Some programming languages (e.g. Python) great for the 97% - but can't make the 3% fast.\n", 214 | "* Some other languages (e.g. C/C++, Fortran) can handle the 3% - but makes the 97% complicated." 215 | ] 216 | }, 217 | { 218 | "cell_type": "markdown", 219 | "metadata": { 220 | "slideshow": { 221 | "slide_type": "slide" 222 | } 223 | }, 224 | "source": [ 225 | "### The Two-language Problem\n", 226 | "\n", 227 | "* Common approach nowadays: \n", 228 | " Write time critical code in C/C++, rest in Python\n", 229 | "\n", 230 | "* Pro: End-user can code comfortably in Python, with good performance\n", 231 | "\n", 232 | "* Con: Complexity of C/C++ **plus** complexity of Python\n", 233 | "\n", 234 | "* Con: Need proficiency in **two** languages, barrier that prevents \n", 235 | " non-expert users from contributing to important parts of code\n", 236 | "\n", 237 | "* Con: Limits generic implementation of algorithms\n", 238 | "\n", 239 | "* Con: Severely limits metaprogramming, automatic differentiation, etc." 240 | ] 241 | }, 242 | { 243 | "cell_type": "markdown", 244 | "metadata": { 245 | "slideshow": { 246 | "slide_type": "slide" 247 | } 248 | }, 249 | "source": [ 250 | "## The Expression Problem\n", 251 | "\n", 252 | "> The expression problem is a new name for an old problem. The goal is to define a datatype by cases, where one can add new cases to the datatype and new functions over the datatype, without recompiling existing code, and while retaining static type safety (e.g., no casts).\n", 253 | "\n", 254 | "Philip Wadler\n", 255 | "\n", 256 | "* In other words: The capability to add both new subtypes and new functionality for a type defined in a package you don't own\n", 257 | "* Object oriented languages typically can't do this \n", 258 | " (Ruby has a dirty way, Scala a clean workaround)\n", 259 | "* If you have programming experience, you have felt this, even if you didn't name it\n", 260 | "* Result: Packages tend not to compose well\n" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": { 266 | "slideshow": { 267 | "slide_type": "slide" 268 | } 269 | }, 270 | "source": [ 271 | "### We were looking for a language ...\n", 272 | "\n", 273 | "* as fast as C/C++/Fortran\n", 274 | "* as easy to learn and productive as Python\n", 275 | "* with a solution for the expression problem\n", 276 | "* with first class math support (vectors, matrices, etc.)\n", 277 | "* with true functional programming\n", 278 | "* with great Fortran/C/C++/Python integration\n", 279 | "* with true metaprogramming (like Lisp or Scala)\n", 280 | "* good at parallel and distributed programming\n", 281 | "* suitable for for interactive, small and large applications" 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "metadata": { 287 | "slideshow": { 288 | "slide_type": "slide" 289 | } 290 | }, 291 | "source": [ 292 | "### Julia\n", 293 | "\n", 294 | "* Designed for scientific/technical computing\n", 295 | "* Originated at MIT, first public version 2012\n", 296 | "* Covers the whole wish-list\n", 297 | "* Clear focus on user productivity and software quality\n", 298 | "* Rapid growth of user base and software packages\n", 299 | "* Current version: Julia v1.8 (v1.9 release candidate available)" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": { 305 | "nbpresent": { 306 | "id": "b6763142-7446-43bf-a8a3-e7af798914d0" 307 | }, 308 | "slideshow": { 309 | "slide_type": "slide" 310 | } 311 | }, 312 | "source": [ 313 | "### Julia Language Properties\n", 314 | "\n", 315 | "* Fast: JAOT compilation to native CPU and GPU code\n", 316 | "* Multiple-dispatch (more powerful than object-oriented): \n", 317 | " solves the expression problem\n", 318 | "* Dynamically typed\n", 319 | "* Very powerful type system, types are first-class values\n", 320 | "* Functional programming and metaprogramming\n", 321 | "* First-class math support (like Fortran or Matlab)\n", 322 | "* ..." 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": { 328 | "slideshow": { 329 | "slide_type": "slide" 330 | } 331 | }, 332 | "source": [ 333 | "### Julia Language Properties, cont.\n", 334 | "\n", 335 | "* ...\n", 336 | "* Local and distributed code execution\n", 337 | "* State-of-the-art multi-threading: parallel code \n", 338 | " can call parallel code that can call parallel code, ..., \n", 339 | " without oversubscribing threads\n", 340 | "* Software package management: \n", 341 | " Trivial to create and install packages\n", 342 | "* Excellent REPL (console)\n", 343 | "* Easy to call Fortran, C/C++ and Python code" 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": { 349 | "slideshow": { 350 | "slide_type": "slide" 351 | } 352 | }, 353 | "source": [ 354 | "### Julia large-scale use case examples\n", 355 | "\n", 356 | "* Celeste: Variational Bayesian inference for astronomical images (doi:10.1214/19-AOAS1258), 1.54 petaflops using 1.3 million threads on 9,300 Knights Landing (KNL) nodes on Cori at NERSC\n", 357 | "\n", 358 | "* Clima: Full-earth climate simulation, https://clima.caltech.edu, large team, uses everything from MPI to GPUs\n", 359 | "\n", 360 | "* ...\n" 361 | ] 362 | }, 363 | { 364 | "cell_type": "markdown", 365 | "metadata": { 366 | "slideshow": { 367 | "slide_type": "slide" 368 | } 369 | }, 370 | "source": [ 371 | "### When (not) to use Julia\n", 372 | "\n", 373 | "* *Do* use Julia for computations, visualization, data processing ... pretty much anything scientific/technical\n", 374 | "\n", 375 | "* *Do not* use Julia for scripts what will only run for a second (code gen overhead), use Python or shell scripts\n", 376 | "\n", 377 | "* *Do not* use Julia for non-computing web apps, etc. (*at least not yet*), use Go or Node.js" 378 | ] 379 | }, 380 | { 381 | "cell_type": "markdown", 382 | "metadata": { 383 | "slideshow": { 384 | "slide_type": "slide" 385 | } 386 | }, 387 | "source": [ 388 | "## Julia 101" 389 | ] 390 | }, 391 | { 392 | "cell_type": "markdown", 393 | "metadata": { 394 | "slideshow": { 395 | "slide_type": "slide" 396 | } 397 | }, 398 | "source": [ 399 | "### Verbs and nouns - functions and types\n", 400 | "\n", 401 | "* Julia is not Java: Verbs aren't owned by nouns\n", 402 | "\n", 403 | "* Julia has: types, functions and methods\n", 404 | "\n", 405 | "* Methods belong to *functions*, not to types!" 406 | ] 407 | }, 408 | { 409 | "cell_type": "markdown", 410 | "metadata": { 411 | "slideshow": { 412 | "slide_type": "slide" 413 | } 414 | }, 415 | "source": [ 416 | "### Functions\n", 417 | "\n", 418 | "Short one-liner function:" 419 | ] 420 | }, 421 | { 422 | "cell_type": "code", 423 | "execution_count": null, 424 | "metadata": {}, 425 | "outputs": [], 426 | "source": [ 427 | "f(x) = x^2" 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": null, 433 | "metadata": {}, 434 | "outputs": [], 435 | "source": [ 436 | "f(3)" 437 | ] 438 | }, 439 | { 440 | "cell_type": "markdown", 441 | "metadata": { 442 | "slideshow": { 443 | "slide_type": "slide" 444 | } 445 | }, 446 | "source": [ 447 | "Function that needs more than one line:" 448 | ] 449 | }, 450 | { 451 | "cell_type": "code", 452 | "execution_count": null, 453 | "metadata": {}, 454 | "outputs": [], 455 | "source": [ 456 | "function f(x)\n", 457 | " # ... something ...\n", 458 | " x^2\n", 459 | "end" 460 | ] 461 | }, 462 | { 463 | "cell_type": "markdown", 464 | "metadata": {}, 465 | "source": [ 466 | "is equivalent to" 467 | ] 468 | }, 469 | { 470 | "cell_type": "code", 471 | "execution_count": null, 472 | "metadata": {}, 473 | "outputs": [], 474 | "source": [ 475 | "function f(x)\n", 476 | " # ... something ...\n", 477 | " return x^2\n", 478 | "end" 479 | ] 480 | }, 481 | { 482 | "cell_type": "markdown", 483 | "metadata": {}, 484 | "source": [ 485 | "**Note:** `return` is optional, and often not used explicitly. Last expression in a function, block, etc. is automatically returned (like in Mathematica)." 486 | ] 487 | }, 488 | { 489 | "cell_type": "markdown", 490 | "metadata": { 491 | "slideshow": { 492 | "slide_type": "slide" 493 | } 494 | }, 495 | "source": [ 496 | "### Types\n", 497 | "\n", 498 | "An abstract type, must be empty:\n", 499 | "\n", 500 | "```julia\n", 501 | "abstract type MySuperType end\n", 502 | "```\n", 503 | "\n", 504 | "An immutable type, value of `i` can't change:\n", 505 | "\n", 506 | "```julia\n", 507 | "struct MySubType <: MySuperType\n", 508 | " i::Int\n", 509 | "end\n", 510 | "```\n", 511 | "\n", 512 | "A mutable type, value of `i` can change:\n", 513 | "\n", 514 | "```julia\n", 515 | "mutable struct MyMutableSubType <: MySuperType\n", 516 | " i::Int\n", 517 | "end\n", 518 | "```" 519 | ] 520 | }, 521 | { 522 | "cell_type": "markdown", 523 | "metadata": { 524 | "slideshow": { 525 | "slide_type": "slide" 526 | } 527 | }, 528 | "source": [ 529 | "### Type parameters\n", 530 | "\n", 531 | "Julia has a powerful parametric type system, based on set theory:\n", 532 | "\n", 533 | "```julia\n", 534 | "struct MyRealArray{T<:Real,N} <: AbstractArray{T,N}\n", 535 | " # ...\n", 536 | "end\n", 537 | "```\n", 538 | "\n", 539 | "defines an array type with real-valued elements.\n", 540 | "\n", 541 | "```julia\n", 542 | "foo(A::AbstractArray{<:Real}) = do_something_with(A)\n", 543 | "```\n", 544 | "\n", 545 | "is equivalent to\n", 546 | "\n", 547 | "```julia\n", 548 | "bar(A::AbstractArray{T,N}) where {T<:Real,N} = do_something_with(A)\n", 549 | "```\n", 550 | "\n", 551 | "defines a function covariant in the element type of `A`. Can also be contravariant:\n", 552 | "\n", 553 | "```julia\n", 554 | "baz(A::AbstractArray{>:Real}) = do_something_with(A)\n", 555 | "```" 556 | ] 557 | }, 558 | { 559 | "cell_type": "markdown", 560 | "metadata": { 561 | "slideshow": { 562 | "slide_type": "slide" 563 | } 564 | }, 565 | "source": [ 566 | "### Type aliases and union types\n", 567 | "\n", 568 | "Type aliases are just const values:\n", 569 | "\n", 570 | "```julia\n", 571 | "const Abstract2DArray{T} = AbstractArray{T,2}\n", 572 | "rand(2, 2) isa Abstract2DArray == true\n", 573 | "```\n", 574 | "\n", 575 | "Type unions are unions of set of types.\n", 576 | "\n", 577 | "```julia\n", 578 | "const RealVecOrMat{T} where {T<:Real} = Union{AbstractArray{T,1}, AbstractArray{T,2}}\n", 579 | "```\n", 580 | "\n", 581 | "is the union of a 1D and 2D array types with real-valued elements." 582 | ] 583 | }, 584 | { 585 | "cell_type": "markdown", 586 | "metadata": { 587 | "slideshow": { 588 | "slide_type": "slide" 589 | } 590 | }, 591 | "source": [ 592 | "### Syntax: Variables\n", 593 | "\n", 594 | "```julia\n", 595 | "# Global variables:\n", 596 | "const a = 42\n", 597 | "b = 24\n", 598 | "\n", 599 | "function foo(x)\n", 600 | " # Local variables:\n", 601 | "\n", 602 | " c = a * x\n", 603 | " d = b * x # Avoid, type of b can change!\n", 604 | " #...\n", 605 | "end\n", 606 | "```" 607 | ] 608 | }, 609 | { 610 | "cell_type": "markdown", 611 | "metadata": { 612 | "slideshow": { 613 | "slide_type": "slide" 614 | } 615 | }, 616 | "source": [ 617 | "### Loops\n", 618 | "\n", 619 | "For loop:\n", 620 | "\n", 621 | "```julia\n", 622 | "for i in something_iterable\n", 623 | " # ...\n", 624 | "end\n", 625 | "```\n", 626 | "\n", 627 | "`something_iterable` can be a range, an array, anything that implements the Julia [iterator API](https://docs.julialang.org/en/v1/manual/interfaces/).\n", 628 | "\n", 629 | "While loop:\n", 630 | "\n", 631 | "```julia\n", 632 | "while condition\n", 633 | " # do something\n", 634 | "end\n", 635 | "```" 636 | ] 637 | }, 638 | { 639 | "cell_type": "markdown", 640 | "metadata": { 641 | "slideshow": { 642 | "slide_type": "slide" 643 | } 644 | }, 645 | "source": [ 646 | "### Control flow\n", 647 | "\n", 648 | "If-else, evaluate only one branch:\n", 649 | "\n", 650 | "```julia\n", 651 | "if condition\n", 652 | " # do something\n", 653 | "elseif condition\n", 654 | " # do something else\n", 655 | "else\n", 656 | " # or something different\n", 657 | "end\n", 658 | "```\n", 659 | "\n", 660 | "Ternary operator, evaluate only one branch:\n", 661 | "\n", 662 | "```julia\n", 663 | "condition ? result_if_true : result_if_false\n", 664 | "```\n", 665 | "\n", 666 | "`ifelse`, evaluate both results but return only one:\n", 667 | "\n", 668 | "```julia\n", 669 | "ifelse(condition, result_if_true, result_if_false)\n", 670 | "```" 671 | ] 672 | }, 673 | { 674 | "cell_type": "markdown", 675 | "metadata": { 676 | "slideshow": { 677 | "slide_type": "slide" 678 | } 679 | }, 680 | "source": [ 681 | "## Blocks and scoping\n", 682 | "\n", 683 | "Begin/end-block:\n", 684 | "\n", 685 | "```julia\n", 686 | "begin\n", 687 | " # *Not* a new scope in here\n", 688 | " # ...\n", 689 | "end\n", 690 | "```\n", 691 | "\n", 692 | "Let-block:\n", 693 | "\n", 694 | "```julia\n", 695 | "b = 24\n", 696 | "\n", 697 | "let my_b = b\n", 698 | " # New scope in here.\n", 699 | " # If b is bound to new value, my_b won't change.\n", 700 | " # ...\n", 701 | "end\n", 702 | "```" 703 | ] 704 | }, 705 | { 706 | "cell_type": "markdown", 707 | "metadata": { 708 | "slideshow": { 709 | "slide_type": "slide" 710 | } 711 | }, 712 | "source": [ 713 | "## Arrays\n", 714 | "\n", 715 | "Vectors:\n", 716 | "\n", 717 | "```julia\n", 718 | "v = [1, 2, 3]\n", 719 | "\n", 720 | "v = rand(5)\n", 721 | "```\n", 722 | "\n", 723 | "Matrices:\n", 724 | "\n", 725 | "```julia\n", 726 | "A = [1 2; 3 4]\n", 727 | "\n", 728 | "A = rand(4, 5)\n", 729 | "```\n", 730 | "\n", 731 | "* Column-first memory layout!\n", 732 | "\n", 733 | "* Almost anything array-like is subtype of `AbstractArray`." 734 | ] 735 | }, 736 | { 737 | "cell_type": "markdown", 738 | "metadata": { 739 | "slideshow": { 740 | "slide_type": "slide" 741 | } 742 | }, 743 | "source": [ 744 | "### Array indexing\n", 745 | "\n", 746 | "Get `i`-th element of vector `v`:\n", 747 | "\n", 748 | "```julia\n", 749 | "v[i]\n", 750 | "```\n", 751 | "\n", 752 | "Most higher-dimensional array types support cartesian and linear indexing (usually faster):\n", 753 | "\n", 754 | "```julia\n", 755 | "A[i, j]\n", 756 | "A[lin_idx] \n", 757 | "```\n", 758 | "\n", 759 | "Use `eachindex(A)` to get indices of best type for given `A` (usually linear).\n", 760 | "\n", 761 | "\n", 762 | "In Julia, anything array-like can usually be an index as well\n", 763 | "\n", 764 | "```julia\n", 765 | "A[2:3, [1, 4, 5]]\n", 766 | "```" 767 | ] 768 | }, 769 | { 770 | "cell_type": "markdown", 771 | "metadata": { 772 | "slideshow": { 773 | "slide_type": "slide" 774 | } 775 | }, 776 | "source": [ 777 | "### Array comprehension and generators\n", 778 | "\n", 779 | "Returns an array:\n", 780 | "\n", 781 | "```\n", 782 | "[f(x) for x in some_collection]\n", 783 | "```\n", 784 | "\n", 785 | "Returns an iterable generator:\n", 786 | "\n", 787 | "```\n", 788 | "(f(x) for x in some_collection)\n", 789 | "```" 790 | ] 791 | }, 792 | { 793 | "cell_type": "markdown", 794 | "metadata": { 795 | "slideshow": { 796 | "slide_type": "slide" 797 | } 798 | }, 799 | "source": [ 800 | "### Hello World (and more) in Julia" 801 | ] 802 | }, 803 | { 804 | "cell_type": "code", 805 | "execution_count": null, 806 | "metadata": {}, 807 | "outputs": [], 808 | "source": [ 809 | "println(\"Hello, World!\")" 810 | ] 811 | }, 812 | { 813 | "cell_type": "markdown", 814 | "metadata": { 815 | "slideshow": { 816 | "slide_type": "fragment" 817 | } 818 | }, 819 | "source": [ 820 | "Let's define a function" 821 | ] 822 | }, 823 | { 824 | "cell_type": "code", 825 | "execution_count": null, 826 | "metadata": {}, 827 | "outputs": [], 828 | "source": [ 829 | "f(x, y) = x * y\n", 830 | "f(20, 2.1)" 831 | ] 832 | }, 833 | { 834 | "cell_type": "markdown", 835 | "metadata": {}, 836 | "source": [ 837 | "Multiplication is also defined for vectors, so this works, too:" 838 | ] 839 | }, 840 | { 841 | "cell_type": "code", 842 | "execution_count": null, 843 | "metadata": {}, 844 | "outputs": [], 845 | "source": [ 846 | "f(4.2, [1, 2, 3, 4])" 847 | ] 848 | }, 849 | { 850 | "cell_type": "markdown", 851 | "metadata": { 852 | "slideshow": { 853 | "slide_type": "slide" 854 | } 855 | }, 856 | "source": [ 857 | "### Let's Look Under the Hood" 858 | ] 859 | }, 860 | { 861 | "cell_type": "code", 862 | "execution_count": null, 863 | "metadata": {}, 864 | "outputs": [], 865 | "source": [ 866 | "@code_llvm debuginfo=:none f(20, 2.1)" 867 | ] 868 | }, 869 | { 870 | "cell_type": "code", 871 | "execution_count": null, 872 | "metadata": {}, 873 | "outputs": [], 874 | "source": [ 875 | "@code_native debuginfo=:none f(20, 2.1)" 876 | ] 877 | }, 878 | { 879 | "cell_type": "markdown", 880 | "metadata": { 881 | "slideshow": { 882 | "slide_type": "slide" 883 | } 884 | }, 885 | "source": [ 886 | "### Multiple Dispatch" 887 | ] 888 | }, 889 | { 890 | "cell_type": "code", 891 | "execution_count": null, 892 | "metadata": {}, 893 | "outputs": [], 894 | "source": [ 895 | "foo(x::Integer, y::Number) = x * y\n", 896 | "foo(x::Integer, y::AbstractString) = join(fill(y, x))" 897 | ] 898 | }, 899 | { 900 | "cell_type": "code", 901 | "execution_count": null, 902 | "metadata": {}, 903 | "outputs": [], 904 | "source": [ 905 | "foo(3, 4)" 906 | ] 907 | }, 908 | { 909 | "cell_type": "code", 910 | "execution_count": null, 911 | "metadata": {}, 912 | "outputs": [], 913 | "source": [ 914 | "foo(3, \"abc\")" 915 | ] 916 | }, 917 | { 918 | "cell_type": "code", 919 | "execution_count": null, 920 | "metadata": {}, 921 | "outputs": [], 922 | "source": [ 923 | "foo(4.5, 3)" 924 | ] 925 | }, 926 | { 927 | "cell_type": "markdown", 928 | "metadata": { 929 | "slideshow": { 930 | "slide_type": "slide" 931 | } 932 | }, 933 | "source": [ 934 | "### Functional Programming" 935 | ] 936 | }, 937 | { 938 | "cell_type": "code", 939 | "execution_count": null, 940 | "metadata": {}, 941 | "outputs": [], 942 | "source": [ 943 | "A = rand(10)" 944 | ] 945 | }, 946 | { 947 | "cell_type": "code", 948 | "execution_count": null, 949 | "metadata": {}, 950 | "outputs": [], 951 | "source": [ 952 | "idxs = findall(x -> 0.2 < x < 0.6, A)" 953 | ] 954 | }, 955 | { 956 | "cell_type": "code", 957 | "execution_count": null, 958 | "metadata": {}, 959 | "outputs": [], 960 | "source": [ 961 | "A[idxs]" 962 | ] 963 | }, 964 | { 965 | "cell_type": "markdown", 966 | "metadata": { 967 | "slideshow": { 968 | "slide_type": "slide" 969 | } 970 | }, 971 | "source": [ 972 | "Even types are first-class values:" 973 | ] 974 | }, 975 | { 976 | "cell_type": "code", 977 | "execution_count": null, 978 | "metadata": {}, 979 | "outputs": [], 980 | "source": [ 981 | "mytype = Number" 982 | ] 983 | }, 984 | { 985 | "cell_type": "code", 986 | "execution_count": null, 987 | "metadata": {}, 988 | "outputs": [], 989 | "source": [ 990 | "subtypes(mytype)" 991 | ] 992 | }, 993 | { 994 | "cell_type": "markdown", 995 | "metadata": {}, 996 | "source": [ 997 | "Julia type hierarchy extends all the way down to primitive types:" 998 | ] 999 | }, 1000 | { 1001 | "cell_type": "code", 1002 | "execution_count": null, 1003 | "metadata": {}, 1004 | "outputs": [], 1005 | "source": [ 1006 | "Float64 <: AbstractFloat <: Real <: Number <: Any" 1007 | ] 1008 | }, 1009 | { 1010 | "cell_type": "markdown", 1011 | "metadata": { 1012 | "slideshow": { 1013 | "slide_type": "slide" 1014 | } 1015 | }, 1016 | "source": [ 1017 | "### Broadcasting" 1018 | ] 1019 | }, 1020 | { 1021 | "cell_type": "code", 1022 | "execution_count": null, 1023 | "metadata": {}, 1024 | "outputs": [], 1025 | "source": [ 1026 | "A = [1.1, 2.2, 3.3]\n", 1027 | "B = [4.4, 5.5, 6.6]\n", 1028 | "broadcast((x, y) -> (x + y)^2, A, B)" 1029 | ] 1030 | }, 1031 | { 1032 | "cell_type": "markdown", 1033 | "metadata": {}, 1034 | "source": [ 1035 | "Shorter broadcast syntax:" 1036 | ] 1037 | }, 1038 | { 1039 | "cell_type": "code", 1040 | "execution_count": null, 1041 | "metadata": {}, 1042 | "outputs": [], 1043 | "source": [ 1044 | "(A .+ B) .^ 2" 1045 | ] 1046 | }, 1047 | { 1048 | "cell_type": "markdown", 1049 | "metadata": { 1050 | "slideshow": { 1051 | "slide_type": "slide" 1052 | } 1053 | }, 1054 | "source": [ 1055 | "#### Loop Fusion and SIMD Vectorization" 1056 | ] 1057 | }, 1058 | { 1059 | "cell_type": "code", 1060 | "execution_count": null, 1061 | "metadata": {}, 1062 | "outputs": [], 1063 | "source": [ 1064 | "foo(X, Y) = (X .+ Y) .^ 2\n", 1065 | "@code_llvm raw=false debuginfo=:none foo(A, B)" 1066 | ] 1067 | }, 1068 | { 1069 | "cell_type": "code", 1070 | "execution_count": null, 1071 | "metadata": { 1072 | "slideshow": { 1073 | "slide_type": "slide" 1074 | } 1075 | }, 1076 | "outputs": [], 1077 | "source": [ 1078 | "@code_native debuginfo=:none foo(A, B)" 1079 | ] 1080 | }, 1081 | { 1082 | "cell_type": "markdown", 1083 | "metadata": { 1084 | "slideshow": { 1085 | "slide_type": "slide" 1086 | } 1087 | }, 1088 | "source": [ 1089 | "### Package management\n", 1090 | "\n", 1091 | "* Julia probably has the best package management to date\n", 1092 | "\n", 1093 | "* Press \"]\" to enter package management console\n", 1094 | "\n", 1095 | "* Typically `add PACKAGE_NAME` is sufficient, can also do `add PACKAGE_NAME@VERSION`\n", 1096 | "\n", 1097 | "* To get an unreleased version, use `add PACKAGE_NAME#BRANCH_NAME`\n", 1098 | "\n", 1099 | "* Easy to start modifying a package via `dev PACKAGE_NAME`\n", 1100 | "\n", 1101 | "* Multiple package versions can be installed, selection via [Pkg.jl environments](https://julialang.github.io/Pkg.jl/v1/environments).\n", 1102 | "\n", 1103 | "* Also useful: `julia> using Pkg; pkg\"\"`" 1104 | ] 1105 | }, 1106 | { 1107 | "cell_type": "markdown", 1108 | "metadata": { 1109 | "slideshow": { 1110 | "slide_type": "slide" 1111 | } 1112 | }, 1113 | "source": [ 1114 | "### Package creation\n", 1115 | "\n", 1116 | "* A Julia package needs:\n", 1117 | "\n", 1118 | " * A \"Project.toml\" file\n", 1119 | " * A \"src/PackageName.jl\" file\n", 1120 | "\n", 1121 | "* That's it: Push to GitHub, and package is installable via `add PACKAGE_URL`\n", 1122 | "\n", 1123 | "* Use [Documenter.jl](https://github.com/JuliaDocs/Documenter.jl) to document your package\n", 1124 | "\n", 1125 | "* To enable `add PACKAGE_NAME`, package must be [registered](https://github.com/JuliaRegistries/Registrator.jl), there are [some rules](https://github.com/JuliaRegistries/RegistryCI.jl#automatic-merging-guidelines)\n", 1126 | "\n", 1127 | "* Use [PkgTemplates.jl](https://github.com/invenia/PkgTemplates.jl) to generate new package with CI config (Travis, Appveyor, ...), docs generation, etc." 1128 | ] 1129 | }, 1130 | { 1131 | "cell_type": "markdown", 1132 | "metadata": { 1133 | "slideshow": { 1134 | "slide_type": "slide" 1135 | } 1136 | }, 1137 | "source": [ 1138 | "### No free lunch\n", 1139 | "\n", 1140 | "* Package loading and code-gen can sometime take a long time, \n", 1141 | " but mitigations available:\n", 1142 | "\n", 1143 | "* [Revise.jl](https://github.com/timholy/Revise.jl): Hot code-reloading at runtime\n", 1144 | "\n", 1145 | "* [PackageCompiler.jl](https://github.com/JuliaLang/PackageCompiler.jl]: Ahead-of-time compilation, producing custom Julia system images" 1146 | ] 1147 | }, 1148 | { 1149 | "cell_type": "markdown", 1150 | "metadata": { 1151 | "slideshow": { 1152 | "slide_type": "slide" 1153 | } 1154 | }, 1155 | "source": [ 1156 | "### Performance tips\n", 1157 | "\n", 1158 | "* Read the [official Julia performance tips](https://docs.julialang.org/en/v1/manual/performance-tips/)!\n", 1159 | "* Do *not* call on (non-const) global variables from time-critical code\n", 1160 | "* Type-stable code is fast code. Use [`@code_warntype`](https://docs.julialang.org/en/v1/manual/performance-tips/#man-code-warntype-1) and [`Test.@inferred`](https://docs.julialang.org/en/v1/stdlib/Test/#Test.@inferred) to check!\n", 1161 | "* In some situations, closures [can be troublesome](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-captured-1), using `let` can help the compiler" 1162 | ] 1163 | }, 1164 | { 1165 | "cell_type": "markdown", 1166 | "metadata": { 1167 | "slideshow": { 1168 | "slide_type": "slide" 1169 | } 1170 | }, 1171 | "source": [ 1172 | "This is efficient (not runtime reflection):" 1173 | ] 1174 | }, 1175 | { 1176 | "cell_type": "code", 1177 | "execution_count": null, 1178 | "metadata": {}, 1179 | "outputs": [], 1180 | "source": [ 1181 | "half_dynrange(T::Type{<:Number}) = (Int(typemax(T)) - Int(typemin(T))) / 2\n", 1182 | "half_dynrange(Int16)" 1183 | ] 1184 | }, 1185 | { 1186 | "cell_type": "code", 1187 | "execution_count": null, 1188 | "metadata": { 1189 | "scrolled": true 1190 | }, 1191 | "outputs": [], 1192 | "source": [ 1193 | "@code_llvm half_dynrange(Int16)" 1194 | ] 1195 | }, 1196 | { 1197 | "cell_type": "markdown", 1198 | "metadata": { 1199 | "slideshow": { 1200 | "slide_type": "slide" 1201 | } 1202 | }, 1203 | "source": [ 1204 | "### SIMD\n", 1205 | "\n", 1206 | "Demo" 1207 | ] 1208 | }, 1209 | { 1210 | "cell_type": "markdown", 1211 | "metadata": { 1212 | "slideshow": { 1213 | "slide_type": "slide" 1214 | } 1215 | }, 1216 | "source": [ 1217 | "### Shared-memory parallelism\n", 1218 | "\n", 1219 | "* Julia has native multithreading support\n", 1220 | "\n", 1221 | "* Simple cases: Use `@threads` macro\n", 1222 | "\n", 1223 | "* Since Julia v1.3: Cache-efficient [composable multi-threaded parallelism](https://julialang.org/blog/2019/07/multithreading/)" 1224 | ] 1225 | }, 1226 | { 1227 | "cell_type": "markdown", 1228 | "metadata": { 1229 | "slideshow": { 1230 | "slide_type": "slide" 1231 | } 1232 | }, 1233 | "source": [ 1234 | "### Processes, Clusters, MPI\n", 1235 | "\n", 1236 | "* Julia brings a full API for remote processes and compute clusters\n", 1237 | "\n", 1238 | "* Native support for local processes and remote processes via SSH\n", 1239 | "\n", 1240 | "* MPI support via [MPI.jl](https://github.com/JuliaParallel/MPI.jl) and [MPIClusterManagers.jl](https://github.com/JuliaParallel/MPIClusterManagers.jl)" 1241 | ] 1242 | }, 1243 | { 1244 | "cell_type": "markdown", 1245 | "metadata": { 1246 | "slideshow": { 1247 | "slide_type": "slide" 1248 | } 1249 | }, 1250 | "source": [ 1251 | "## Benchmarking and profiling, digging deeper\n", 1252 | "\n", 1253 | "Demo" 1254 | ] 1255 | }, 1256 | { 1257 | "cell_type": "markdown", 1258 | "metadata": { 1259 | "slideshow": { 1260 | "slide_type": "slide" 1261 | } 1262 | }, 1263 | "source": [ 1264 | "## Docs and help\n", 1265 | "\n", 1266 | "* [Official Julia docs](https://docs.julialang.org/en/v1/)\n", 1267 | "\n", 1268 | "* [Julia Cheat Sheet](https://juliadocs.github.io/Julia-Cheat-Sheet/)\n", 1269 | "\n", 1270 | "* [ThinkJulia](https://benlauwens.github.io/ThinkJulia.jl/latest/book.html)\n", 1271 | "\n", 1272 | "* https://julialang.org/learning/\n", 1273 | "\n", 1274 | "* [Julia Discourse](https://discourse.julialang.org/)\n", 1275 | "\n", 1276 | "* [Julia Slack](https://slackinvite.julialang.org/)\n", 1277 | "\n", 1278 | "* [Julia Gitter](https://gitter.im/JuliaLang/julia)\n", 1279 | "\n", 1280 | "* [Julia on Youtube](https://www.youtube.com/user/JuliaLanguage)\n", 1281 | "\n", 1282 | "* [JuliaCon 2020](https://juliacon.org/)" 1283 | ] 1284 | }, 1285 | { 1286 | "cell_type": "markdown", 1287 | "metadata": { 1288 | "slideshow": { 1289 | "slide_type": "slide" 1290 | } 1291 | }, 1292 | "source": [ 1293 | "## Statistics\n", 1294 | "\n", 1295 | "Demo" 1296 | ] 1297 | }, 1298 | { 1299 | "cell_type": "markdown", 1300 | "metadata": { 1301 | "slideshow": { 1302 | "slide_type": "slide" 1303 | } 1304 | }, 1305 | "source": [ 1306 | "## Visualization/Plotting: Plots, Makie, plotting recipes" 1307 | ] 1308 | }, 1309 | { 1310 | "cell_type": "markdown", 1311 | "metadata": { 1312 | "slideshow": { 1313 | "slide_type": "slide" 1314 | } 1315 | }, 1316 | "source": [ 1317 | "#### Let's Make a Plot" 1318 | ] 1319 | }, 1320 | { 1321 | "cell_type": "code", 1322 | "execution_count": null, 1323 | "metadata": {}, 1324 | "outputs": [], 1325 | "source": [ 1326 | "using Plots\n", 1327 | "range = -π:0.01:π\n", 1328 | "plot(range, sin.(range) + rand(length(range)))" 1329 | ] 1330 | }, 1331 | { 1332 | "cell_type": "markdown", 1333 | "metadata": { 1334 | "slideshow": { 1335 | "slide_type": "slide" 1336 | } 1337 | }, 1338 | "source": [ 1339 | "#### Histograms are easy, too" 1340 | ] 1341 | }, 1342 | { 1343 | "cell_type": "code", 1344 | "execution_count": null, 1345 | "metadata": {}, 1346 | "outputs": [], 1347 | "source": [ 1348 | "using Distributions\n", 1349 | "dist = Normal(0.0, 5.0)" 1350 | ] 1351 | }, 1352 | { 1353 | "cell_type": "code", 1354 | "execution_count": null, 1355 | "metadata": {}, 1356 | "outputs": [], 1357 | "source": [ 1358 | "stephist(rand(dist, 10000))" 1359 | ] 1360 | }, 1361 | { 1362 | "cell_type": "markdown", 1363 | "metadata": { 1364 | "slideshow": { 1365 | "slide_type": "slide" 1366 | } 1367 | }, 1368 | "source": [ 1369 | "## Automatic differentiation\n", 1370 | "\n", 1371 | "Let's define a simple neural network layer and loss function and auto-differentiate through it." 1372 | ] 1373 | }, 1374 | { 1375 | "cell_type": "code", 1376 | "execution_count": null, 1377 | "metadata": {}, 1378 | "outputs": [], 1379 | "source": [ 1380 | "struct DenseLayer{M<:AbstractMatrix{<:Real},V<:AbstractVector{<:Real},F<:Function} <: Function\n", 1381 | " A::M\n", 1382 | " b::V\n", 1383 | " f::F\n", 1384 | "end \n", 1385 | "\n", 1386 | "(l::DenseLayer)(x::AbstractVector{<:Real}) = (l.f).(l.A * x + l.b)\n", 1387 | "\n", 1388 | "f_loss(y) = sum(y .^ 2);" 1389 | ] 1390 | }, 1391 | { 1392 | "cell_type": "code", 1393 | "execution_count": null, 1394 | "metadata": {}, 1395 | "outputs": [], 1396 | "source": [ 1397 | "mylayer = DenseLayer(rand(5,5), rand(5), x -> ifelse(x > zero(x), x, zero(x)));" 1398 | ] 1399 | }, 1400 | { 1401 | "cell_type": "code", 1402 | "execution_count": null, 1403 | "metadata": {}, 1404 | "outputs": [], 1405 | "source": [ 1406 | "x = rand(5)\n", 1407 | "mylayer(x)" 1408 | ] 1409 | }, 1410 | { 1411 | "cell_type": "code", 1412 | "execution_count": null, 1413 | "metadata": {}, 1414 | "outputs": [], 1415 | "source": [ 1416 | "f_loss(mylayer(x))" 1417 | ] 1418 | }, 1419 | { 1420 | "cell_type": "code", 1421 | "execution_count": null, 1422 | "metadata": { 1423 | "slideshow": { 1424 | "slide_type": "slide" 1425 | } 1426 | }, 1427 | "outputs": [], 1428 | "source": [ 1429 | "using Zygote\n", 1430 | "g = Zygote.gradient((mylayer, x) -> f_loss(mylayer(x)), mylayer, x)\n", 1431 | "g[1].A" 1432 | ] 1433 | }, 1434 | { 1435 | "cell_type": "code", 1436 | "execution_count": null, 1437 | "metadata": {}, 1438 | "outputs": [], 1439 | "source": [ 1440 | "g[1].b" 1441 | ] 1442 | }, 1443 | { 1444 | "cell_type": "markdown", 1445 | "metadata": { 1446 | "slideshow": { 1447 | "slide_type": "slide" 1448 | } 1449 | }, 1450 | "source": [ 1451 | "## Calling code written in other language, REPL modes\n", 1452 | "\n", 1453 | "### Shell REPL mode\n", 1454 | "\n", 1455 | "* The Julia shell is multi-language\n", 1456 | "\n", 1457 | "* Press \",\" for a system shell" 1458 | ] 1459 | }, 1460 | { 1461 | "cell_type": "markdown", 1462 | "metadata": { 1463 | "slideshow": { 1464 | "slide_type": "slide" 1465 | } 1466 | }, 1467 | "source": [ 1468 | "### PyCall, RCall\n", 1469 | "\n", 1470 | "Calling Python from Julia is easy, can even use inline Python code:\n", 1471 | "\n", 1472 | "```julia\n", 1473 | "using PyCall\n", 1474 | "numpy = pyimport(\"numpy\")\n", 1475 | "numpy.zeros(5) isa Array\n", 1476 | "\n", 1477 | "A = rand(5)\n", 1478 | "py\"\"\"type($A)\"\"\" isa PyObject\n", 1479 | "```" 1480 | ] 1481 | }, 1482 | { 1483 | "cell_type": "markdown", 1484 | "metadata": { 1485 | "slideshow": { 1486 | "slide_type": "slide" 1487 | } 1488 | }, 1489 | "source": [ 1490 | "## An incomplete tour of the Julia package ecosystem" 1491 | ] 1492 | }, 1493 | { 1494 | "cell_type": "markdown", 1495 | "metadata": { 1496 | "slideshow": { 1497 | "slide_type": "slide" 1498 | } 1499 | }, 1500 | "source": [ 1501 | "### Math\n", 1502 | "\n", 1503 | "* [ApproxFun.jl](https://github.com/JuliaApproximation/ApproxFun.jl): Powerful function approximations\n", 1504 | "\n", 1505 | "* [FFTW.jl](https://github.com/JuliaMath/FFTW.jl): Fast fourier transforms via [FFTW](http://www.fftw.org/)\n", 1506 | "\n", 1507 | "* [DifferentialEquations.jl](https://github.com/JuliaDiffEq/DifferentialEquations.jl): A suite for numerically solving differential equations\n", 1508 | "\n", 1509 | "* ..." 1510 | ] 1511 | }, 1512 | { 1513 | "cell_type": "markdown", 1514 | "metadata": { 1515 | "slideshow": { 1516 | "slide_type": "slide" 1517 | } 1518 | }, 1519 | "source": [ 1520 | "### Optimization\n", 1521 | "\n", 1522 | "* [JuMP.jl](https://github.com/JuliaOpt/JuMP.jl): Modeling language for Mathematical Optimization\n", 1523 | "\n", 1524 | "* [NLopt.jl](https://github.com/JuliaOpt/NLopt.jl): Optimization via [NLopt](https://github.com/stevengj/nlopt)\n", 1525 | "\n", 1526 | "* [Optim](https://github.com/JuliaNLSolvers/Optim.jl): Julia native nonlinear optimization" 1527 | ] 1528 | }, 1529 | { 1530 | "cell_type": "markdown", 1531 | "metadata": { 1532 | "slideshow": { 1533 | "slide_type": "slide" 1534 | } 1535 | }, 1536 | "source": [ 1537 | "### TypedTables and DataFrames\n", 1538 | "\n", 1539 | "* [Tables.jl](https://github.com/JuliaData/Tables.jl): Abstract API for tabular data\n", 1540 | "\n", 1541 | "* [DataFrames.jl](https://github.com/JuliaData/DataFrames.jl): Python/R-like dataframes\n", 1542 | "\n", 1543 | "* [TypedTables.jl](https://github.com/JuliaData/TypedTables.jl): Type-stable tables\n", 1544 | "\n", 1545 | "* [Query.jl](https://github.com/queryverse/Query.jl) LINQ-inspired data query and transformation" 1546 | ] 1547 | }, 1548 | { 1549 | "cell_type": "markdown", 1550 | "metadata": { 1551 | "slideshow": { 1552 | "slide_type": "slide" 1553 | } 1554 | }, 1555 | "source": [ 1556 | "### Plotting and Visualization\n", 1557 | "\n", 1558 | "* [IJulia.jl](https://github.com/JuliaLang/IJulia.jl): Julia Jupyter kernel\n", 1559 | "\n", 1560 | "* [Images.jl](https://github.com/JuliaImages/Images.jl): Image processing\n", 1561 | "\n", 1562 | "* [PyPlot.jl](https://github.com/JuliaPy/PyPlot.jl): Use matplotlib/PyPlot from Julia\n", 1563 | "\n", 1564 | "* [Makie.jl](https://github.com/JuliaPlots/Makie.jl): Hardware-accelerated plotting\n", 1565 | "\n", 1566 | "* [Plots.jl](https://github.com/JuliaPlots/Plots.jl): Plotting with generic recipes and multiple backends" 1567 | ] 1568 | }, 1569 | { 1570 | "cell_type": "markdown", 1571 | "metadata": { 1572 | "slideshow": { 1573 | "slide_type": "slide" 1574 | } 1575 | }, 1576 | "source": [ 1577 | "### Statistics\n", 1578 | "\n", 1579 | "* [Distributions.jl](https://github.com/JuliaStats/Distributions.jl): Probability distributions and associated functions\n", 1580 | "\n", 1581 | "* [StatsBase.jl](https://github.com/JuliaStats/StatsBase.jl): Statistics, histograms, etc.\n", 1582 | "\n", 1583 | "* Many specialized packages" 1584 | ] 1585 | }, 1586 | { 1587 | "cell_type": "markdown", 1588 | "metadata": {}, 1589 | "source": [ 1590 | "### Automatic Differentiation\n", 1591 | "\n", 1592 | "* [ForwardDiff.jl](https://github.com/JuliaDiff/ForwardDiff.jl): Forward-mode automatic differentiation\n", 1593 | "* [Zyote.jl](https://github.com/FluxML/Zygote.jl): Source-level reverse-mode automatic differentiation\n", 1594 | "* [Enzyme.jl](https://github.com/wsmoses/Enzyme.jl): LLVM-level reverse-mode automatic differentiation\n", 1595 | "* Several other packages available ([ReverseDiff.jl](https://github.com/JuliaDiff/ReverseDiff.jl), [Nabla.jl](https://github.com/invenia/Nabla.jl), [Yota.jl](https://github.com/dfdx/Yota.jl), ...)\n", 1596 | "* Exciting developements to come with new Julia Compiler features" 1597 | ] 1598 | }, 1599 | { 1600 | "cell_type": "markdown", 1601 | "metadata": { 1602 | "slideshow": { 1603 | "slide_type": "slide" 1604 | } 1605 | }, 1606 | "source": [ 1607 | "### Bayesian analysis and probabilistic programming\n", 1608 | "\n", 1609 | "\n", 1610 | "* [BAT.jl](https://github.com/bat/BAT.jl): Bayesian analysis toolkit\n", 1611 | "\n", 1612 | "* [Gen.jl](https://github.com/probcomp/Gen): General-Purpose Probabilistic Programming System\n", 1613 | "\n", 1614 | "* [Mamba.jl](https://github.com/brian-j-smith/Mamba.jl): MCMC for Bayesian analysis\n", 1615 | "\n", 1616 | "* [Turing.jl](https://github.com/TuringLang/Turing.jl): Probabilistic machine learning and Bayesian statistics\n", 1617 | "\n", 1618 | "* ..." 1619 | ] 1620 | }, 1621 | { 1622 | "cell_type": "markdown", 1623 | "metadata": { 1624 | "slideshow": { 1625 | "slide_type": "slide" 1626 | } 1627 | }, 1628 | "source": [ 1629 | "### Machine learning\n", 1630 | "\n", 1631 | "* [Flux.jl](https://github.com/FluxML/Flux.jl): Julia native deep learning library\n", 1632 | "\n", 1633 | "* [Knet.jl](https://github.com/denizyuret/Knet.jl):Koc University deep learning framework\n", 1634 | "\n", 1635 | "* [MXNet.jl](https://github.com/dmlc/MXNet.jl): [MXNet](https://mxnet.apache.org/) Julia API\n", 1636 | "\n", 1637 | "* ..." 1638 | ] 1639 | }, 1640 | { 1641 | "cell_type": "markdown", 1642 | "metadata": { 1643 | "slideshow": { 1644 | "slide_type": "slide" 1645 | } 1646 | }, 1647 | "source": [ 1648 | "### Calling code in other languages\n", 1649 | "\n", 1650 | "* [Cxx.jl](https://github.com/JuliaInterop/Cxx.jl): Call C++ from Julia\n", 1651 | "\n", 1652 | "* [PyCall.jl](https://github.com/JuliaPy/PyCall.jl): Call Python from Julia\n", 1653 | "\n", 1654 | "* [RCall.jl](https://github.com/JuliaInterop/RCall.jl): Call R from Julia\n", 1655 | "\n", 1656 | "* ..." 1657 | ] 1658 | }, 1659 | { 1660 | "cell_type": "markdown", 1661 | "metadata": { 1662 | "slideshow": { 1663 | "slide_type": "slide" 1664 | } 1665 | }, 1666 | "source": [ 1667 | "### Efficient memory layout\n", 1668 | "\n", 1669 | "* [ArraysOfArrays.jl](https://github.com/oschulz/ArraysOfArrays.jl): Duality of flat and nested arrays\n", 1670 | "\n", 1671 | "* [StructArrays.jl](https://github.com/JuliaArrays/StructArrays.jl), [TypedTables.jl](https://github.com/JuliaData/TypedTables.jl): AoS and SoA duality\n", 1672 | "\n", 1673 | "* [ValueShapes.jl](https://github.com/oschulz/ValueShapes.jl): Duality of flat and nested structures\n", 1674 | "\n", 1675 | "* ..." 1676 | ] 1677 | }, 1678 | { 1679 | "cell_type": "markdown", 1680 | "metadata": { 1681 | "slideshow": { 1682 | "slide_type": "slide" 1683 | } 1684 | }, 1685 | "source": [ 1686 | "### GPU Programming\n", 1687 | "\n", 1688 | "* [AMDGPU.jl](https://github.com/JuliaGPU/AMDGPU.jl): Julia on AMD GPUs (WIP)\n", 1689 | "\n", 1690 | "* [CUDA.jl](https://github.com/JuliaGPU/CUDA.jl): Julia on NVIDIA GPUs\n", 1691 | "\n", 1692 | "* [GPUArrays.jl](https://github.com/JuliaGPU/GPUArrays.jl): Generic CPU programming API\n", 1693 | "\n", 1694 | "* [XLA.jl](https://github.com/JuliaTPU/XLA.jl): Julia on Google TPUs\n", 1695 | "\n", 1696 | "* Maybe more architectures in the future?" 1697 | ] 1698 | }, 1699 | { 1700 | "cell_type": "markdown", 1701 | "metadata": { 1702 | "slideshow": { 1703 | "slide_type": "slide" 1704 | } 1705 | }, 1706 | "source": [ 1707 | "### IDEs\n", 1708 | "\n", 1709 | "* [julia-vscode](https://github.com/julia-vscode/julia-vscode): Visual Studio Code based Julia IDE\n", 1710 | "\n", 1711 | "* [Juno](https://junolab.org/): Atom based Julia IDE (now deprecated in favor of VS code)" 1712 | ] 1713 | }, 1714 | { 1715 | "cell_type": "markdown", 1716 | "metadata": { 1717 | "slideshow": { 1718 | "slide_type": "slide" 1719 | } 1720 | }, 1721 | "source": [ 1722 | "## Final Remarks\n", 1723 | "\n", 1724 | "* Julia is productive, fast and fun - give it a chance!\n", 1725 | "\n", 1726 | "* Multiple dispatch opens up powerful ways of combining code" 1727 | ] 1728 | } 1729 | ], 1730 | "metadata": { 1731 | "@webio": { 1732 | "lastCommId": null, 1733 | "lastKernelId": null 1734 | }, 1735 | "celltoolbar": "Slideshow", 1736 | "hide_input": false, 1737 | "kernelspec": { 1738 | "display_name": "Julia 1.9.0", 1739 | "language": "julia", 1740 | "name": "julia-1.9" 1741 | }, 1742 | "language_info": { 1743 | "file_extension": ".jl", 1744 | "mimetype": "application/julia", 1745 | "name": "julia", 1746 | "version": "1.9.0" 1747 | }, 1748 | "livereveal": { 1749 | "start_slideshow_at": "selected", 1750 | "theme": "simple", 1751 | "transition": "none" 1752 | }, 1753 | "nbpresent": { 1754 | "slides": {}, 1755 | "themes": {} 1756 | }, 1757 | "toc": { 1758 | "base_numbering": 1, 1759 | "nav_menu": {}, 1760 | "number_sections": false, 1761 | "sideBar": true, 1762 | "skip_h1_title": false, 1763 | "title_cell": "Table of Contents", 1764 | "title_sidebar": "Contents", 1765 | "toc_cell": false, 1766 | "toc_position": {}, 1767 | "toc_section_display": true, 1768 | "toc_window_display": false 1769 | } 1770 | }, 1771 | "nbformat": 4, 1772 | "nbformat_minor": 2 1773 | } 1774 | --------------------------------------------------------------------------------