├── images ├── types.jpg └── Type-hierarchy_Number.png ├── notebooks ├── MPI-Heat-Transfer-2D │ ├── types.jl │ ├── config.yaml │ ├── get_sim_params.jl │ ├── io.jl │ ├── example.ipynb │ ├── init.jl │ ├── update.jl │ └── main.jl ├── 00_Intro.ipynb ├── 11_DataFrames-Example.ipynb ├── 12_FluxML-Example.ipynb ├── 09_Units.ipynb ├── 10_Parallel-Computing.ipynb ├── 08_Plotting.ipynb ├── 06_Modules.ipynb ├── 05_Array-Broadcasting.ipynb ├── 01_Variables-and-Types.ipynb ├── 13_Package-Development.ipynb ├── 04_Control-Flow.ipynb ├── 07_Types-and-Structs.ipynb ├── 02_Functions.ipynb └── 03_Data-Structures.ipynb ├── .gitignore ├── LICENSE ├── README.md ├── pluto ├── translate.jl └── 01_Variables-and-Types.jl ├── Project.toml ├── distinctions ├── 04_Interfaces.ipynb ├── Julia_Distinctions.ipynb ├── 00_Why_Julia.ipynb ├── 01_Arrays.ipynb ├── 02_Type_System.ipynb ├── 03_Functions.ipynb └── 05_Modules_and_Packaging.ipynb └── data └── plot_size_case4.csv /images/types.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ornl-training/julia-basics/HEAD/images/types.jpg -------------------------------------------------------------------------------- /images/Type-hierarchy_Number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ornl-training/julia-basics/HEAD/images/Type-hierarchy_Number.png -------------------------------------------------------------------------------- /notebooks/MPI-Heat-Transfer-2D/types.jl: -------------------------------------------------------------------------------- 1 | function oneDArray(t::DataType, len::Int) 2 | return zeros(t, len) 3 | end 4 | 5 | function twoDArray(t::DataType, len_x::Int, len_y::Int) 6 | return zeros(t, len_x, len_y) 7 | end -------------------------------------------------------------------------------- /notebooks/MPI-Heat-Transfer-2D/config.yaml: -------------------------------------------------------------------------------- 1 | grid: 2 | nx: 128 3 | ny: 128 4 | 5 | numerics: 6 | nprocx: 2 7 | nprocy: 2 8 | max_steps: 1000 9 | dt: 0.0001 10 | tol: 0.5 11 | 12 | output: 13 | filename: "output.dat" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Vi(m) temporary buffer files 2 | *.sw? 3 | 4 | # Emacs temporary files 5 | *~ 6 | 7 | #Eclipse 8 | .cproject 9 | .project 10 | .settings/ 11 | .pydevproject 12 | 13 | #binary, library, or temporary build files 14 | *.o 15 | *.a 16 | *.so 17 | *.tmp 18 | *.exe 19 | *.bp 20 | *.out 21 | *.pyc 22 | *.bp 23 | *.bp.dir 24 | build/ 25 | 26 | # Mac OSX finder-related files 27 | .DS_Store 28 | 29 | # Doxygen generated files 30 | html/ 31 | *.html 32 | docs/api_doxygen/CXX11/xml/ 33 | docs/api_doxygen/C/xml/ 34 | 35 | # readthedocs generated: 36 | docs/bin/ 37 | docs/lib/ 38 | docs/pyvenv.cfg 39 | 40 | # Visual Studio 41 | .vs/ 42 | .vscode/ 43 | CMakeSettings.json 44 | 45 | # Jupyter Julia Kernel 46 | .ipynb_checkpoints/ 47 | notebooks/MPI-Heat-Transfer-2D/output.dat 48 | Manifest.toml 49 | 50 | -------------------------------------------------------------------------------- /notebooks/MPI-Heat-Transfer-2D/get_sim_params.jl: -------------------------------------------------------------------------------- 1 | import YAML 2 | 3 | function get_sim_params(ARGS) 4 | 5 | path = ARGS[1] 6 | data = YAML.load(open(path)) 7 | 8 | key = "grid" 9 | Nx = data[key]["nx"] 10 | Ny = data[key]["ny"] 11 | 12 | key = "numerics" 13 | NPROCX = data[key]["nprocx"] 14 | NPROCY = data[key]["nprocy"] 15 | MAX_STEPS = data[key]["max_steps"] 16 | Dt = data[key]["dt"] 17 | TOL = data[key]["tol"] 18 | 19 | key = "output" 20 | filename = data[key]["filename"] 21 | workdir = dirname(path) 22 | output_path = joinpath(workdir, basename(filename)) 23 | 24 | println() 25 | println("Loading: ", path) 26 | println("Output: ", output_path) 27 | 28 | return Int64[Nx, Ny, NPROCX, NPROCY, MAX_STEPS], Float64[Dt, TOL], output_path 29 | 30 | end 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ORNL Training for Scientists 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # julia-basics 2 | 3 | Tutorial material for Julia basic training using [from zero to Julia](https://techytok.com/from-zero-to-julia/) content as Jupyter Notebooks to distribute reproducible code. 4 | 5 | Run locally: 6 | 7 | Requirements: 8 | 9 | 1. Julia latest version (1.11+) 10 | - [Download Julia](https://julialang.org/downloads/) 11 | 2. VS Code 12 | 3. IJulia (already included in the project, no need to install separately) 13 | 14 | 15 | ## Quick start 16 | 17 | ``` 18 | $ git clone https://github.com/ornl-training/julia-basics.git 19 | $ cd julia-basics 20 | $ julia --project -e "import Pkg; Pkg.instantiate()" 21 | ``` 22 | 23 | 1. Open the project directory in VS Code 24 | 2. Open a notebook in the `notebooks` directory. 25 | 3. Select the Julia kernel in the top right corner of the notebook interface. 26 | 4. Run the cells in the notebook. 27 | 28 | ## Additional information 29 | 30 | See `Project.toml` for required Julia packages 31 | 32 | Run on mybinder.org (might be outdated, not recommended): 33 | 34 | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ornl-training/julia-basics.git/main?labpath=notebooks) 35 | -------------------------------------------------------------------------------- /pluto/translate.jl: -------------------------------------------------------------------------------- 1 | using Pkg 2 | #Pkg.add("JSON") 3 | using JSON 4 | 5 | 6 | plutoversion = filter(x-> x.second.name == "Pluto", Pkg.dependencies()) |> x -> first(x)[2].version 7 | header = """### A Pluto.jl notebook ### 8 | # v$plutoversion 9 | 10 | using Markdown 11 | using InteractiveUtils 12 | 13 | """ 14 | section_prefix = "# ╔═╡ " 15 | subsection_prefix = "# ╠═" 16 | 17 | cell_order = [] 18 | 19 | if length(ARGS) != 1 20 | print("usage: julia " * PROGRAM_FILE * " filename.ipynb") 21 | exit() 22 | end 23 | 24 | 25 | f = open(ARGS[1], "r") 26 | d = JSON.parse(f) 27 | 28 | # name of translated pluto notebook .jl file 29 | new_filename = split(last(split(ARGS[1], "/")), ".ipynb")[1] * ".jl" 30 | jlversion = open(new_filename, "w") 31 | write(jlversion, header) 32 | 33 | for cell in d["cells"] 34 | uuid = string(Base.UUID(rand(UInt128))) 35 | push!(cell_order, uuid) 36 | write(jlversion, section_prefix*uuid*"\n") 37 | if cmp("markdown", cell["cell_type"]) == 0 38 | write(jlversion, "md\"\"\"\n") 39 | write(jlversion, join(cell["source"])) 40 | write(jlversion, "\n\"\"\"") 41 | elseif cmp("code", cell["cell_type"]) == 0 42 | write(jlversion, "begin\n") 43 | for line in cell["source"] 44 | write(jlversion, "\t"*"$line") 45 | end 46 | write(jlversion, "\nend") 47 | end 48 | write(jlversion, "\n\n") 49 | end 50 | 51 | write(jlversion, section_prefix*"Cell order:\n") 52 | for uuid in cell_order 53 | write(jlversion, subsection_prefix*"$uuid"*"\n") 54 | end 55 | 56 | close(jlversion) -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | uuid = "6aea3074-96e1-11eb-a8b3-0242ac130003" 2 | version = "0.1.0" 3 | 4 | [deps] 5 | BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" 6 | CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" 7 | DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" 8 | DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" 9 | DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" 10 | Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c" 11 | GLM = "38e38edf-8417-5370-95a0-9cbb8c7f171a" 12 | Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" 13 | IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" 14 | JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" 15 | LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" 16 | MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" 17 | Plotly = "58dd65bb-95f3-509e-9936-c39a10fdeae7" 18 | PlotlyBase = "a03496cd-edff-5a9b-9e67-9cda94a718b5" 19 | PlotlyKaleido = "f2990250-8cf9-495f-b13a-cce12b45703c" 20 | Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" 21 | Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781" 22 | PrettyPrinting = "54e16d92-306c-5ea0-a30b-337be88ac337" 23 | PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" 24 | QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" 25 | SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" 26 | Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" 27 | Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" 28 | YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" 29 | 30 | [compat] 31 | JuliaFormatter = "0.6.2" 32 | julia = "1" 33 | 34 | [extras] 35 | JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" 36 | Test = "eaa6fafa-8011-46e2-b288-c2f1e2a8ee56" 37 | 38 | [targets] 39 | test = ["Test"] 40 | -------------------------------------------------------------------------------- /notebooks/MPI-Heat-Transfer-2D/io.jl: -------------------------------------------------------------------------------- 1 | using Printf 2 | 3 | function write_to_disk(x::Array{Float64,1}, x_domains::Int, y_domains::Int, 4 | xcell::Int, ycell::Int, temp1::Float64, filename:: String) 5 | f = open(filename, "w") 6 | 7 | #add first x-boundary 8 | for k = 1:ycell*y_domains + 2 9 | print(f, lstrip(@sprintf("%15.11f", temp1))) 10 | if (k < ycell*y_domains + 2) 11 | print(f, " ") 12 | end 13 | end 14 | print(f, "\n") 15 | 16 | #then, add internal cells + y-boundaries 17 | c = 0 18 | for k = 1:x_domains 19 | for m = 1:xcell 20 | for i = 1:y_domains 21 | for j = 1:ycell 22 | c += 1 23 | if (i==1 && j==1) 24 | print(f, lstrip(@sprintf("%15.11f", temp1))) 25 | print(f, " ") 26 | end 27 | print(f, lstrip(@sprintf("%15.11f", x[(i-1) * x_domains * xcell * ycell + 28 | (k-1) * xcell * ycell + (j-1) * xcell + m]))) 29 | print(f, " ") 30 | if (i==y_domains && j==ycell) 31 | print(f, lstrip(@sprintf("%15.11f", temp1))) 32 | print(f, "\n") 33 | end 34 | end 35 | end 36 | end 37 | end 38 | 39 | #add second x-boundary 40 | for k = 1:ycell*y_domains + 2 41 | print(f, lstrip(@sprintf("%15.11f", temp1))) 42 | if (k < ycell*y_domains + 2) 43 | print(f, " ") 44 | end 45 | end 46 | 47 | close(f) 48 | end -------------------------------------------------------------------------------- /notebooks/MPI-Heat-Transfer-2D/example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## MPI Heat Transfer 2D\n", 8 | "\n", 9 | "Let's put it all together with a classic example.\n", 10 | "\n", 11 | "Credit goes to the [Difussion.jl](https://github.com/cbellei/Diffusion.jl) package" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "# This is a required step if using mpirun from the system\n", 21 | "using Pkg\n", 22 | "Pkg.add(\"MPI\")\n", 23 | "Pkg.build(\"MPI\")\n", 24 | "\n", 25 | "using MPI\n", 26 | "MPI.install_mpiexecjl(force=true)\n", 27 | "\n", 28 | "# MyBinder: \n", 29 | "run(`/srv/julia/pkg/bin/mpiexecjl -n 4 julia main.jl config.yaml`)\n", 30 | "# local: modify mpiexecjl location \n", 31 | "# run(`/home/wgodoy/.julia/bin/mpiexecjl -n 4 julia main.jl config.yaml`)" 32 | ] 33 | }, 34 | { 35 | "cell_type": "markdown", 36 | "metadata": {}, 37 | "source": [ 38 | "# Read data with DelimitedFiles and Plots" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "metadata": {}, 45 | "outputs": [], 46 | "source": [ 47 | "using DelimitedFiles\n", 48 | "using Plots\n", 49 | "gr()\n", 50 | "\n", 51 | "data = readdlm(\"output.dat\", ' ', Float64)\n", 52 | "\n", 53 | "heatmap(1:size(data,1),\n", 54 | " 1:size(data,2), data,\n", 55 | " c=cgrad([:blue, :white,:red, :yellow]),\n", 56 | " xlabel=\"x\", ylabel=\"y\",\n", 57 | " title=\"My Heat Map\")" 58 | ] 59 | } 60 | ], 61 | "metadata": { 62 | "kernelspec": { 63 | "display_name": "Julia 1.11.6", 64 | "language": "julia", 65 | "name": "julia-1.11" 66 | }, 67 | "language_info": { 68 | "file_extension": ".jl", 69 | "mimetype": "application/julia", 70 | "name": "julia", 71 | "version": "1.11.6" 72 | } 73 | }, 74 | "nbformat": 4, 75 | "nbformat_minor": 4 76 | } 77 | -------------------------------------------------------------------------------- /notebooks/00_Intro.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "0a2fbf14-a1f0-41b3-beed-e66f659542c1", 6 | "metadata": {}, 7 | "source": [ 8 | "# Introduction\n", 9 | "\n", 10 | "## Resources\n", 11 | "\n", 12 | "Refer frequently to the Julia [documentation](https://docs.julialang.org) where you can search for specific language and standard library features.\n", 13 | "\n", 14 | "The [manual](https://docs.julialang.org/en/v1/manual/getting-started/) is especially helpful for understanding the mental model of the language.\n", 15 | "\n", 16 | "Discover useful packages here: https://juliahub.com/ui/Packages, and learn more about packaging here: https://pkgdocs.julialang.org/.\n", 17 | "\n", 18 | "Most content material in this tutorial is taken from https://techytok.com/from-zero-to-julia/." 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "id": "281596ac-280c-4ce4-a5f1-295918b9a791", 24 | "metadata": {}, 25 | "source": [ 26 | "## Interesting topics not covered in these notebooks\n", 27 | "\n", 28 | "- `@code_llvm` and the like\n", 29 | "- metaprogramming\n", 30 | "- profiling tools\n", 31 | "- debugging\n", 32 | "- parallelization\n" 33 | ] 34 | }, 35 | { 36 | "cell_type": "markdown", 37 | "id": "b5472760-e4ab-4666-8cda-3df3c4a8b5aa", 38 | "metadata": {}, 39 | "source": [ 40 | "# Exercises\n", 41 | "\n", 42 | "Try these and your own ideas. These are intended as a starting point. Play. As we go along, you'll have more ideas.\n", 43 | "\n", 44 | "- Write a simple script defining a struct and a function on that struct.\n", 45 | "\n", 46 | " (example: define an animal from command-line arguments defining type, color,\n", 47 | " age, and sex, and pretty print the resulting struct instance)\n", 48 | "\n", 49 | "- Create a package for some simple functionality and an appropriate package test.\n", 50 | "\n", 51 | " (example: geometric shapes and properties)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "id": "d3f9b70c-3040-4c5d-983c-1898238a1a0c", 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [] 61 | } 62 | ], 63 | "metadata": { 64 | "kernelspec": { 65 | "display_name": "Julia 1.11.6", 66 | "language": "julia", 67 | "name": "julia-1.11" 68 | }, 69 | "language_info": { 70 | "file_extension": ".jl", 71 | "mimetype": "application/julia", 72 | "name": "julia", 73 | "version": "1.11.6" 74 | } 75 | }, 76 | "nbformat": 4, 77 | "nbformat_minor": 5 78 | } 79 | -------------------------------------------------------------------------------- /distinctions/04_Interfaces.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "b61304b8-698f-409d-b519-1e127d077b8f", 6 | "metadata": {}, 7 | "source": [ 8 | "# Programming Paradigms\n", 9 | "- Generic programming\n", 10 | "- Functional programming\n", 11 | "- Object-oriented concepts mapped to generic" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "fc8a2a13-9f69-49e1-8aef-d1da5eb8548f", 17 | "metadata": {}, 18 | "source": [ 19 | "# Interfaces\n", 20 | "\n", 21 | "Manual: [Interfaces](https://docs.julialang.org/en/v1/manual/interfaces/)\n", 22 | "\n", 23 | "In Julia, interfaces are informal and comprise free functions operating on a type.\n", 24 | "\n", 25 | "This flexibility can be very powerful. This, with the type system, enable Julia's remarkable composability.\n", 26 | "\n", 27 | "Simple examples here don't do it justice, but the manual page linked above is especially insightful.\n", 28 | "\n", 29 | "Let's fork their `Squares` example for illustration." 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "id": "6ee1ac4f-015c-4392-82f3-7934188e8eea", 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "struct Squares # <: AbstractVector{Int}\n", 40 | " N::Int\n", 41 | "end\n", 42 | "\n", 43 | "Base.size(S::Squares) = (S.N,)\n", 44 | "\n", 45 | "Base.getindex(S::Squares, i) = i*i\n", 46 | "\n", 47 | "# If you wanted to check bounds\n", 48 | "# Base.getindex(S::Squares, i) = 1 <= i <= S.N ? i*i : throw(BoundsError(S, i))\n", 49 | "\n", 50 | "\n", 51 | "S5 = Squares(5)\n", 52 | "@show length(S5)\n", 53 | "@show S5[begin]\n", 54 | "@show S5[end]\n", 55 | "\n", 56 | "for s in Squares(4)\n", 57 | " @show s\n", 58 | "end\n", 59 | ";" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "id": "f664f1b2-f101-4f58-841c-05dbd444c29d", 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "using LinearAlgebra\n", 70 | "\n", 71 | "@show Squares(3) + Squares(3)\n", 72 | "@show dot(Squares(4), Squares(4))\n", 73 | ";" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "id": "bdf6acc8-7d5d-4e4e-9092-4e3ba6cbdf10", 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [] 83 | } 84 | ], 85 | "metadata": { 86 | "kernelspec": { 87 | "display_name": "Julia 1.11.6", 88 | "language": "julia", 89 | "name": "julia-1.11" 90 | }, 91 | "language_info": { 92 | "file_extension": ".jl", 93 | "mimetype": "application/julia", 94 | "name": "julia", 95 | "version": "1.11.6" 96 | } 97 | }, 98 | "nbformat": 4, 99 | "nbformat_minor": 5 100 | } 101 | -------------------------------------------------------------------------------- /notebooks/11_DataFrames-Example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# DataFrames Example\n", 8 | "\n", 9 | "DataFrames is a collection of tools for working with tabular data in Julia. Think of pandas or R's Data frames.\n", 10 | "\n", 11 | "Resources:\n", 12 | "- https://github.com/bkamins/Julia-DataFrames-Tutorial/\n", 13 | "- https://dataframes.juliadata.org/v0.11/man/getting_started.html\n", 14 | "\n", 15 | "Let's go to functional code:" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": null, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "import CSV\n", 25 | "import DataFrames\n", 26 | "using Plots\n", 27 | "\n", 28 | "function plot_CSV_3col( filename::String )\n", 29 | " \n", 30 | " df = CSV.File(filename) |> DataFrames.DataFrame\n", 31 | " \n", 32 | " x = df[!,\"plots_cells\"]\n", 33 | " yL0 = df[!,3]\n", 34 | " yL1 = df[!,4]\n", 35 | " yL2 = df[!,5] \n", 36 | " z = df[!,\"caseID\"] \n", 37 | " \n", 38 | " fig2 = Plots.scatter( x, yL0, groups = z, label=\"L0\",\n", 39 | " marker = (:x, 5, 1.0),\n", 40 | " markercolor = [:blue :green :red :orange :pink :black],\n", 41 | " legendfontsize=6,\n", 42 | " legend = :outertopleft,\n", 43 | " xlabel = \"cumulative output ncells ( output_counter x ncells )\", \n", 44 | " ylabel = \"cumulative output data size (bytes)\", \n", 45 | " title = \"Amrex Castro hydro_test Sedov output size per Level\",\n", 46 | " size=(850,600),\n", 47 | " reuse = true\n", 48 | " )\n", 49 | " Plots.scatter!( x, yL1, groups = z, label= \"L1\", \n", 50 | " marker = (:+, 5, 1.0),\n", 51 | " markercolor = [:blue :green :red :orange :pink :black],\n", 52 | " )\n", 53 | " \n", 54 | " Plots.scatter!( x, yL2, groups = z, label=\"L2\", \n", 55 | " marker = (:star4, 5, 1.0),\n", 56 | " markercolor = [:blue :green :red :orange :pink :black],\n", 57 | " )\n", 58 | "end\n", 59 | "\n", 60 | "\n", 61 | "\n", 62 | "function main()\n", 63 | " filename = \"../data/plot_size_case4.csv\"\n", 64 | " plot_CSV_3col(filename)\n", 65 | "end\n", 66 | "\n", 67 | "main()" 68 | ] 69 | } 70 | ], 71 | "metadata": { 72 | "kernelspec": { 73 | "display_name": "Julia 1.11.6", 74 | "language": "julia", 75 | "name": "julia-1.11" 76 | }, 77 | "language_info": { 78 | "file_extension": ".jl", 79 | "mimetype": "application/julia", 80 | "name": "julia", 81 | "version": "1.11.6" 82 | } 83 | }, 84 | "nbformat": 4, 85 | "nbformat_minor": 4 86 | } 87 | -------------------------------------------------------------------------------- /notebooks/MPI-Heat-Transfer-2D/init.jl: -------------------------------------------------------------------------------- 1 | function init_values(x0::Array{Float64,2}, size_total_x::Int, size_total_y::Int, 2 | temp1_init::Float64, temp2_init::Float64) 3 | 4 | #Setup temp1_init on borders 5 | for i = 1:size_total_x - 1 6 | x0[i,1] = temp1_init 7 | x0[i,size_total_y] = temp1_init 8 | end 9 | 10 | for j = 1:size_total_y 11 | x0[1,j] = temp1_init 12 | x0[size_total_x, j] = temp1_init 13 | end 14 | 15 | for i = 2:size_total_x-1 16 | x0[i,2] = temp1_init 17 | x0[i,size_total_y-1] = temp1_init 18 | end 19 | 20 | for j = 2:size_total_y-1 21 | x0[2,j] = temp1_init 22 | x0[size_total_x-1,j] = temp1_init 23 | end 24 | 25 | #Setup temp2_init inside 26 | for i = 3:size_total_x-2 27 | for j = 3:size_total_y-2 28 | x0[i,j] = temp2_init 29 | end 30 | end 31 | 32 | return 33 | end 34 | 35 | function neighbors(my_id::Int, nproc::Int, nx_domains::Int, ny_domains::Int) 36 | #find all processes that are my neighbors 37 | id_pos = Array{Int,2}(undef, nx_domains, ny_domains) 38 | for id = 0:nproc-1 39 | n_row = (id+1) % nx_domains > 0 ? (id+1) % nx_domains : nx_domains 40 | n_col = ceil(Int, (id + 1) / nx_domains) 41 | if (id == my_id) 42 | global my_row = n_row 43 | global my_col = n_col 44 | end 45 | id_pos[n_row, n_col] = id 46 | end 47 | 48 | neighbor_N = my_row + 1 <= nx_domains ? my_row + 1 : -1 49 | neighbor_S = my_row - 1 > 0 ? my_row - 1 : -1 50 | neighbor_E = my_col + 1 <= ny_domains ? my_col + 1 : -1 51 | neighbor_W = my_col - 1 > 0 ? my_col - 1 : -1 52 | 53 | neighbors = Dict{String,Int}() 54 | neighbors["N"] = neighbor_N >= 0 ? id_pos[neighbor_N, my_col] : -1 55 | neighbors["S"] = neighbor_S >= 0 ? id_pos[neighbor_S, my_col] : -1 56 | neighbors["E"] = neighbor_E >= 0 ? id_pos[my_row, neighbor_E] : -1 57 | neighbors["W"] = neighbor_W >= 0 ? id_pos[my_row, neighbor_W] : -1 58 | 59 | return neighbors 60 | end 61 | 62 | function process_coordinates!(xs::Array{Int}, ys::Array{Int}, xe::Array{Int}, 63 | ye::Array{Int}, xcell::Int, ycell::Int, nx_domains::Int, ny_domains::Int, nproc::Int) 64 | 65 | #Computation of starting ys,ye on (Ox) standard axis 66 | #for the first column of global domain, 67 | #Convention x(i,j) with i row and j column 68 | ys[1:nx_domains] .= 3 69 | #Here, ye[1:x_domains] = 3 + ycell-1 70 | ye[1:nx_domains] = ys[1:nx_domains] .+ ycell .- 1 71 | 72 | #Computation of ys,ye on (Ox) standard axis 73 | #for all other cells of global domain 74 | for i = 1:ny_domains-1 75 | ys[i*nx_domains+1:(i+1)*nx_domains] = ys[(i-1)*nx_domains+1:i*nx_domains] .+ ycell .+ 2 76 | ye[i*nx_domains+1:(i+1)*nx_domains] = ys[i*nx_domains+1:(i+1)*nx_domains] .+ ycell .- 1 77 | end 78 | 79 | #Computation of starting xs,xe on (Oy) standard axis 80 | #for the first row of global domain, 81 | #Convention x(i,j) with i row and j column 82 | for i = 1:ny_domains 83 | xs[(i-1)*nx_domains+1] = 3 84 | #Here, xe(i*x_domains) = 2+xcell-1 85 | xe[(i-1)*nx_domains+1] = xs[(i-1)*nx_domains+1] + xcell - 1 86 | end 87 | 88 | #Computation of xs,xe on (Oy) standard axis 89 | #for all other cells of global domain 90 | for i = 1:ny_domains 91 | for j = 2:nx_domains 92 | xs[(i-1)*nx_domains+j] = xs[(i-1)*nx_domains+(j-1)] + xcell + 2 93 | xe[(i-1)*nx_domains+j] = xs[(i-1)*nx_domains+j] + xcell - 1 94 | end 95 | end 96 | 97 | return xs, xe, ys, ye 98 | end -------------------------------------------------------------------------------- /distinctions/Julia_Distinctions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "b3f52d37-5356-4b9e-8c0a-571e6c15e142", 6 | "metadata": {}, 7 | "source": [ 8 | "# What makes Julia different?\n", 9 | "Assuming you already know how to write code in another language (say Python or C++), the basics of programming in Julia are very straightforward. Here we want to discuss some of the ways that Julia might differ and behave in ways you might not expect.\n", 10 | "\n", 11 | "Manual: [Noteworthy differences](https://docs.julialang.org/en/v1/manual/noteworthy-differences/)\n", 12 | "\n", 13 | "__Sections__:\n", 14 | "\n", 15 | "0. [Why Julia](00_Why_Julia.ipynb)\n", 16 | "1. [Arrays](01_Arrays.ipynb)\n", 17 | "2. [Type System](02_Type_System.ipynb)\n", 18 | "3. [Functions](03_Functions.ipynb)\n", 19 | "4. [Interfaces and Paradigms](04_Interfaces.ipynb)\n", 20 | "5. [Modules and Packaging](05_Modules_and_Packaging.ipynb)" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "id": "7439ee69-d45f-45fd-8fae-4084ffca2f42", 26 | "metadata": {}, 27 | "source": [ 28 | "## Resources\n", 29 | "\n", 30 | "- The Julia [documentation](https://docs.julialang.org) (you can search for specific language and standard library features)\n", 31 | "- The Julia [manual](https://docs.julialang.org/en/v1/manual/getting-started/) (especially helpful for understanding the mental model of the language as well as finer details of each feature)\n", 32 | "- Discover useful [packages](https://juliahub.com/ui/Packages)\n", 33 | "- Pkg [documentation](https://pkgdocs.julialang.org)\n", 34 | "- More listed [here](https://juliaornl.github.io/TutorialJuliaHPC/)." 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "id": "74d1e2cd-6950-449f-96ca-488f10bb11a2", 40 | "metadata": {}, 41 | "source": [ 42 | "## Expeditious Grammar\n", 43 | "\n", 44 | "Manual: [Control Flow](https://docs.julialang.org/en/v1/manual/control-flow/),\n", 45 | "[Scopes](https://docs.julialang.org/en/v1/manual/variables-and-scoping/)\n", 46 | "\n", 47 | "- Consistent use of `end` keyword (rather than braces or tabs)\n", 48 | "- No \"switch\" syntax" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "id": "501300c6-0614-4806-aff7-715aa52c18fc", 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "@show typeof(ARGS) # global container for command-line arguments\n", 59 | "\n", 60 | "function foo()\n", 61 | " flag = false\n", 62 | " for i in 1:10\n", 63 | " if flag\n", 64 | " break\n", 65 | " elseif i < 10\n", 66 | " continue\n", 67 | " else\n", 68 | " flag = true\n", 69 | " end\n", 70 | " end\n", 71 | " return nothing\n", 72 | "end\n", 73 | "\n", 74 | "try\n", 75 | " error(\"this is an error\")\n", 76 | "catch e\n", 77 | " println(e.msg)\n", 78 | " rethrow()\n", 79 | "end\n", 80 | "\n", 81 | "; # this is just for the notebook" 82 | ] 83 | } 84 | ], 85 | "metadata": { 86 | "kernelspec": { 87 | "display_name": "Julia 1.11.6", 88 | "language": "julia", 89 | "name": "julia-1.11" 90 | }, 91 | "language_info": { 92 | "file_extension": ".jl", 93 | "mimetype": "application/julia", 94 | "name": "julia", 95 | "version": "1.11.6" 96 | } 97 | }, 98 | "nbformat": 4, 99 | "nbformat_minor": 5 100 | } 101 | -------------------------------------------------------------------------------- /notebooks/12_FluxML-Example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# FluxML Example\n", 8 | "\n", 9 | "Flux is the Julia Machine Learning. The website comes with plenty of resources: https://fluxml.ai/\n", 10 | "Let's jump to functional code. " 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": null, 16 | "metadata": {}, 17 | "outputs": [], 18 | "source": [ 19 | "import Flux\n", 20 | "import Statistics\n", 21 | "\n", 22 | "import Plots;\n", 23 | "using LaTeXStrings\n", 24 | "\n", 25 | "function _flux_linear_fit(nepocs, X, Y, W0, b0, plot)\n", 26 | " # Prepare the training data (horizontal concatenation)\n", 27 | " Xd = reduce(hcat, X)\n", 28 | " Yd = reduce(hcat, Y)\n", 29 | " data = [(Xd, Yd)]\n", 30 | " println(\"Data\", data)\n", 31 | "\n", 32 | " # Define the model. Weight and bias are arrays initialized to some values.\n", 33 | " # Training a model will adjust weights and biases.\n", 34 | " model = Flux.Dense(1, 1) # model = W x + b\n", 35 | " model.weight .= [W0]\n", 36 | " model.bias .= [b0]\n", 37 | "\n", 38 | " # Define the loss function\n", 39 | " loss(model, x, y) = Statistics.mean((model(x) .- y) .^ 2)\n", 40 | "\n", 41 | " # Define an optimizer\n", 42 | " # optimizer = Flux.Descent(1)\n", 43 | " # optimizer = Flux.ADAM()\n", 44 | " optimizer = Flux.ADAM(1, (0.99, 0.999))\n", 45 | " opt = Flux.setup(optimizer, model)\n", 46 | "\n", 47 | " Yd_0 = model(Xd)\n", 48 | " println(\"Initial solution: \", Yd_0)\n", 49 | "\n", 50 | " # Plot the initial data\n", 51 | " if plot\n", 52 | " Plots.plot(X, Y, st=:scatter, label=\"y\", legend=:topleft)\n", 53 | " end\n", 54 | "\n", 55 | " for iter = 1:nepocs\n", 56 | " Flux.train!(loss, model, data, opt)\n", 57 | "\n", 58 | " # Plot the evolution of the model\n", 59 | " if iter % 10 == 0\n", 60 | " if plot\n", 61 | " if iter <= 100\n", 62 | " Yd_nE = model(Xd)\n", 63 | " Plots.plot!(Xd', Yd_nE', lc=:blue, label=L\"y_{%$iter}\")\n", 64 | " elseif 100 < iter && iter <= 1000 && iter % 200 == 0\n", 65 | " Yd_nE = model(Xd)\n", 66 | " Plots.plot!(Xd', Yd_nE', lc=:orange, label=L\"y_{%$iter}\")\n", 67 | " elseif 1000 < iter && iter <= nepocs && iter % 500 == 0\n", 68 | " Yd_nE = model(Xd)\n", 69 | " Plots.plot!(Xd', Yd_nE', lc=:red, label=L\"y_{%$iter}\")\n", 70 | " end\n", 71 | " end\n", 72 | " println(\"Epoch: \", iter, \" Loss: \", loss(model, Xd, Yd))\n", 73 | " end\n", 74 | " end\n", 75 | "\n", 76 | " Yd_nE = model(Xd)\n", 77 | " println(\"Final solution: \", Yd_nE)\n", 78 | "\n", 79 | " if plot\n", 80 | " Plots.plot!(Xd', Yd_0', lc=:green, label=L\"y_0\")\n", 81 | " Plots.plot!(xlabel=\"x\", ylabel=\"y\", title=\"Flux.jl Linear Fit\")\n", 82 | " end\n", 83 | "\n", 84 | "end\n", 85 | "\n", 86 | "function test()\n", 87 | " x = Float32[ 256, 2_100, 4_096, 512, 5_500, 8_192]\n", 88 | " y = Float32[435_867, 2_959_963, 1_475_489, 1_485_569, 2_592_234, 4_518_030]\n", 89 | "\n", 90 | " W0 = 20.0\n", 91 | " b0 = 500.0\n", 92 | "\n", 93 | " nepocs = 2_000\n", 94 | " plot = true\n", 95 | "\n", 96 | " _flux_linear_fit(nepocs, x, y, W0, b0, plot)\n", 97 | "end\n", 98 | "\n", 99 | "test()\n" 100 | ] 101 | } 102 | ], 103 | "metadata": { 104 | "kernelspec": { 105 | "display_name": "Julia 1.11.6", 106 | "language": "julia", 107 | "name": "julia-1.11" 108 | }, 109 | "language_info": { 110 | "file_extension": ".jl", 111 | "mimetype": "application/julia", 112 | "name": "julia", 113 | "version": "1.11.6" 114 | } 115 | }, 116 | "nbformat": 4, 117 | "nbformat_minor": 4 118 | } 119 | -------------------------------------------------------------------------------- /notebooks/09_Units.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Units\n", 8 | "\n", 9 | "In this lesson we will learn how to use units of measurement in Julia. We will learn how to convert from one unit to another and we will see how to write functions which can deal with units.\n", 10 | "\n", 11 | "The package used to deal with units of measurement in Julia is [Unitful.jl](https://github.com/PainterQubits/Unitful.jl).\n", 12 | "In order to add units of measurement to numbers we use the notation `u\"unit\"`" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": null, 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "using Unitful\n", 22 | "\n", 23 | "function UnitsFun()\n", 24 | " one_meter = 1u\"m\"\n", 25 | " println(one_meter, \" type of one meter: \", typeof(one_meter))\n", 26 | " \n", 27 | " # convert\n", 28 | " one_kilometer = uconvert(u\"km\", one_meter)\n", 29 | " println(one_kilometer, \" type of one_kilometer: \", typeof(one_kilometer))\n", 30 | " # strip\n", 31 | " c = ustrip(u\"m\", one_meter)\n", 32 | " println(c, \" type of c: \", typeof(c))\n", 33 | "end\n", 34 | "\n", 35 | "UnitsFun()" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "It is possible to write functions that accept arguments with units without any particular change:" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "function compute_speed(Δx, Δt)\n", 52 | " return Δx/Δt\n", 53 | "end\n", 54 | "\n", 55 | "println( compute_speed(1u\"km\", 2u\"s\") )" 56 | ] 57 | }, 58 | { 59 | "cell_type": "markdown", 60 | "metadata": {}, 61 | "source": [ 62 | "It is also possible to write functions with type annotations specific for arguments with units. Unitful provides many abstract types such as `Unitful.Length` or `Unitful.Time`, which are useful for type annotation of function arguments:" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "function compute_speed(Δx::Unitful.Length, Δt::Unitful.Time)\n", 72 | " return uconvert(u\"m/s\", Δx/Δt)\n", 73 | "end\n", 74 | "\n", 75 | "println( compute_speed(1u\"km\", 2u\"s\") )" 76 | ] 77 | }, 78 | { 79 | "cell_type": "markdown", 80 | "metadata": {}, 81 | "source": [ 82 | "Although it may be tempting, it is better to refrain from using abstract types inside type definitions. When defining a struct, you can use typeof to get the right type for the annotations:" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "struct Person\n", 92 | " height::typeof(1.0u\"m\")\n", 93 | " mass::typeof(1.0u\"kg\")\n", 94 | "end" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "Similarly, in Julia it is possible to compute integrals numerically taking into account units of measurements. For example, if we integrate a velocity over an interval of time we will get the distance covered:" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "using QuadGK\n", 111 | "velocity(t::Unitful.Time) = 2u\"m/s^2\"*t + 1u\"m/s\"\n", 112 | "\n", 113 | "println( quadgk(velocity, 0u\"s\", 3u\"s\")[1] )\n" 114 | ] 115 | }, 116 | { 117 | "cell_type": "markdown", 118 | "metadata": {}, 119 | "source": [ 120 | "## Conclusions\n", 121 | "\n", 122 | "In this lesson we have learnt how to add units of measurement using Unitful.jl and how to write functions which naturally use units. Furthermore, we have seen how QuadGK handles correctly units inside integrals." 123 | ] 124 | } 125 | ], 126 | "metadata": { 127 | "kernelspec": { 128 | "display_name": "Julia 1.11.6", 129 | "language": "julia", 130 | "name": "julia-1.11" 131 | }, 132 | "language_info": { 133 | "file_extension": ".jl", 134 | "mimetype": "application/julia", 135 | "name": "julia", 136 | "version": "1.11.6" 137 | } 138 | }, 139 | "nbformat": 4, 140 | "nbformat_minor": 4 141 | } 142 | -------------------------------------------------------------------------------- /notebooks/MPI-Heat-Transfer-2D/update.jl: -------------------------------------------------------------------------------- 1 | function updateBound!(u::Array{Float64,2}, size_total_x, size_total_y, neighbors, comm, 2 | me, xs, ys, xe, ye, xcell, ycell, nproc) 3 | 4 | mep1 = me + 1 5 | 6 | #assume, to start with, that this process is not going to receive anything 7 | rreq = Dict{String, MPI.Request}( 8 | "N" => MPI.REQUEST_NULL, 9 | "S" => MPI.REQUEST_NULL, 10 | "E" => MPI.REQUEST_NULL, 11 | "W" => MPI.REQUEST_NULL 12 | ) 13 | recv = Dict{String, Array{Float64,1}}() 14 | ghost_boundaries = Dict{String, Any}( 15 | "N" => (xe[mep1]+1, ys[mep1]:ye[mep1]), 16 | "S" => (xs[mep1]-1, ys[mep1]:ye[mep1]), 17 | "E" => (xs[mep1]:xe[mep1], ye[mep1]+1), 18 | "W" => (xs[mep1]:xe[mep1], ys[mep1]-1) 19 | ) 20 | is_receiving = Dict{String, Bool}("N" => false, "S" => false, "E" => false, "W" => false) 21 | 22 | #send 23 | neighbors["N"] >=0 && MPI.Isend(u[xe[mep1], ys[mep1]:ye[mep1]], neighbors["N"], me + 40, comm) 24 | neighbors["S"] >=0 && MPI.Isend(u[xs[mep1], ys[mep1]:ye[mep1]], neighbors["S"], me + 50, comm) 25 | neighbors["E"] >=0 && MPI.Isend(u[xs[mep1]:xe[mep1], ye[mep1]], neighbors["E"], me + 60, comm) 26 | neighbors["W"] >=0 && MPI.Isend(u[xs[mep1]:xe[mep1], ys[mep1]], neighbors["W"], me + 70, comm) 27 | 28 | #receive 29 | if (neighbors["N"] >= 0) 30 | recv["N"] = Array{Float64,1}(undef, ycell) 31 | is_receiving["N"] = true 32 | rreq["N"] = MPI.Irecv!(recv["N"], neighbors["N"], neighbors["N"] + 50, comm) 33 | end 34 | if (neighbors["S"] >= 0) 35 | recv["S"] = Array{Float64,1}(undef, ycell) 36 | is_receiving["S"] = true 37 | rreq["S"] = MPI.Irecv!(recv["S"], neighbors["S"], neighbors["S"] + 40, comm) 38 | end 39 | if (neighbors["E"] >= 0) 40 | recv["E"] = Array{Float64,1}(undef, xcell) 41 | is_receiving["E"] = true 42 | rreq["E"] = MPI.Irecv!(recv["E"], neighbors["E"], neighbors["E"] + 70, comm) 43 | end 44 | if (neighbors["W"] >= 0) 45 | recv["W"] = Array{Float64,1}(undef, xcell) 46 | is_receiving["W"] = true 47 | rreq["W"] = MPI.Irecv!(recv["W"], neighbors["W"], neighbors["W"] + 60, comm) 48 | end 49 | 50 | MPI.Waitall!([rreq[k] for k in keys(rreq)]) 51 | for (k, v) in is_receiving 52 | if v 53 | u[ghost_boundaries[k][1], ghost_boundaries[k][2]] = recv[k] 54 | end 55 | end 56 | end 57 | 58 | 59 | function computeNext!(u0::Array{Float64,2}, u::Array{Float64,2}, 60 | size_total_x::Int, size_total_y::Int, dt::Float64, hx::Float64, hy::Float64, 61 | me::Int, xs::Array{Int,1}, ys::Array{Int,1}, xe::Array{Int,1}, ye::Array{Int,1}, 62 | nproc::Int, k0::Float64) 63 | 64 | # The stencil of the explicit operator for the heat equation 65 | # on a regular rectangular grid using a five point finite difference 66 | # scheme in space is : 67 | # 68 | # | weightx * u[i-1][j] | 69 | # | | 70 | # | weighty * u[i][j-1] (diagx * weightx + diagy * weighty) * u[i][j] weighty * u[i][j+1] | 71 | # | | 72 | # | weightx * u[i+1][j] | 73 | 74 | mep1 = me + 1 75 | 76 | diagx = Float64(-2.0 + hx * hx / (2 * k0 * dt)) 77 | diagy = Float64(-2.0 + hy * hy / (2 * k0 * dt)) 78 | weightx = Float64(k0 * dt / (hx * hx)) 79 | weighty = Float64(k0 * dt / (hy * hy)) 80 | 81 | #Perform an explicit update on the points within the domain. 82 | #Optimization : inner loop on column index (second index) since 83 | #Julia is row major 84 | diff = 0.0 85 | for i = xs[mep1]:xe[mep1] 86 | for j = ys[mep1]:ye[mep1] 87 | u[i,j] = weightx * (u0[i-1,j] + u0[i+1,j] + u0[i,j]*diagx) + 88 | weighty * (u0[i,j-1] + u0[i,j+1] + u0[i,j]*diagy) 89 | end 90 | end 91 | 92 | #Compute the difference into domain for convergence. 93 | #Update the value u0(i,j). 94 | #Optimization : inner loop on column index (second index) since 95 | #Julia is row major 96 | diff = 0.0 97 | for j = ys[mep1]:ye[mep1] 98 | for i = xs[mep1]:xe[mep1] 99 | ldiff = u0[i,j] - u[i,j] 100 | diff = diff + ldiff * ldiff 101 | u0[i,j] = u[i,j] 102 | end 103 | end 104 | 105 | return diff 106 | end -------------------------------------------------------------------------------- /distinctions/00_Why_Julia.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "935d3cfe-3776-456e-8fdb-feba10ec580a", 6 | "metadata": {}, 7 | "source": [ 8 | "# Why Julia?\n", 9 | "\n", 10 | "## Simple answer\n", 11 | "Maybe it's enough to say that you can code like in Python or MATLAB, but you get good performance and dependency management isn't a nightmare.\n", 12 | "\n", 13 | "## Just-in-Time (JIT) Compilation\n", 14 | "\n", 15 | "Julia is a programming language that superficially looks like an interpreted\n", 16 | "language and mostly behaves like one. But before each function is executed it\n", 17 | "will be compiled *just in time*.\n", 18 | "\n", 19 | "Thus you get the flexibility of an interpreted language and the execution speed\n", 20 | "of a compiled language at the cost of waiting a bit longer for the first\n", 21 | "execution of any function.\n", 22 | "\n", 23 | "## Built-in Engineering\n", 24 | "\n", 25 | "Julia has software engineering features integrated into the language.\n", 26 | "\n", 27 | "- dependency management\n", 28 | "- packaging\n", 29 | "- documentation\n", 30 | "- testing\n", 31 | "- profiling\n", 32 | "\n", 33 | "## Designed for Scientists\n", 34 | "\n", 35 | "Julia includes many tools commonly used in scientific computing.\n", 36 | "\n", 37 | "- multi-dimensional arrays\n", 38 | "- linear algebra (including sparse arrays)\n", 39 | "- random numbers\n", 40 | "- statistics\n", 41 | "\n", 42 | "(and, of course, many other things through easily-accessible packages).\n", 43 | "\n", 44 | "## Package Composition\n", 45 | "\n", 46 | "There is another aspect of Julia that makes it interesting and that is the way\n", 47 | "packages compose. This is captured the best by an analogy from [Sam\n", 48 | "Urmy](https://github.com/ElOceanografo):\n", 49 | "\n", 50 | "> Say you want a toy truck.\n", 51 | ">\n", 52 | "> The Python/R solution is to look for the appropriate package–like buying a\n", 53 | "> Playmobil truck. It comes pre-manufactured, well-engineered and tested, and\n", 54 | "> does 95% of what you would ever want a toy truck to do.\n", 55 | ">\n", 56 | "> The Fortran/C solution is to build the truck from scratch. This allows total\n", 57 | "> customization and you can optimize the features and performance however you\n", 58 | "> want. The downside is that it takes more time, you need woodworking skills,\n", 59 | "> and might hurt yourself with the power tools.\n", 60 | ">\n", 61 | "> The Julia solution is like Legos. You can get the truck kit if you want–which\n", 62 | "> will require a bit more assembly than the Playmobil, but way less than\n", 63 | "> building it from scratch. Or, you can get the component pieces and assemble\n", 64 | "> the truck to your own specs. There’s no limit to what you can put together,\n", 65 | "> and because the pieces all have the same system of bumps, everything snaps\n", 66 | "> together quickly and easily.\n", 67 | ">\n", 68 | ">

\n", 69 | "> ![](https://global.discourse-cdn.com/julialang/original/3X/5/2/52e63856ad9e23876cda4297a04171879fa625b4.jpeg){width=\"600\" height=\"400\" alt=\"\"}\n", 70 | ">

\n", 71 | ">\n", 72 | "> OK, sure. Toy trucks are like linear algebra, though, a common request, and\n", 73 | "> every “toy system” will have an implementation that works basically fine. But\n", 74 | "> what if you want a time-traveling sailing/space ship with lasers AND dragon\n", 75 | "> wings? And it should be as easy to build and use as a basic dump truck?\n", 76 | ">\n", 77 | ">

\n", 78 | "> \"\"\n", 79 | ">

\n", 80 | ">\n", 81 | "> There’s a reason that only Lego ever made anything like Dr. Cyber’s Flying\n", 82 | "> Time Vessel!\n", 83 | "\n", 84 | "Originally posted on [Discourse](https://discourse.julialang.org/t/what-is-the-advantage-of-julia-over-fortran/65964/101).\n", 85 | "\n", 86 | "## HPC Made Easier\n", 87 | "Julia brings performance and productivity closer. Many high-performance computing tools are made more accessible with native packages providing higher-level abstractions without needing to sacrifice performance (thanks to JIT compilation).\n", 88 | "\n", 89 | "More thoughts [here](https://juliaornl.github.io/TutorialJuliaHPC/#why-julia)." 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "id": "834770ae-4bfe-48b9-b9ee-9274b7cf95df", 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [] 99 | } 100 | ], 101 | "metadata": { 102 | "kernelspec": { 103 | "display_name": "Julia 1.11.6", 104 | "language": "julia", 105 | "name": "julia-1.11" 106 | }, 107 | "language_info": { 108 | "file_extension": ".jl", 109 | "mimetype": "application/julia", 110 | "name": "julia", 111 | "version": "1.11.6" 112 | } 113 | }, 114 | "nbformat": 4, 115 | "nbformat_minor": 5 116 | } 117 | -------------------------------------------------------------------------------- /notebooks/10_Parallel-Computing.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Parallel Computing\n", 8 | "\n", 9 | "In this lesson we will deal with parallel computing, which is a type of computation in which many calculations or the execution of processes are carried out simultaneously on different CPU cores. We will show the differences between multi-threading and multi-processing and we will learn how those techniques are implemented in Julia.\n", 10 | "\n", 11 | "For this lesson you will need Julia version 1.3 or above.\n", 12 | "\n", 13 | "## Contents\n", 14 | "\n", 15 | "- [Intro](#Intro)\n", 16 | "- [Data based](#Data-based)\n", 17 | "- [Task based](#Task-based)\n", 18 | "\n", 19 | "\n", 20 | "## Intro \n", 21 | "In order to use multi-threading we need to start Julia with a number of threads equal to the number of you CPU cores. If you are using the Juno IDE it will automatically start Julia with the appropriate number of threads. If you are working from the REPL, you need to manually start Julia from the command line.\n", 22 | "\n", 23 | "```\n", 24 | "$ export JULIA_NUM_THREADS=4\n", 25 | "$ julia\n", 26 | "```" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": null, 32 | "metadata": {}, 33 | "outputs": [], 34 | "source": [ 35 | "# Check number of threads\n", 36 | "Threads.nthreads()" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "## Data based\n", 44 | "\n", 45 | "The first step to know if it's worth applying parallel processing is to measure any potential benefit by timing bottlenecks in code. \n", 46 | "It is possible to measure the execution time of a function using the macro `@time` from the `BenchmarkTools` package." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "using SpecialFunctions\n", 56 | "using BenchmarkTools\n", 57 | "\n", 58 | "function besselFun()\n", 59 | " x = range(0,1000, length=10000000)\n", 60 | " results = zeros(length(x))\n", 61 | " results .= besselj1.(x)\n", 62 | " return\n", 63 | "end\n", 64 | "\n", 65 | "@time besselFun()" 66 | ] 67 | }, 68 | { 69 | "cell_type": "markdown", 70 | "metadata": {}, 71 | "source": [ 72 | "Rewrite this funciton in a loop" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "function besselFun()\n", 82 | " x = range(0,1000, length=10000000)\n", 83 | " results = zeros(length(x))\n", 84 | " for i in 1:length(x)\n", 85 | " results[i] = besselj1(x[i])\n", 86 | " end\n", 87 | " return\n", 88 | "end\n", 89 | "\n", 90 | "@time besselFun()" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "In that loop every iteration is independent from the next one: this hints the possibility to make the code parallel. To achieve parallelization, we import the Threads module and call the `@threads` macro." 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "function besselFun()\n", 107 | " x = range(0,1000, length=10000000)\n", 108 | " results = zeros(length(x))\n", 109 | "Threads.@threads for i in 1:length(x)\n", 110 | " results[i] = besselj1(x[i])\n", 111 | " end\n", 112 | " return\n", 113 | "end\n", 114 | "\n", 115 | "@time besselFun()" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": {}, 121 | "source": [ 122 | "## Task based\n", 123 | "\n", 124 | "Use the `@spawn` macro and the fetch function\n" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "import Base.Threads.@spawn\n", 134 | "using BenchmarkTools\n", 135 | "\n", 136 | "function slow_func(x)\n", 137 | " sleep(0.01) #sleep for 5ms\n", 138 | " return x\n", 139 | "end\n", 140 | "\n", 141 | "println(\"Time for @spawn/fetch code:\")\n", 142 | "@time let\n", 143 | " a = @spawn slow_func(2)\n", 144 | " b = @spawn slow_func(4)\n", 145 | " c = @spawn slow_func(42)\n", 146 | " d = @spawn slow_func(12)\n", 147 | " res = fetch(a) .+ fetch(b) .* fetch(c) ./ fetch(d)\n", 148 | " println(\"Result: \", res)\n", 149 | "end\n", 150 | "\n", 151 | "println(\"Time for serial code:\")\n", 152 | "@time let\n", 153 | " a = slow_func(2)\n", 154 | " b = slow_func(4)\n", 155 | " c = slow_func(42)\n", 156 | " d = slow_func(12)\n", 157 | " res = a .+ b .* c ./ d\n", 158 | " println(\"Result: \", res)\n", 159 | "end" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": {}, 166 | "outputs": [], 167 | "source": [] 168 | } 169 | ], 170 | "metadata": { 171 | "kernelspec": { 172 | "display_name": "Julia 1.11.6", 173 | "language": "julia", 174 | "name": "julia-1.11" 175 | }, 176 | "language_info": { 177 | "file_extension": ".jl", 178 | "mimetype": "application/julia", 179 | "name": "julia", 180 | "version": "1.11.6" 181 | } 182 | }, 183 | "nbformat": 4, 184 | "nbformat_minor": 4 185 | } 186 | -------------------------------------------------------------------------------- /notebooks/MPI-Heat-Transfer-2D/main.jl: -------------------------------------------------------------------------------- 1 | import MPI 2 | 3 | include("types.jl") 4 | include("init.jl") 5 | include("get_sim_params.jl") 6 | include("update.jl") 7 | include("io.jl") 8 | include("get_sim_params.jl") 9 | 10 | 11 | function main() 12 | #MPI Initialization 13 | MPI.Init() 14 | comm = MPI.COMM_WORLD 15 | my_id = MPI.Comm_rank(comm) 16 | nproc = MPI.Comm_size(comm) 17 | println("Number of MPI processes: ", nproc) 18 | 19 | #temp1_init: temperature init on borders 20 | temp1_init = 10.0 21 | #temp2_init: temperature init inside domain 22 | temp2_init = -10.0 23 | #diffusion coefficient 24 | k0 = Float64(1) 25 | 26 | #define rank of root process 27 | root = 0 28 | 29 | #simulation parameters 30 | params_int = Array{Int64}(undef, 5) 31 | params_double = Array{Float64}(undef, 2) 32 | if (my_id == root) 33 | params_int, params_double, output_path = get_sim_params(ARGS) 34 | end 35 | 36 | MPI.Barrier(comm) 37 | MPI.Bcast!(params_int, 5, 0, comm) 38 | MPI.Bcast!(params_double, 2, 0, comm) 39 | 40 | size_x = params_int[1] 41 | size_y = params_int[2] 42 | nx_domains = params_int[3] 43 | ny_domains = params_int[4] 44 | maxStep = params_int[5] 45 | dt1 = params_double[1] 46 | epsilon = params_double[2] 47 | 48 | #Warning message if dimensions and number of processes don't match 49 | if ((my_id == root) && (nproc != (nx_domains * ny_domains))) 50 | println("ERROR - Number of processes not equal to number of subdomains") 51 | println("nprocs: ", nproc, " domains: ", nx_domains * ny_domains ) 52 | end 53 | 54 | #Various other variables 55 | size_global_x = size_x + 2 56 | size_global_y = size_y + 2 57 | hx = Float64(1.0 / size_global_x) 58 | hy = Float64(1.0 / size_global_y) 59 | dt2 = 0.25 * min(hx, hy)^2 / k0 #fraction of CFL condition 60 | size_total_x = size_x + 2 * nx_domains + 2 #including ghost cells 61 | size_total_y = size_y + 2 * ny_domains + 2 #including ghost cells 62 | 63 | #Take a right time step for convergence 64 | if (dt1 >= dt2) 65 | if (my_id == 0) 66 | println() 67 | println("Time step too large ==> Taking convergence criterion") 68 | end 69 | dt = dt2 70 | else 71 | dt = dt1 72 | end 73 | 74 | #2D solution including ghost cells 75 | u0 = twoDArray(Float64, size_total_x, size_total_y) 76 | u = twoDArray(Float64, size_total_x, size_total_y) 77 | 78 | #Allocate coordinates of processes (start cell, end cell) 79 | xs = oneDArray(Int, nproc) 80 | xe = oneDArray(Int, nproc) 81 | ys = oneDArray(Int, nproc) 82 | ye = oneDArray(Int, nproc) 83 | 84 | #Size of each physical domain 85 | xcell = Int(size_x / nx_domains) 86 | ycell = Int(size_y / ny_domains) 87 | 88 | #allocate flattened (1D) local physical solution (i.e., relative to sub-domain) 89 | u_local = oneDArray(Float64, xcell * ycell) 90 | #allocate flattened (1D) global physical solution 91 | u_global = oneDArray(Float64, size_x * size_y) 92 | 93 | #find processes surrounding mine (if they exist) 94 | my_neighbors = neighbors(my_id, nproc, nx_domains, ny_domains) 95 | 96 | #compute coordinates of processes for each sub-domain 97 | process_coordinates!(xs, ys, xe, ye, xcell, ycell, nx_domains, ny_domains, nproc) 98 | 99 | #initialize domain 100 | init_values(u0, size_total_x, size_total_y, temp1_init, temp2_init) 101 | 102 | #update ghost cells 103 | updateBound!(u0, size_total_x, size_total_y, my_neighbors, comm, 104 | my_id, xs, ys, xe, ye, xcell, ycell, nproc) 105 | 106 | #Initialize step, time and convergence boolean 107 | step = 0 108 | t = 0.0 109 | converged = false 110 | 111 | #Starting time 112 | if (my_id==0) 113 | time_init = time() 114 | end 115 | 116 | #Main loop : until convergence 117 | while (!converged) 118 | #increment step and time 119 | step += 1 120 | t += dt 121 | #perform one step of the explicit scheme 122 | local_diff = computeNext!(u0, u, size_total_x, size_total_y, dt, hx, hy, 123 | my_id, xs, ys, xe, ye, nproc, k0) 124 | 125 | #update ghost cells 126 | updateBound!(u0, size_total_x, size_total_y, my_neighbors, comm, 127 | my_id, xs, ys, xe, ye, xcell, ycell, nproc) 128 | 129 | MPI.Barrier(comm) 130 | 131 | #sum local_diff to get global difference 132 | global_diff = MPI.Allreduce(local_diff, MPI.SUM, comm) 133 | global_diff = sqrt(global_diff) 134 | #break if convergence reached or step greater than maxStep 135 | if ((global_diff <= epsilon) || (step >= maxStep)) 136 | converged = true 137 | end 138 | end 139 | 140 | #get ending time 141 | if (my_id == 0) 142 | time_final = time() 143 | #elapsed time 144 | elapsed_time = time_final - time_init 145 | println("Elapsed time = ", elapsed_time) 146 | println("Steps = ", step) 147 | end 148 | 149 | #find solution on my sub-domain (as a 1-dimensional array) 150 | i = 1 151 | for j = ys[my_id+1]:ye[my_id+1] 152 | u_local[(i-1)*xcell+1:i*xcell] = u0[xs[my_id+1]:xe[my_id+1],j] 153 | i = i+1 154 | end 155 | 156 | #gather local solution to global solution (as a 1-dimensional array) 157 | u_global = MPI.Gather(u_local, root, comm) 158 | 159 | #write to disk 160 | if (my_id == 0) 161 | write_to_disk(u_global, nx_domains, ny_domains, xcell, ycell, temp1_init, output_path) 162 | end 163 | 164 | MPI.Finalize() 165 | 166 | end 167 | 168 | 169 | main() -------------------------------------------------------------------------------- /distinctions/01_Arrays.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "26492217-61b9-46ac-95ae-72875f8a2677", 6 | "metadata": {}, 7 | "source": [ 8 | "# Arrays\n", 9 | "Multi-dimensional (heap-allocated) arrays are standard library data structures in Julia, with a rich set of tools for manipulation, reshaping, slices, views.\n", 10 | "\n", 11 | "Manual: [Arrays](https://docs.julialang.org/en/v1/manual/arrays/)" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "390fc92c-e1a6-4f84-ba4e-af5b52a37dde", 17 | "metadata": {}, 18 | "source": [ 19 | "## Array Indexing\n", 20 | "Arrays in Julia are __*column-major*__ and indexed starting at 1." 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "id": "b1926057-7497-4e98-8896-db04151286fc", 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "a = [1 2 3 1; 4 5 6 4; 7 8 9 7]\n", 31 | "print(\"a: \")\n", 32 | "display(a)\n", 33 | "@show a[1,1]\n", 34 | "@show a[3,2]\n", 35 | ";" 36 | ] 37 | }, 38 | { 39 | "cell_type": "code", 40 | "execution_count": null, 41 | "id": "5f41f609-d938-4c4b-ad4d-aeaf2a9af21a", 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "print(\"a_flat:\")\n", 46 | "a_flat = reshape(a, length(a))\n", 47 | "display(a_flat)\n", 48 | ";" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "id": "4cf33122-d013-40eb-97cd-169ec22bef99", 55 | "metadata": {}, 56 | "outputs": [], 57 | "source": [ 58 | "a_flat[1] = -1\n", 59 | "display(a_flat)\n", 60 | "display(a)\n", 61 | ";" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "id": "0ace2ce3-2b7d-411f-a94a-456e0995d9c5", 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "# NOTE: This is just to demonstrate the array ordering;\n", 72 | "# There are very few contexts in Julia in which raw pointers are needed\n", 73 | "println(\"@a[1,2]: \", pointer(view(a, 1, 2)))\n", 74 | "println(\"@a_flat[4]: \", pointer(a_flat, 4))\n", 75 | "@show unsafe_load(pointer(a_flat, 4))\n", 76 | ";" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": null, 82 | "id": "c2c64703-3b4a-4353-b8c9-579ecb8d84a8", 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "a_T = transpose(a)\n", 87 | "display(a_T)\n", 88 | "println(\"@a_T[2,1]: \", pointer(view(a_T, 2, 1)))\n", 89 | ";" 90 | ] 91 | }, 92 | { 93 | "cell_type": "markdown", 94 | "id": "f1fcbfcc-1f34-40a1-a21a-252aba4d7b66", 95 | "metadata": {}, 96 | "source": [ 97 | "So, when nesting loops, remember that the first index should be the fastest." 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": null, 103 | "id": "71aeb486-a5ed-4c18-a230-37ff24c46f0a", 104 | "metadata": {}, 105 | "outputs": [], 106 | "source": [ 107 | "for j in 1:3\n", 108 | " for i in 1:3\n", 109 | " println(i, \", \", j, \": \", a[i,j])\n", 110 | " end\n", 111 | "end" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "id": "30ed4ff8-7475-478a-9f12-afc7a663769d", 117 | "metadata": {}, 118 | "source": [ 119 | "## Array Broadcasting\n", 120 | "Unary and binary functions on individual elements can be broadcast to all elements of an array (or two arrays). Nested broadcast operations are fused into one. The `@.` macro can be used to broadcast all operations in an expression without using the `.` everywhere." 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "id": "de0334ec-516a-4496-9d4b-c2f48605011e", 127 | "metadata": {}, 128 | "outputs": [], 129 | "source": [ 130 | "function bc()\n", 131 | " N = 20_000_000\n", 132 | " u = randn(N)\n", 133 | " v = randn(N)\n", 134 | " w = randn(N)\n", 135 | " @time begin\n", 136 | " tmp1 = sin.(u)\n", 137 | " tmp2 = cos.(v)\n", 138 | " tmp3 = tmp1 .* tmp2\n", 139 | " tmp4 = cos.(w)\n", 140 | " tmp5 = sin.(tmp4)\n", 141 | " fin1 = tmp3 .* tmp5\n", 142 | " end\n", 143 | " @time begin\n", 144 | " fin2 = sin.(u) .* cos.(v) .* sin.(cos.(w))\n", 145 | " end\n", 146 | " @time begin\n", 147 | " # This is basically what the above expression is expanded to\n", 148 | " fin3 = broadcast((x,y,z) -> sin(x) * cos(y) * sin(cos(z)), u, v, w)\n", 149 | " end\n", 150 | " @time begin\n", 151 | " # Pre-allocate in order to use .=\n", 152 | " fin4 = similar(u)\n", 153 | " # Equivalent to\n", 154 | " # fin4 .= sin.(u) .* cos.(v) .* sin.(cos.(w))\n", 155 | " @. fin4 = sin(u) * cos(v) * sin(cos(w))\n", 156 | " end\n", 157 | " \n", 158 | " @show (fin1 == fin2 == fin3 == fin4)\n", 159 | "end\n", 160 | "bc()\n", 161 | "bc()\n", 162 | "bc()\n", 163 | ";" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "id": "bde27ed1-eee3-4702-b981-8b07ac5d3c1e", 169 | "metadata": {}, 170 | "source": [ 171 | "This is handy for non-numerical applications as well..." 172 | ] 173 | }, 174 | { 175 | "cell_type": "code", 176 | "execution_count": null, 177 | "id": "51a66046-64c4-452a-94a6-e029ea626e91", 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "names = [\"Brigid\", \"Thecla\", \"Hildegard\"]\n", 182 | "names .= \"Saint \" .* names .* \", pray for us.\"\n", 183 | "display(names)\n", 184 | ";" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": null, 190 | "id": "c49ad62d-c148-4be8-b8cb-96ce7038779b", 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [] 194 | } 195 | ], 196 | "metadata": { 197 | "kernelspec": { 198 | "display_name": "Julia 1.11.6", 199 | "language": "julia", 200 | "name": "julia-1.11" 201 | }, 202 | "language_info": { 203 | "file_extension": ".jl", 204 | "mimetype": "application/julia", 205 | "name": "julia", 206 | "version": "1.11.6" 207 | } 208 | }, 209 | "nbformat": 4, 210 | "nbformat_minor": 5 211 | } 212 | -------------------------------------------------------------------------------- /pluto/01_Variables-and-Types.jl: -------------------------------------------------------------------------------- 1 | ### A Pluto.jl notebook ### 2 | # v0.19.26 3 | 4 | using Markdown 5 | using InteractiveUtils 6 | 7 | # ╔═╡ 2122c0dc-abd8-e6a7-3bdf-f329a7200b3c 8 | md""" 9 | # Variables and Types 10 | 11 | The content material is taken from https://techytok.com/from-zero-to-julia/ 12 | 13 | **Table of Contents** 14 | - [Variables](#Variables) 15 | - [Types](#Types) 16 | - [Conclusion](#Conclusion) 17 | 18 | In this lesson you will learn what are variables and how to perform simple mathematical operations. Furthermore, we will deal with the concept of “types” and their role in Julia as `typing` (in the Python sense) and `type-safety` are parts of the language. 19 | 20 | ## Variables 21 | 22 | Julia is not very different from other language for simple variables, just assign data to a variable. 23 | A few important things: 24 | 25 | 1. Julia doesn't support member functions: **Never** `variable.function() will appear`, but always `function(variable)` just like in C or Fortran. 26 | 2. Variables can be strings, numbers, multidimensional arrays, and higher-level constructs: dictionaries, structs. 27 | 3. Understand default types for your system if you're not assigning types explicitly (see [Types](#Types)). 28 | 29 | In Julia, variables can be global or local scope. **Note: avoid global variables when working in shared code (even with yourself). Why? Name conflicts and types can't be assigned.** 30 | 31 | """ 32 | 33 | # ╔═╡ 71613150-bfad-9bed-99d5-71d28eaab59e 34 | begin 35 | # Use # for comments 36 | # I'm a global variable...don't use global variables unless it's necessary (e.g. library) 37 | globalVariable = "Don't use me" 38 | 39 | function run() 40 | # local variables 41 | name = "William F Godoy" 42 | napples = 10 43 | weight = 167.5 44 | kids = ( "Mark"=> 4, "Emily"=> 0) 45 | years = [ 2017, 2020 ] 46 | 47 | # Unlike Python print doesn't add a newline \n by default. 48 | # Julia uses print and println 49 | # It understands higher-level objects (e.g. dictionary) 50 | println(name) 51 | println(napples) 52 | println(weight) 53 | println(kids) 54 | println(years) 55 | return 56 | end 57 | 58 | # run your local function 59 | run() 60 | end 61 | 62 | # ╔═╡ 7c72b738-be7e-5f56-b3a3-f8af99257bc8 63 | md""" 64 | ## Types 65 | 66 | In Julia every element has a type. The type system is a hierarchical structure: at the top of the tree there is the type `Any`, which means that every element belongs to it, then there are many other sub-types, for example `Number` which includes `Real` and `Complex`, and `Real` contains for example `Int` (integer) numbers and `Float64` numbers. 67 | 68 | Drawing 69 | 70 | Julia enables out-of-the-box type safety as types can be explicitly assigned using the `::` notation, this is preferred for high-performance code as the compiler would actually provide checks and optimizations. In Python, the [typing module](https://docs.python.org/3/library/typing.html) was added in v3.5 for type hints. 71 | 72 | Types can be retrieved using the `typeof` function. It's good practtice to be aware of default types on your system: 73 | """ 74 | 75 | # ╔═╡ e3c865cf-fcea-dd86-a807-e2713de2594a 76 | begin 77 | println("Default types:") 78 | function runWithoutTypes() 79 | name = "William F Godoy" 80 | napples = 10 81 | weight = 167.5 82 | kids = ( "Mark"=> 4, "Emily"=> 0) 83 | years = [ 2017, 2020 ] 84 | 85 | println("name: ", name, " type: ", typeof(name) ) 86 | println("napples: ", napples, " type: ", typeof(napples)) 87 | println("weight: ", weight, " type: ", typeof(weight)) 88 | printstyled(kids, " type: ", typeof(kids), "\n" ; color = :red) 89 | println("years: ", years, " type: ", typeof(years)) 90 | return 91 | end 92 | 93 | println() 94 | runWithoutTypes() 95 | 96 | println("") 97 | println("") 98 | println("Assigned types:") 99 | function runWithTypes() 100 | name::String = "William" 101 | napples::Int32 = 10 102 | weight::Float32 = 167.5 103 | kids::Dict{String,Int32} = Dict( "Mark"=> 4, "Emily"=> 0 ) 104 | years::Array{Int32,1} = [ 2017, 2020 ] 105 | 106 | println("name: ", name, " type: ", typeof(name) ) 107 | println("napples: ", napples, " type: ", typeof(napples)) 108 | println("weight: ", weight, " type: ", typeof(weight)) 109 | printstyled(kids, " type: ", typeof(kids), "\n" ; color = :red) 110 | println("years: ", years, " type: ", typeof(years)) 111 | return 112 | end 113 | 114 | println() 115 | runWithTypes() 116 | end 117 | 118 | # ╔═╡ 9039894d-c27f-6c98-b39b-535e96b55165 119 | md""" 120 | **NOTE: the dictionary type Dict must be typed at least on the right-side of =, otherwise Julia assings a Tuple** 121 | 122 | Although it is possible to change the value of a variable inside a program (it is a variable, after all) it is good programming practice and is also critical for performance that inside a program a variable is “type stable”. This means that if we have assigned `a = 42` it is better not to assign a new value to a which cannot be converted into an Int without losing information, like a `Float64` `a = 0.42` (if we convert a `Float64` to an `Int`, the decimal part gets truncated). 123 | 124 | If we know that a variable (such as a) will have to contain values of type `Float64` it is better to initialise it with a value that is already of that type. 125 | """ 126 | 127 | # ╔═╡ 98c5294b-e703-3caf-c05b-58bd5e1e5f05 128 | begin 129 | function run() 130 | a = 2 # if we need to operate with ints 131 | println("Type of a: ", typeof(a)) 132 | b = 2.0 # if we need to operate with floats 133 | println("Type of a: ", typeof(b)) 134 | 135 | # a = 2.0 # not a good practice, type is Float64 by default 136 | # a = convert(Float32, a) # only is required, we aware of truncation 137 | println("Type of a: ", typeof(a)) 138 | 139 | return 140 | end 141 | 142 | run() 143 | end 144 | 145 | # ╔═╡ 05085c9f-c50f-b8d7-9196-f5159aa29983 146 | md""" 147 | # Conclusion 148 | We have learned what variables are, how to perform basic operations and we have dealt about types. 149 | """ 150 | 151 | # ╔═╡ bf90751a-a161-3f45-8d37-3e92281eb3d3 152 | md""" 153 | Questions: 154 | 155 | 1. Does Julia support unsigned integers? 156 | 2. Does Julia support member functions? 157 | 3. What's the abstraction for any type? 158 | """ 159 | 160 | # ╔═╡ Cell order: 161 | # ╠═2122c0dc-abd8-e6a7-3bdf-f329a7200b3c 162 | # ╠═71613150-bfad-9bed-99d5-71d28eaab59e 163 | # ╠═7c72b738-be7e-5f56-b3a3-f8af99257bc8 164 | # ╠═e3c865cf-fcea-dd86-a807-e2713de2594a 165 | # ╠═9039894d-c27f-6c98-b39b-535e96b55165 166 | # ╠═98c5294b-e703-3caf-c05b-58bd5e1e5f05 167 | # ╠═05085c9f-c50f-b8d7-9196-f5159aa29983 168 | # ╠═bf90751a-a161-3f45-8d37-3e92281eb3d3 169 | -------------------------------------------------------------------------------- /notebooks/08_Plotting.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Plotting in Julia\n", 8 | "\n", 9 | "In this lesson we will learn how to make beautiful plots using the [Plots.jl package](https://github.com/JuliaPlots/Plots.jl)\n", 10 | "\n", 11 | "n Julia there are many different libraries for plotting, for example `PyPlot.jl`, `GR.jl` and `Plotly.jl`. `Plots.jl` is a wrapper around all those library and exposes a clean and simple API for plotting. You can find all the back-ends available for Plots.jl [here](https://docs.juliaplots.org/latest/backends/).\n", 12 | "\n", 13 | "## Contents\n", 14 | "- [Using Plots.jl](#Using-Plots.jl)\n", 15 | "- [Working with different backends](#Working-with-different-backends)\n", 16 | "\n", 17 | "\n", 18 | "## Using Plots.jl\n", 19 | "\n", 20 | "Let’s make our first plot! We need to compute `x` and `y = f(x)`, for example let’s plot the sine function using the `gr` backend:\n" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": null, 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "using Plots\n", 30 | "gr()\n", 31 | "\n", 32 | "function plotsFun()\n", 33 | " x = 1:0.01:10*π\n", 34 | " y = sin.(x)\n", 35 | " plot(x, y, label=\"sin(x)\")\n", 36 | " plot!(xlab=\"x\", ylab=\"f(x)\")\n", 37 | "end\n", 38 | "\n", 39 | "plotsFun()" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "We add some elements to the plot, using `plot!`. Remember that in Julia the `!` is appended to the name of functions which perform some modification: in this case `plot!` modifies the current plot. In particular, we add a label to the x-axis and the y-axis.\n", 47 | "\n", 48 | "Let’s add another curve to the plot:" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": {}, 55 | "outputs": [], 56 | "source": [ 57 | "function plotsFun()\n", 58 | " x = 1:0.01:10*π\n", 59 | " y = sin.(x)\n", 60 | " plot(x, y, label=\"sin(x)\")\n", 61 | " plot!(xlab=\"x\", ylab=\"f(x)\")\n", 62 | " \n", 63 | " y2=sin.(x).^2\n", 64 | " plot!(x, y2, label=\"sin(x)^2\", color=:red, line=:dash)\n", 65 | "\n", 66 | "end\n", 67 | "\n", 68 | "plotsFun()" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "you can see that we have specified the colour of the line and the linestyle (which is dashed in this case). You can find more information on the possible parameters at the [official documentation](http://docs.juliaplots.org/latest/attributes/).\n", 76 | "\n", 77 | "Now we can set the scale of the x-axis to be logarithmic and change the position of the legend. We can see how it is possible to save the current plot as a .png file if we like:" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "function plotsFun()\n", 87 | " x = 1:0.01:10*π\n", 88 | " y = sin.(x)\n", 89 | " plot(x, y, label=\"sin(x)\")\n", 90 | " plot!(xlab=\"x\", ylab=\"f(x)\")\n", 91 | " \n", 92 | " y2=sin.(x).^2\n", 93 | " plot!(x, y2, label=\"sin(x)^2\", color=:red, line=:dash)\n", 94 | " xaxis!(:log10)\n", 95 | " plot!(legend=:bottomleft)\n", 96 | "\n", 97 | " #savefig(\"img1c.png\")\n", 98 | "end\n", 99 | "\n", 100 | "plotsFun()" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "## Working with different backends\n", 108 | "\n", 109 | "Different backends have different features. Up to now we have worked with GR, which is fast and has almost everything you may need. Since GR is a relatively new backend, you may need to look at other backends for more customisation options. In this section, we will deal with Plotly and PyPlot.\n", 110 | "\n", 111 | "### Plotly\n", 112 | "\n", 113 | "[Plotly](https://plotly.com/julia/) is a good solution if you want to have nice interactive plots.\n", 114 | "\n", 115 | "To make a plot with Plotly, select the `plotly()` back-end and create a plot:" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": null, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "# set new backend\n", 125 | "plotly()\n", 126 | "\n", 127 | "function PlotlyFun()\n", 128 | " x=1:0.1:3*π\n", 129 | " y=1:0.1:3*π\n", 130 | "\n", 131 | " xx = reshape([xi for xi in x for yj in y], length(y), length(x))\n", 132 | " yy = reshape([yj for xi in x for yj in y], length(y), length(x))\n", 133 | " zz = sin.(xx).*cos.(yy)\n", 134 | " \n", 135 | " plot3d(xx,yy,zz, label=:none, st = :surface)\n", 136 | " plot!(xlab=\"x\", ylab=\"y\", zlab=\"sin(x)*cos(y)\")\n", 137 | " #savefig(\"img2\") \n", 138 | "end\n", 139 | "\n", 140 | "PlotlyFun()" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "Interact with [html image](../images/img2.html).\n", 148 | "\n", 149 | "### PyPlot\n", 150 | "\n", 151 | "PyPlot is a Python library for plotting, it's a wrapper to the module in Matplotlib. It has many customisation capabilities with the downside that you need to first install python and configure Julia to interact with Python. In order to configure and install PyCall, the package required to interact with Python, please refer to [this guide](https://techytok.com/lesson-interacting-with-python/).\n", 152 | "\n", 153 | "This is just an example, I didn't configure this IJulia kernel to work with Python.\n", 154 | "With the PyPlot back-end it is possible to use LaTeX in the labels (and axis labels) adding the LaTeXStrings package:\n", 155 | "\n", 156 | "```\n", 157 | "using Pkg\n", 158 | "Pkg.add(\"PyPlot\")\n", 159 | "Pkg.add(\"LaTeXStrings\")\n", 160 | "\n", 161 | "\n", 162 | "using LaTeXStrings\n", 163 | "pyplot()\n", 164 | "\n", 165 | "function PyPlotFun()\n", 166 | " x=0:0.1:2*π\n", 167 | " y=sin.(x).^2\n", 168 | "\n", 169 | " # You can use LaTeX strings\n", 170 | " plot(x, y, label=L\"$\\sin(x)^2$\")\n", 171 | "end\n", 172 | "\n", 173 | "PyPlotFun()\n", 174 | "```\n", 175 | "\n", 176 | "## Conclusions\n", 177 | "We have learned how to make some nice plots using three different backends, each one has its pros and cons. \n", 178 | "The Plots package serves as a unified interface.\n", 179 | "To summarise:\n", 180 | "\n", 181 | "- Use GR for fast “default” plotting.\n", 182 | "\n", 183 | "- Use Plotly for interactive plotting.\n", 184 | "\n", 185 | "- Use PyPlot if you need some of its customisation options.\n", 186 | "\n", 187 | "For a more comprehensive guide refer to the official documentation:\n", 188 | "https://docs.juliaplots.org/latest/tutorial/" 189 | ] 190 | } 191 | ], 192 | "metadata": { 193 | "kernelspec": { 194 | "display_name": "Julia 1.11.6", 195 | "language": "julia", 196 | "name": "julia-1.11" 197 | }, 198 | "language_info": { 199 | "file_extension": ".jl", 200 | "mimetype": "application/julia", 201 | "name": "julia", 202 | "version": "1.11.6" 203 | } 204 | }, 205 | "nbformat": 4, 206 | "nbformat_minor": 4 207 | } 208 | -------------------------------------------------------------------------------- /notebooks/06_Modules.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "c3a4e087", 6 | "metadata": {}, 7 | "source": [ 8 | "# Modules\n", 9 | "\n", 10 | "In this lesson we will learn what modules are and how they can be used for code reusability.\n", 11 | "\n", 12 | "## Contents\n", 13 | "\n", 14 | "- [Working with Modules](#Working-with-Modules)\n", 15 | "- [Conflicts](#Conflicts)\n", 16 | "- [User defined Modules](#User-defined-Modules)\n", 17 | "\n", 18 | "\n", 19 | "## Working with Modules\n", 20 | "\n", 21 | "Libraries in Julia come in the form of module which can be loaded via the using notation. A module is a separate environment with its sets of variables and functions, some of which are exported in the calling scope, which means that you can call exported functions by simply typing their name as if they where defined in the same scope, while others are accessible only through the `ModuleName.functionName` notation.\n", 22 | "\n", 23 | "In order to use an existing official module, we need first to install it and then import it, you can do it using the Julia `Pkg` unified package manager.\n", 24 | "\n", 25 | "```\n", 26 | "using Pkg\n", 27 | "Pkg.add(\"ModuleName\")\n", 28 | "```\n", 29 | "\n", 30 | "Now we are able to access its avaiable functions. There is 2 ways to do this:\n", 31 | "- `using Module` # usage: foo(x)\n", 32 | "- `import Module` # usage: Module.foo(x)\n", 33 | "\n", 34 | "`using` makes all the functions and variables from that module accesible. In general, this is very common but it's discourage in large projects using several modules due to name conflict. `import` requires the use of the module name as a namespace, while this is more verbose. For simple and quick tasks use `using`, for more complex workflows use `import`.\n", 35 | "\n", 36 | "For this example we will use the [SpecialFunctions.jl](https://github.com/JuliaMath/SpecialFunctions.jl) package, which contains functions such as gamma and Bessel functions.\n", 37 | "\n", 38 | "```\n", 39 | "using Pkg\n", 40 | "Pkg.add(\"SpecialFunctions\") // only needed once!\n", 41 | "```" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": null, 47 | "id": "e965e582", 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "import SpecialFunctions as SF\n", 52 | "import SpecialFunctions: cosint\n", 53 | "\n", 54 | "function SpecialFunctionsFun()\n", 55 | " println( \"Gamma: \", SF.gamma(3) )\n", 56 | " println( \"Bessel: \", SpecialFunctions.besselj0(3) )\n", 57 | " println( \"Sine integral: \", SpecialFunctions.sinint(3) )\n", 58 | " println( \"Cosine integral: \", cosint(3) )\n", 59 | " return\n", 60 | "end\n", 61 | "\n", 62 | "SpecialFunctionsFun()" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "id": "8162560e", 68 | "metadata": {}, 69 | "source": [ 70 | "## Conflicts\n", 71 | "\n", 72 | "Sometimes it is useful to call a function taking in consideration the package where it is defined. If a function is not exported by the module (more on `export` later) or if there are several modules which export a function with the same name and same argument signature, we can specify which module the function belongs to using the following syntax:" 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "id": "79964ec5", 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "function gamma(x)\n", 83 | " println(\"I am another 'gamma' function\")\n", 84 | " return x^2\n", 85 | "end\n", 86 | "\n", 87 | "println(\"My gamma(3): \", gamma(3))\n", 88 | "println(\"SpecialFunctions.gamma(3): \", SpecialFunctions.gamma(3))" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "id": "6ad6c6c5", 94 | "metadata": {}, 95 | "source": [ 96 | "As you can see, it is better to avoid the `using` notations due to conflict as it puts the function name in the global Main area. Instead use `import`, which “imports” the desired module all the same but without exporting any function in the calling scope." 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "id": "408d7c30", 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "using SpecialFunctions\n", 107 | "\n", 108 | "function SpecialFunctionsFun()\n", 109 | " println( \"Gamma: \", gamma(3) )\n", 110 | " println( \"SpecialFunctions.gamma(3): \", SpecialFunctions.gamma(3))\n", 111 | " println( \"Bessel: \", besselj0(3) )\n", 112 | " println( \"Sine integral: \", sinint(3) )\n", 113 | " println( \"Cosine integral: \", cosint(3) )\n", 114 | " return\n", 115 | "end\n", 116 | "\n", 117 | "SpecialFunctionsFun()" 118 | ] 119 | }, 120 | { 121 | "cell_type": "markdown", 122 | "id": "0e4fcd83", 123 | "metadata": {}, 124 | "source": [ 125 | "## User defined modules\n", 126 | "\n", 127 | "It is possible to think of modules as compact blocks of variables and functions which can be easily imported in another program. One should not think of a module as something similar to what is a class in object oriented languages such as C++ and Python, but instead as a separate global scope with its own set of variables and functions which can be called from another program. One difference with a class is that it is not possible to import a module several times to have different sets of “global variables”, while it is usual in object oriented languages to have different instances of the same class.\n", 128 | "\n", 129 | "In Julia the functions inside a module should thus depend only on their input (and eventually some global variables or other functions defined inside the module which will be shared).\n", 130 | "\n", 131 | "In the following example we will define a simple module which performs some operations and exports a function." 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": null, 137 | "id": "4f26b954", 138 | "metadata": {}, 139 | "outputs": [], 140 | "source": [ 141 | "# define MyModule\n", 142 | "module MyModule # start a module\n", 143 | "export func2 # external visibility to import and using\n", 144 | "\n", 145 | "a=42\n", 146 | "function func1(x)\n", 147 | " return x^2\n", 148 | "end\n", 149 | "\n", 150 | "function func2(x)\n", 151 | " return func1(x) + a\n", 152 | "end\n", 153 | "\n", 154 | "end #end of module\n", 155 | "\n", 156 | "# consume MyModule (put it in the path automatically with .)\n", 157 | "using .MyModule\n", 158 | "\n", 159 | "println( \"func2: \", func2(3) ) # exposed\n", 160 | "\n", 161 | "println( \"MyModule.func1: \", MyModule.func1(3) ) # exposed\n", 162 | "\n", 163 | "println( \"func1: \", func1(3) ) # not exposed" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "id": "8741eda1-f87a-441f-a988-6ada7c469789", 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [] 173 | } 174 | ], 175 | "metadata": { 176 | "kernelspec": { 177 | "display_name": "Julia 1.11.6", 178 | "language": "julia", 179 | "name": "julia-1.11" 180 | }, 181 | "language_info": { 182 | "file_extension": ".jl", 183 | "mimetype": "application/julia", 184 | "name": "julia", 185 | "version": "1.11.6" 186 | } 187 | }, 188 | "nbformat": 4, 189 | "nbformat_minor": 5 190 | } 191 | -------------------------------------------------------------------------------- /distinctions/02_Type_System.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "8c3403ba-258e-43b1-9293-0d7b1f7cb2e2", 6 | "metadata": {}, 7 | "source": [ 8 | "# Type System\n", 9 | "\n", 10 | "Manual: [Types](https://docs.julialang.org/en/v1/manual/types/)\n", 11 | "\n", 12 | "In Julia every element has a type. The type system is a hierarchical structure: at the top of the tree there is the type `Any`, which means that every element belongs to it, then there are many other sub-types, for example `Number` which includes `Real` and `Complex`, and `Real` contains for example `Int` (integer) numbers and `Float64` numbers.\n", 13 | "\n", 14 | "\"Subtypes\n", 15 | "\n", 16 | "Julia enables out-of-the-box type safety as types can be explicitly assigned using the :: notation, this is preferred for high-performance code as the compiler can provide more checks and optimizations with more information." 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "id": "d786ced5-50ce-497d-aa99-17e135774763", 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "let\n", 27 | " \n", 28 | "a = 2\n", 29 | "println(a, \", typeof(a): \", typeof(a))\n", 30 | "b = 2.0\n", 31 | "println(b, \", typeof(b): \", typeof(b))\n", 32 | " \n", 33 | "# a = 2.0 # not a good practice, type is Float64 by default\n", 34 | "a = convert(Float32, a) # only if required, we are aware of truncation\n", 35 | "println(a, \", typeof(a): \", typeof(a))\n", 36 | "\n", 37 | "# if you specify type\n", 38 | "d::UInt = 3\n", 39 | "d = convert(Float32, d)\n", 40 | "println(d, \", typeof(d): \", typeof(d))\n", 41 | " \n", 42 | "end\n", 43 | ";" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "id": "01908df0-0713-4139-bd4e-e462704f3e74", 49 | "metadata": {}, 50 | "source": [ 51 | "Notice how types can be used as values. Types are \"first-class\" citizens in Julia.\n", 52 | "\n", 53 | "### Avoid changing the type of a variable\n", 54 | "It is good programming practice and is also critical for performance that inside a program a variable is “type stable”. This means that if we have assigned `a = 42` it is better not to assign a new value which cannot be converted to `Int` without losing information, like a `Float64` `a = 0.42` (if we convert a `Float64` to an `Int`, the decimal part gets truncated).\n", 55 | "\n", 56 | "If we know that a variable (such as a) will have to contain values of type `Float64` it is better to initialise it with a value that is already of that type." 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "id": "a74ebfcb-bf45-4b3a-a975-d69b50069659", 62 | "metadata": {}, 63 | "source": [ 64 | "### Other types of types\n", 65 | "I view abstract types more like type constraints (or concepts).\n", 66 | "(Starts to make more sense when used as function parameters or struct members.)\n", 67 | "\n", 68 | "Other than types in the normal hierarchy, there are also union types\n", 69 | "\n", 70 | "- Parametric types used without parameters (UnionAll)\n", 71 | "- Unions\n", 72 | "\n", 73 | "This is especially useful for constraining generic function parameters" 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "id": "8867911c-3181-4776-8128-792d1100b56f", 80 | "metadata": {}, 81 | "outputs": [], 82 | "source": [ 83 | "let\n", 84 | "\n", 85 | "@show typeof(Float64)\n", 86 | "@show typeof(AbstractFloat)\n", 87 | " \n", 88 | "struct Point{T} # example of a parametric type (T is the parameter)\n", 89 | " x::T\n", 90 | " y::T\n", 91 | "end\n", 92 | "@show typeof(Point{Float64})\n", 93 | "@show typeof(Point)\n", 94 | "\n", 95 | "a::Union{Int, String} = 1\n", 96 | "@show typeof(Union{Int, String})\n", 97 | "println(typeof(a))\n", 98 | "a = \"hello\"\n", 99 | "println(typeof(a))\n", 100 | " \n", 101 | "end\n", 102 | ";" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "id": "1071741c-2cab-4982-98fc-b22544aec8b1", 108 | "metadata": {}, 109 | "source": [ 110 | "Here the name `Point` without a parameter is a `UnionAll` type (a special kind of abstract type) because the parameter is required to make a concrete type." 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": null, 116 | "id": "220a3c33-7e57-4cd1-93a2-cc5613f493f0", 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "@show typeof(Vector)\n", 121 | "@show typeof(Vector{Float64})\n", 122 | "@show supertypes(Vector)\n", 123 | "@show supertypes(Vector{Float64})\n", 124 | ";" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "id": "cab96e88-5d14-453d-894c-b3ff66ffaf20", 130 | "metadata": {}, 131 | "source": [ 132 | "You can see two \"dimensions\" of abstraction here, the hierarchy and the\n", 133 | "parameter. Remember abstract types represent \"groups\" of types that are\n", 134 | "intuitively similar. This is useful when defining structures or functions that\n", 135 | "need to be generic. When you need your function to apply to a group of possible\n", 136 | "input types, you simply think in terms of how tight the constraint needs to be." 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "id": "14f17080-cb43-4bdb-91ce-2b9f707519cb", 142 | "metadata": {}, 143 | "source": [ 144 | "## Type Hierarchy Examples" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "id": "29f4a9b2-ab6f-4482-bea1-dc389ac87d18", 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "@show subtypes(Real)\n", 155 | "@show supertypes(DataType)\n", 156 | "\n", 157 | "using AbstractTrees\n", 158 | "AbstractTrees.children(x::Type) = subtypes(x)\n", 159 | "println()\n", 160 | "print_tree(Number)\n", 161 | "println()\n", 162 | "print_tree(AbstractVector)\n", 163 | "println()\n", 164 | "print_tree(Type)" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "id": "a02da603-3284-4af6-bcd2-38d6b1b2d930", 170 | "metadata": {}, 171 | "source": [ 172 | "## Defining Type Hierarchies\n", 173 | "A Julia `struct` is immutable by default" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "id": "53cf4194-4995-4882-b0b7-0ded007f1c21", 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "abstract type Person\n", 184 | "end\n", 185 | "\n", 186 | "abstract type Musician <: Person\n", 187 | "end\n", 188 | "\n", 189 | "struct ClassicalMusician <: Musician\n", 190 | " name::String\n", 191 | " instrument::String\n", 192 | "end\n", 193 | "\n", 194 | "mutable struct RockStar <: Musician\n", 195 | " name::String\n", 196 | " instrument::String\n", 197 | " bandName::String\n", 198 | "end" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "id": "4d8b29cb-e372-4e69-8f2c-711620070156", 205 | "metadata": {}, 206 | "outputs": [], 207 | "source": [ 208 | "aurelio = ClassicalMusician(\"Aurelio\", \"Violin\")\n", 209 | "print(aurelio)\n", 210 | "aurelio.instrument = \"Guitar\"" 211 | ] 212 | }, 213 | { 214 | "cell_type": "code", 215 | "execution_count": null, 216 | "id": "d64b883a-c568-4ace-928a-b2389465721a", 217 | "metadata": {}, 218 | "outputs": [], 219 | "source": [ 220 | "phil = RockStar(\"Phil Collins\", \"Drums\", \"Genesis\")\n", 221 | "println(phil)\n", 222 | "phil.instrument = \"Vocals\"\n", 223 | "println(phil)" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "id": "5f9c59fe-2c9a-4a8e-9715-a4c173f741a8", 230 | "metadata": {}, 231 | "outputs": [], 232 | "source": [] 233 | } 234 | ], 235 | "metadata": { 236 | "kernelspec": { 237 | "display_name": "Julia 1.11.6", 238 | "language": "julia", 239 | "name": "julia-1.11" 240 | }, 241 | "language_info": { 242 | "file_extension": ".jl", 243 | "mimetype": "application/julia", 244 | "name": "julia", 245 | "version": "1.11.6" 246 | } 247 | }, 248 | "nbformat": 4, 249 | "nbformat_minor": 5 250 | } 251 | -------------------------------------------------------------------------------- /notebooks/05_Array-Broadcasting.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Array Broadcasting\n", 8 | "\n", 9 | "## Contents\n", 10 | "\n", 11 | "- [Introduction](#Introduction)\n", 12 | "- [Operations with Arrays](#Operations-with-Arrays)\n", 13 | "- [Broadcasting](#Broadcasting)\n", 14 | "- [Conclusion](#Conclusion)\n", 15 | "\n", 16 | "## Introduction \n", 17 | "\n", 18 | "Many languages (such as Python with numpy and Matlab) make extensive use of optimised C or Fortran routines under the hood to perform fast mathematical operations, as such the user is encouraged to write vectorised code, so that these routines can perform faster for loops, as a big part of the CPU time is spent on calling the underlying compiled routine. More or less what happens is that the user writes vectorized code which communicates to C code that can run fast for loops and the result is then returned to the user in the form of an array (or matrix).\n", 19 | "\n", 20 | "In Julia, the interpreter will directly compile to optimised machine code using just-in-time (JIT) compilation.\n", 21 | "\n", 22 | "**NOTE of CAUTION:** measure first! JIT can be a bottleneck on its own (first-time-to-plot is a real issue)\n", 23 | "\n", 24 | "In Julia, the broadcasting notation is given by the dot `.` operator.\n", 25 | "\n", 26 | "## Operations with Arrays\n", 27 | "\n", 28 | "Julia by default deals with operations on arrays and matrices as one would do in mathematics.\n", 29 | "\n", 30 | "From a mathematical point of view, we don’t know how to compute the `sin` of an array, as the sine function is defined only on single (dimensionless) values. At the same time the `exp` can work both on single values and matrices as the exponential of a matrix has a well-defined geometrical meaning. \n", 31 | "\n", 32 | "For the same reason, you cannot multiply two arrays together, unless their size is matching correctly (i.e. one array is a row array and the other one is a column array) and in this case the multiplication of two arrays becomes the well-defined geometrical product of two arrays (which can be a scalar or a matrix, depending on the order of the multiplication).\n", 33 | "\n", 34 | "Let's start with a simple example:" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "function ErrorColCol()\n", 44 | " col1 = [1,2,3] # is a column vector\n", 45 | " col2 = [4,5,6] # is a column vector\n", 46 | " col1*col2\n", 47 | " return\n", 48 | "end\n", 49 | "\n", 50 | "ErrorColCol()" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": null, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "function MultFun()\n", 60 | " println(\"Valid array operations:\")\n", 61 | " col1 = [1,2,3] # is a column vector\n", 62 | " row1 = [4 5 6] # is a row vector\n", 63 | " \n", 64 | " println( \" Matrix col1 * row1 = \", col1 * row1 )\n", 65 | " println( \" Scalar row1 * col1 = \", row1 * col1 )\n", 66 | " \n", 67 | " # 1D vector to 3x3 2D matrix\n", 68 | " mat = reshape([1,2,3,4,5,6,7,8,9],3,3)\n", 69 | " println( \" Column mat * col1 = \", mat * col1 )\n", 70 | " println( \" Row row1 * mat = \", row1 * mat )\n", 71 | " return\n", 72 | "end\n", 73 | "\n", 74 | "MultFun()" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "The previous array operations make perfect sense from a mathematical point of view and operators behave how we would expect. Nonetheless, in programming it is often useful to write operations which work on an element by element basis, and for this reason broadcasting comes to our help.\n", 82 | "\n", 83 | "## Broadcasting\n", 84 | "\n", 85 | "In Julia, with broadcasting we indicate the action of mapping a function or an operation (which are the same in Julia) over an array or a matrix \"element by element\".\n", 86 | "\n", 87 | "The broadcasting notation for operators consists of adding a dot `.` before the operator, for example `.*` .\n", 88 | "\n", 89 | "Considering the example we get:\n" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "metadata": {}, 96 | "outputs": [], 97 | "source": [ 98 | "function BroadcastFun()\n", 99 | " println(\"Valid array broadcast operations:\")\n", 100 | " col1 = [1,2,3] # is a column vector\n", 101 | " row1 = [4 5 6] # is a row vector\n", 102 | " \n", 103 | " println( \" Matrix col1 .* row1 = \", col1 .* row1 )\n", 104 | " println( \" Scalar row1 .* col1 = \", row1 .* col1 )\n", 105 | " \n", 106 | " # 1D vector to 3x3 2D matrix\n", 107 | " mat = reshape([1,2,3,4,5,6,7,8,9],3,3)\n", 108 | " println( \" Column mat .* col1 = \", mat .* col1 )\n", 109 | " println( \" Row row1 .* mat = \", row1 .* mat )\n", 110 | " \n", 111 | " println( \" Squares col1 .* col1 = \", col1 .* col1)\n", 112 | " return\n", 113 | "end\n", 114 | "\n", 115 | "BroadcastFun()" 116 | ] 117 | }, 118 | { 119 | "cell_type": "markdown", 120 | "metadata": {}, 121 | "source": [ 122 | "Notice that when we broadcast the multiplication with a matrix and an array, the array gets multiplied “in the same direction” as it is written, in the sense that if a vector is a column it gets applied column by column etc.\n", 123 | "\n", 124 | "We can use the broadcasting notation also to map a function over an n-dimensional array. There is **no speed gain** in doing so, as it will be exactly equivalent to writing a for loop, but its conciseness may be useful sometimes. So the core idea in Julia is to write functions that take single values and use broadcasting when needed, unless the functions must explicitly work on arrays (for example to compute the mean of a series of values, perform matrix operations, vector multiplications, append to strings, etc)." 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "metadata": {}, 131 | "outputs": [], 132 | "source": [ 133 | "function MoreBroadcastFun()\n", 134 | " # strings\n", 135 | " first_names = [\"Mark\", \"Emily\"]\n", 136 | " # concatenate element by element\n", 137 | " full_names = first_names .* \" Godoy\"\n", 138 | " println(full_names) \n", 139 | " \n", 140 | " \n", 141 | " v = [1,2,3,4,5,6,7,8,9]\n", 142 | " \n", 143 | " # norm\n", 144 | " println( \"L2 Norm = \", sum( v .^ 2) ^ (1/2) ) \n", 145 | " # sines \n", 146 | " println( \"Sine of v elements = \", sin.(v) )\n", 147 | " \n", 148 | " return\n", 149 | "end\n", 150 | "\n", 151 | "MoreBroadcastFun()\n" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "metadata": {}, 157 | "source": [ 158 | "**Tip** \n", 159 | "`Do not try to write vectorised code in Julia (like you would do in Python and Matlab) if you don’t need to, even if you are used to coding this way, as the code will become less readable and more prone to errors. Use instead broadcasting and for loops, when needed, to map a function over several values.`\n", 160 | "\n", 161 | "\n", 162 | "## Conclusion\n", 163 | "\n", 164 | "We have learned what is broadcasting and how it can be used to perform element by element operations between vectors and how to map a function over an array using the concise broadcasting syntax.\n", 165 | "\n", 166 | "We have also pointed out how Julia does not gain in performance by using “vectorised” notations (while other languages do) because the core Julia operations are implemented directly in the Julia language and thus there is no need to call compiled routines coded in other languages under the hood to offload all the heavy work.\n", 167 | "\n", 168 | "## Questions\n", 169 | "\n", 170 | "1. Does broadcasting make your code \"faster\"?\n", 171 | "2. Can you do array \"norms\" with broadcasting?" 172 | ] 173 | } 174 | ], 175 | "metadata": { 176 | "kernelspec": { 177 | "display_name": "Julia 1.11.6", 178 | "language": "julia", 179 | "name": "julia-1.11" 180 | }, 181 | "language_info": { 182 | "file_extension": ".jl", 183 | "mimetype": "application/julia", 184 | "name": "julia", 185 | "version": "1.11.6" 186 | } 187 | }, 188 | "nbformat": 4, 189 | "nbformat_minor": 4 190 | } 191 | -------------------------------------------------------------------------------- /distinctions/03_Functions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "3677201b-30cc-419e-be16-fe38fcebb6f9", 6 | "metadata": {}, 7 | "source": [ 8 | "# Functions and Methods and Multiple Dispatch\n", 9 | "Manual: [Functions](https://docs.julialang.org/en/v1/manual/functions/), [Methods](https://docs.julialang.org/en/v1/manual/methods)\n", 10 | "\n", 11 | "Functions are the primary building blocks in Julia. Every operation is done through functions (even math operations, like `+`)." 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": null, 17 | "id": "884b1a3d-eb8e-4e0a-adef-d4073d4f51dd", 18 | "metadata": {}, 19 | "outputs": [], 20 | "source": [ 21 | "@edit 2+2 # (output is ugly in a notebook)" 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "id": "24f655dc-0826-4d3a-bfe3-37d049ad8e38", 27 | "metadata": {}, 28 | "source": [ 29 | "Functions return the result of the last evaluation, and this enables the short-hand notation" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "id": "57d239f9-79d5-42aa-be3b-483906c8ec59", 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "# full form\n", 40 | "function f(x)\n", 41 | " @show typeof(x)\n", 42 | " return x^2 # the keyword `return` can be omitted, but it's generally good to include for readability\n", 43 | "end\n", 44 | "\n", 45 | "# short form\n", 46 | "g(x) = 2*x\n", 47 | "\n", 48 | "@show g(2)\n", 49 | "\n", 50 | "@show typeof(f(2))\n", 51 | "@show typeof(f(2.0))\n", 52 | ";" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "id": "0919ac55-f0f8-464b-b29b-2e561013497b", 58 | "metadata": {}, 59 | "source": [ 60 | "The parameter `x` above being untyped is unconstrained and takes `Any`. We get a different version from the compiled for each type passed in." 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "id": "b527267f-cfb2-4c8e-aea8-0e47e0b46302", 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "@code_llvm g(2)\n", 71 | "@code_llvm g(2.0)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": null, 77 | "id": "743f2455-ee8a-4baa-b659-efcc6b05b174", 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "@code_native g(2)" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "id": "7b1f49ad-f58d-41be-884a-bebd6ebcbdbc", 87 | "metadata": {}, 88 | "source": [ 89 | "We can constrain the type with an abstract type farther down the tree or with a concrete type and Julia will match with the closest match." 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": null, 95 | "id": "f9ddeebc-6035-4d44-952a-3a2227321b9a", 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "g(x::Integer) = x + x\n", 100 | "g(x::AbstractFloat) = x * x\n", 101 | "g(x::Float32) = x\n", 102 | "@show g(3)\n", 103 | "@show g(3.0)\n", 104 | "@show g(3.0f0)\n", 105 | "\n", 106 | "h(x::Float32) = x\n", 107 | "@show h(2)\n", 108 | ";" 109 | ] 110 | }, 111 | { 112 | "cell_type": "markdown", 113 | "id": "bc1bdd27-4a84-430c-afb0-9a3fb966b123", 114 | "metadata": {}, 115 | "source": [ 116 | "This allows for a lot of flexibility in defining interfaces." 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "id": "24de265b-50c8-482e-b61b-153d74d3f48c", 122 | "metadata": {}, 123 | "source": [ 124 | "### Optional and Keyword Arguments" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": null, 130 | "id": "adda09d7-38d4-4e4c-bb3d-85a834a8d69e", 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "# notice the semicolon\n", 135 | "function keyword_optional(a, b, c=0; d=0, e) # put the optional one at the end in real life :)\n", 136 | " @show a, b, c, d, e\n", 137 | "end\n", 138 | "\n", 139 | "keyword_optional(1, 2, 3; d = 4, e = 5)\n", 140 | "\n", 141 | "# keyword_optional(1, 2) # error: `e` not assigned\n", 142 | "\n", 143 | "keyword_optional(1, 2, e=5) # semicolon at call is not required\n", 144 | "\n", 145 | "# keyword_optional(1, 2, 3, 4, 5) # error because you didn't name `d` or `e`" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "id": "9f01e1c0-8668-485c-b988-9d295451e885", 151 | "metadata": {}, 152 | "source": [ 153 | "### `Nothing` Functions\n", 154 | "Similar to `void` (but with more usefulness), you can `return nothing` if you don't want a value returned from a function." 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "id": "27c03045-be15-4106-97a4-8e238f0c0ece", 161 | "metadata": {}, 162 | "outputs": [], 163 | "source": [ 164 | "function void1()\n", 165 | " return nothing\n", 166 | "end\n", 167 | "@show void1()\n", 168 | "@show typeof(void1())\n", 169 | "\n", 170 | "void2() = nothing\n", 171 | "@show void2()\n", 172 | "@show typeof(void2())\n", 173 | "\n", 174 | "function void3()\n", 175 | " return\n", 176 | "end\n", 177 | "@show void3()\n", 178 | "@show typeof(void3())\n", 179 | ";" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "id": "422c3530-9678-4531-ae12-14ad42081164", 185 | "metadata": {}, 186 | "source": [ 187 | "### Anonymous Functions\n", 188 | "Similar to lambdas in Python or C++:" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": null, 194 | "id": "08b90969-06d2-4c03-87dd-71cec2156d74", 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "# assigned to a variable\n", 199 | "plusThree = (x, y) -> x + y + 3\n", 200 | "@show plusThree(1,1)\n", 201 | "sayHello = () -> println(\"Hello!\")\n", 202 | "@show sayHello()\n", 203 | "\n", 204 | "# used in a function\n", 205 | "a = [1, 2, 3]\n", 206 | "b = map(x -> x^2, a) # for single argument, you can omit the ()\n", 207 | "@show b\n", 208 | ";" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "id": "a94f2f31-3ec1-44c3-b9a0-fb2a1ac1add6", 214 | "metadata": {}, 215 | "source": [ 216 | "### Constructors and Functors\n", 217 | "\n", 218 | "Manual: [Constructors](https://docs.julialang.org/en/v1/manual/constructors/),\n", 219 | "[Functors](https://docs.julialang.org/en/v1/manual/methods/#Function-like-objects)\n", 220 | "\n", 221 | "Julia types do not have member functions. The two (or 2.5) kinds of functions associated with types are constructors and functors." 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "id": "ecd8b976-7f60-4a48-9885-2b7efc895b89", 228 | "metadata": {}, 229 | "outputs": [], 230 | "source": [ 231 | "struct MyData\n", 232 | " x::Float64\n", 233 | " x2::Float64\n", 234 | " y::Float64\n", 235 | " z::Float64\n", 236 | "\n", 237 | " # inner constructor method (if you want to check for errors)\n", 238 | " function MyData(x::Float64, x2::Float64, y::Float64, z::Float64)\n", 239 | " if x < 0.0\n", 240 | " error(\"Can't use negative x value\")\n", 241 | " end\n", 242 | " new(x,x2,y,z) # `new` available only to inner constructor methods\n", 243 | " end\n", 244 | "end\n", 245 | "\n", 246 | "# outer constructor method (useful for custom or simplified constructors)\n", 247 | "function MyData(x::Float64, y::Float64)\n", 248 | " x2=x^2\n", 249 | " z = sin(x2+y)\n", 250 | " MyData(x, x2, y, z) # default constructor\n", 251 | "end\n", 252 | " \n", 253 | "\n", 254 | "println(\"Custom constructor: \", MyData(2.0, 3.0) )\n", 255 | "println(\"Default constructor: \", MyData(2.0, 4.0, 3.0, 0.6569865987187891) )" 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": null, 261 | "id": "a49c76fe-bad6-476b-8d99-4cc24d0cf550", 262 | "metadata": {}, 263 | "outputs": [], 264 | "source": [ 265 | "struct PrintFunc\n", 266 | " a::String\n", 267 | "end\n", 268 | "\n", 269 | "# Use the type instead of a function name\n", 270 | "function (func::PrintFunc)(arg)\n", 271 | " println(func.a, \" \", arg)\n", 272 | "end\n", 273 | "\n", 274 | "func = PrintFunc(\"hello\")\n", 275 | "# Then you can call an object as a function\n", 276 | "func(2)\n", 277 | "func(\"Philip\")" 278 | ] 279 | }, 280 | { 281 | "cell_type": "code", 282 | "execution_count": null, 283 | "id": "1c6c799f-5f72-4a2a-ae6d-ef95d595c0c2", 284 | "metadata": {}, 285 | "outputs": [], 286 | "source": [] 287 | } 288 | ], 289 | "metadata": { 290 | "kernelspec": { 291 | "display_name": "Julia 1.11.6", 292 | "language": "julia", 293 | "name": "julia-1.11" 294 | }, 295 | "language_info": { 296 | "file_extension": ".jl", 297 | "mimetype": "application/julia", 298 | "name": "julia", 299 | "version": "1.11.6" 300 | } 301 | }, 302 | "nbformat": 4, 303 | "nbformat_minor": 5 304 | } 305 | -------------------------------------------------------------------------------- /notebooks/01_Variables-and-Types.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Variables and Types \n", 8 | "\n", 9 | "**Contents**\n", 10 | " - [Variables](#Variables)\n", 11 | " - [Types](#Types)\n", 12 | " - [Conclusion](#Conclusion)\n", 13 | "\n", 14 | "In this lesson you will learn about variables and how to perform simple mathematical operations. Furthermore, we will deal with the concept of “types” and their role in Julia as `typing` (in the Python sense) and `type-safety` are parts of the language.\n", 15 | "\n", 16 | "## Variables\n", 17 | "\n", 18 | "[Manual](https://docs.julialang.org/en/v1/manual/variables/)\n", 19 | "\n", 20 | "Julia is not very different from other language for simple variables, just assign data to a variable.\n", 21 | "A few important things:\n", 22 | "\n", 23 | "1. Julia doesn't support member functions: **Never** `variable.function() will appear`, but always `function(variable)` just like in C or Fortran.\n", 24 | "2. Variables can be strings, numbers, multidimensional arrays, and higher-level constructs: dictionaries, structs.\n", 25 | "3. Understand default types for your system if you're not assigning types explicitly (see [Types](#Types)).\n", 26 | "\n", 27 | "In Julia, variables can be global or local scope. **Note: avoid global variables when working in shared code (even with yourself). Why? Name conflicts and types can't be assigned.**\n" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": null, 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "# Use # for comments\n", 37 | "# I'm a global variable...don't use global variables unless it's necessary (e.g. library)\n", 38 | "globalVariable = \"Don't use me\"\n", 39 | "\n", 40 | "function run()\n", 41 | " # local variables \n", 42 | " name = \"Philip W Fackler\"\n", 43 | " nmoves = 13\n", 44 | " years_married = (2025 - 2007) + (6 - 5) / 12.0\n", 45 | " kids = ( \"Matthew\" => 9, \"Butter\" => 4, \"Stripes\" => 1, \"Socks\" => 1) \n", 46 | " years = [ 2026, 2021, 2024, 2024 ]\n", 47 | " \n", 48 | " # Unlike Python print doesn't add a newline \\n by default. \n", 49 | " # Julia uses print and println\n", 50 | " # It understands higher-level objects (e.g. dictionary)\n", 51 | " println(name)\n", 52 | " println(nmoves)\n", 53 | " println(years_married)\n", 54 | " println(kids)\n", 55 | " println(years)\n", 56 | " return\n", 57 | "end \n", 58 | "\n", 59 | "# run your local function\n", 60 | "run()" 61 | ] 62 | }, 63 | { 64 | "cell_type": "markdown", 65 | "metadata": {}, 66 | "source": [ 67 | "## Types\n", 68 | "\n", 69 | "[Manual](https://docs.julialang.org/en/v1/manual/types/)\n", 70 | "\n", 71 | "In Julia every element has a type. The type system is a hierarchical structure: at the top of the tree there is the type `Any`, which means that every element belongs to it, then there are many other sub-types, for example `Number` which includes `Real` and `Complex`, and `Real` contains for example `Int` (integer) numbers and `Float64` numbers.\n", 72 | "\n", 73 | "\"Drawing\"\n", 74 | "\n", 75 | "Julia enables out-of-the-box type safety as types can be explicitly assigned using the `::` notation, this is preferred for high-performance code as the compiler would actually provide checks and optimizations. In Python, the [typing module](https://docs.python.org/3/library/typing.html) was added in v3.5 for type hints.\n", 76 | "\n", 77 | "Types can be retrieved using the `typeof` function. It's good practice to be aware of default types on your system:" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "println(\"Default types:\")\n", 87 | "function runWithoutTypes()\n", 88 | " name = \"Philip W Fackler\"\n", 89 | " nmoves = 13\n", 90 | " years_married = 18.08\n", 91 | " kids = ( \"Matthew\" => 9, \"Butter\" => 4, \"Stripes\" => 1, \"Socks\" => 1) \n", 92 | " years = [ 2026, 2021, 2024, 2024 ]\n", 93 | " \n", 94 | " println(\"name: \", name, \" type: \", typeof(name) )\n", 95 | " println(\"nmoves: \", nmoves, \" type: \", typeof(nmoves))\n", 96 | " println(\"years_married: \", years_married, \" type: \", typeof(years_married))\n", 97 | " printstyled(kids, \" type: \", typeof(kids), \"\\n\" ; color = :red)\n", 98 | " println(\"years: \", years, \" type: \", typeof(years))\n", 99 | " return\n", 100 | "end \n", 101 | "\n", 102 | "println()\n", 103 | "runWithoutTypes()\n", 104 | "\n", 105 | "println(\"\")\n", 106 | "println(\"\")\n", 107 | "println(\"Assigned types:\")\n", 108 | "function runWithTypes()\n", 109 | " name::String = \"Philip\"\n", 110 | " nmoves::Int32 = 13\n", 111 | " years_married::Float32 = 18.08 \n", 112 | " kids::Dict{String,Int32} = Dict( \"Matthew\"=>9, \"Butter\"=>4, \"Stripes\"=>1, \"Socks\"=>1 ) \n", 113 | " years::Array{Int32,1} = [ 2026, 2021, 2024, 2024 ]\n", 114 | " \n", 115 | " println(\"name: \", name, \" type: \", typeof(name) )\n", 116 | " println(\"nmoves: \", nmoves, \" type: \", typeof(nmoves))\n", 117 | " println(\"years_married: \", years_married, \" type: \", typeof(years_married))\n", 118 | " printstyled(kids, \" type: \", typeof(kids), \"\\n\" ; color = :red)\n", 119 | " println(\"years: \", years, \" type: \", typeof(years))\n", 120 | " return\n", 121 | "end\n", 122 | "\n", 123 | "println()\n", 124 | "runWithTypes()" 125 | ] 126 | }, 127 | { 128 | "cell_type": "markdown", 129 | "metadata": {}, 130 | "source": [ 131 | "**NOTE: the dictionary type Dict must be typed at least on the right-side of =, otherwise Julia assings a Tuple** \n", 132 | "\n", 133 | "### Avoid changing the type of a variable\n", 134 | "It is good programming practice and is also critical for performance that inside a program a variable is “type stable”. This means that if we have assigned `a = 42` it is better not to assign a new value which cannot be converted to `Int` without losing information, like a `Float64` `a = 0.42` (if we convert a `Float64` to an `Int`, the decimal part gets truncated).\n", 135 | "\n", 136 | "If we know that a variable (such as a) will have to contain values of type `Float64` it is better to initialise it with a value that is already of that type." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "function run()\n", 146 | " a = 2 # if we need to operate with ints\n", 147 | " println(a, \", typeof(a): \", typeof(a))\n", 148 | " b = 2.0 # if we need to operate with floats\n", 149 | " println(b, \", typeof(b): \", typeof(b))\n", 150 | " \n", 151 | " # a = 2.0 # not a good practice, type is Float64 by default\n", 152 | " a = convert(Float32, a) # only if required, we are aware of truncation\n", 153 | " println(a, \", typeof(a): \", typeof(a))\n", 154 | "\n", 155 | " # if you specify type\n", 156 | " d::UInt = 3\n", 157 | " d = convert(Float32, d)\n", 158 | " println(d, \", typeof(d): \", typeof(d))\n", 159 | " return\n", 160 | "end\n", 161 | "\n", 162 | "run()" 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "Notice how types can be used as values. Types are \"first-class\" citizens in Julia.\n", 170 | "\n", 171 | "### Other types of types\n", 172 | "I view abstract types more like type constraints (or concepts).\n", 173 | "(Starts to make more sense when used as function parameters or struct members.)\n", 174 | "\n", 175 | "Other than types in the normal hierarchy, there are also union types\n", 176 | "\n", 177 | "- Parametric types used without parameters (UnionAll)\n", 178 | "- Unions\n", 179 | "\n", 180 | "This is especially useful for constraining generic function parameters" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": {}, 187 | "outputs": [], 188 | "source": [ 189 | "struct Point{T}\n", 190 | " x::T\n", 191 | " y::T\n", 192 | "end\n", 193 | "@show typeof(Point{Float64})\n", 194 | "@show typeof(Point)\n", 195 | "\n", 196 | "a::Union{Int, String} = 1\n", 197 | "@show typeof(Union{Int, String})\n", 198 | "println(typeof(a))\n", 199 | "a = \"hello\"\n", 200 | "println(typeof(a))" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "## Type Hierarchy Examples" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": null, 213 | "metadata": {}, 214 | "outputs": [], 215 | "source": [ 216 | "@show supertypes(Float64)\n", 217 | "@show supertypes(Vector{Float64})\n", 218 | "@show supertypes(DataType)\n", 219 | "\n", 220 | "using AbstractTrees\n", 221 | "AbstractTrees.children(x::Type) = subtypes(x)\n", 222 | "println()\n", 223 | "print_tree(Number)\n", 224 | "println()\n", 225 | "print_tree(AbstractVector)" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "# Conclusion\n", 233 | "We have learned what variables are, how to perform basic operations and we have dealt about types." 234 | ] 235 | }, 236 | { 237 | "cell_type": "markdown", 238 | "metadata": {}, 239 | "source": [ 240 | "Questions:\n", 241 | "\n", 242 | "1. Does Julia support unsigned integers?\n", 243 | "2. Does Julia support member functions?\n", 244 | "3. What's the abstraction for any type?" 245 | ] 246 | } 247 | ], 248 | "metadata": { 249 | "kernelspec": { 250 | "display_name": "Julia 1.11.6", 251 | "language": "julia", 252 | "name": "julia-1.11" 253 | }, 254 | "language_info": { 255 | "file_extension": ".jl", 256 | "mimetype": "application/julia", 257 | "name": "julia", 258 | "version": "1.11.6" 259 | } 260 | }, 261 | "nbformat": 4, 262 | "nbformat_minor": 4 263 | } 264 | -------------------------------------------------------------------------------- /notebooks/13_Package-Development.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "910d3114-8c2b-4e70-8214-684490f5844a", 6 | "metadata": {}, 7 | "source": [ 8 | "# Package Development (Quick Start)\n", 9 | "\n", 10 | "This covers the basics of creating and developing a Julia package.\n", 11 | "For more details see the [`Pkg` docs](https://pkgdocs.julialang.org/v1/).\n", 12 | "\n", 13 | "*__NOTE__* : From the Julia REPL, you can enter the Pkg REPL by pressing `]` (and return to the Julia REPL with backspace). This allows typing commands instead of functions. We will show both representations for the operations covered here. The function version (actual Julia code) can be executed.\n", 14 | "\n", 15 | "*__WARNING__* : This notebook is for reference. It will work if run linearly from the beginning. But if you start over within the same session, you will have problems.\n", 16 | "\n", 17 | "It might make more sense to perform the operations in a terminal and a text editor." 18 | ] 19 | }, 20 | { 21 | "cell_type": "markdown", 22 | "id": "9aa16c5b-0609-4d2c-8da3-0f513a6337d1", 23 | "metadata": {}, 24 | "source": [ 25 | "## Creating a package\n", 26 | "\n", 27 | "When you create a package, you also create a dependency environment specific to your project. We'll look at this step-by-step.\n" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "id": "53026cbe-a97d-4176-aa33-8daafc378877", 33 | "metadata": {}, 34 | "source": [ 35 | "### Generate a package (\"project\")\n", 36 | "```\n", 37 | "(@v1.11) pkg> generate MyPack\n", 38 | "```" 39 | ] 40 | }, 41 | { 42 | "cell_type": "code", 43 | "execution_count": null, 44 | "id": "5bad3016-0f99-4774-ae95-59b553fdd1f4", 45 | "metadata": {}, 46 | "outputs": [], 47 | "source": [ 48 | "import Pkg\n", 49 | "Pkg.generate(\"MyPack\")" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "id": "8616962c-ee8f-4fc0-bede-43aa0fcca90f", 55 | "metadata": {}, 56 | "source": [ 57 | "### Package status\n", 58 | "```\n", 59 | "(@v1.11) pkg> status\n", 60 | "```" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "id": "7594fe08-88a1-42f8-acd8-6b0a616a9633", 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "Pkg.status()" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "id": "62976cef-f384-4e93-86ae-e7099bf84ea7", 76 | "metadata": {}, 77 | "source": [ 78 | "Note that we are actually in the `julia-basics` environment. By default at the terminal you would start in the default Julia (@v1.11 when this was written) environment." 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "id": "cbfcba74-5cfc-41a3-9651-af2bfc2b2bce", 84 | "metadata": {}, 85 | "source": [ 86 | "### Activate package environment\n", 87 | "```\n", 88 | "(@v1.11) pkg> activate MyPack\n", 89 | " Activating project at ...\n", 90 | "(MyPack) pkg> status\n", 91 | "```" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": null, 97 | "id": "45b0484b-0c1c-45d5-9b56-0c2e3fceed4e", 98 | "metadata": {}, 99 | "outputs": [], 100 | "source": [ 101 | "Pkg.activate(\"MyPack\")" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "id": "2d9e53a6-765e-4d5e-afe5-93c88911a93e", 108 | "metadata": {}, 109 | "outputs": [], 110 | "source": [ 111 | "Pkg.status()" 112 | ] 113 | }, 114 | { 115 | "cell_type": "markdown", 116 | "id": "933208b5-5792-4fc7-b867-5aec25efedd3", 117 | "metadata": {}, 118 | "source": [ 119 | "### Add dependencies\n", 120 | "\n", 121 | "Notice how the project environment is empty, that is, there are no packages available to use. To use packages within your environment, you must add them as dependencies.\n", 122 | "\n", 123 | "```\n", 124 | "(MyPack) pkg> add ArgParse YAML StaticArrays\n", 125 | "```" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "id": "ccc20cb0-2f59-4261-ab1a-7f55090a815f", 132 | "metadata": {}, 133 | "outputs": [], 134 | "source": [ 135 | "Pkg.add([\"ArgParse\", \"YAML\", \"StaticArrays\"])" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "id": "2db0fa46-788e-42fc-938d-505eda0d25ce", 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "Pkg.status()" 146 | ] 147 | }, 148 | { 149 | "cell_type": "markdown", 150 | "id": "56d4f56b-18ae-4abb-a65d-81deeb05fa77", 151 | "metadata": {}, 152 | "source": [ 153 | "Project information is kept in the `Project.toml` file. Let's take a look." 154 | ] 155 | }, 156 | { 157 | "cell_type": "code", 158 | "execution_count": null, 159 | "id": "eb983dfa-b723-4c1b-a649-bbccd9313f33", 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "function catFile(fname::AbstractString)\n", 164 | " for line in readlines(fname)\n", 165 | " println(line)\n", 166 | " end\n", 167 | "end" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": null, 173 | "id": "fd65c798-00eb-422f-a337-084db7490061", 174 | "metadata": {}, 175 | "outputs": [], 176 | "source": [ 177 | "catFile(\"MyPack/Project.toml\")" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "id": "2b5f5e9c-774b-4b1e-b490-688d76f30f79", 183 | "metadata": {}, 184 | "source": [ 185 | "### Write some code\n", 186 | "\n", 187 | "The top-level module associated with a project has the same name as the package. This was already generated for you in `src/MyPack.jl`:" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": null, 193 | "id": "1172186e-e5a5-4bd4-8af0-e4d0156cc5eb", 194 | "metadata": {}, 195 | "outputs": [], 196 | "source": [ 197 | "catFile(\"MyPack/src/MyPack.jl\")" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": null, 203 | "id": "16250e11-8581-4579-8d38-2823ba7881b9", 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "import MyPack\n", 208 | "MyPack.greet()" 209 | ] 210 | }, 211 | { 212 | "cell_type": "markdown", 213 | "id": "017c1c5f-d97f-4f29-b4fd-89c781463675", 214 | "metadata": {}, 215 | "source": [ 216 | "Let's replace the code in `src/MyPack.jl` to do something different." 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": null, 222 | "id": "196f060d-4813-4649-963a-bce7fb6c9735", 223 | "metadata": {}, 224 | "outputs": [], 225 | "source": [ 226 | "write(\"MyPack/src/MyPack.jl\", \"\"\"\n", 227 | " module MyPack\n", 228 | "\n", 229 | " function modifyArg(arg::AbstractString)\n", 230 | " return \"Hello \" * arg\n", 231 | " end\n", 232 | "\n", 233 | " function modifyArg(arg::Number)\n", 234 | " return arg + 5\n", 235 | " end\n", 236 | " \n", 237 | " end\n", 238 | " \"\"\")" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "id": "bd5fa370-9f13-4415-8393-fab968e9001f", 245 | "metadata": {}, 246 | "outputs": [], 247 | "source": [ 248 | "MyPack.modifyArg(\"William\")" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "id": "aa85bb08-41b7-4630-9387-d0db24513573", 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [ 258 | "MyPack.modifyArg(23)" 259 | ] 260 | }, 261 | { 262 | "cell_type": "markdown", 263 | "id": "8d8cbe90-a826-4197-bb9c-115bedd14438", 264 | "metadata": {}, 265 | "source": [ 266 | "## Testing\n", 267 | "\n", 268 | "In order for your package to be testable by the `Pkg` system, you must follow a convention. You need a `test` directory containing a `Project.toml` for your testing environment and a `runtests.jl` script as the entry point for your tests.\n", 269 | "\n", 270 | "The first two requirements can be handled from `Pkg`:\n", 271 | "```\n", 272 | "(MyPack) pkg> activate test\n", 273 | "(test) pkg> add Test\n", 274 | "```\n", 275 | "Here we generate the testing environment (and directory) and add the `Test` package as a testing dependency." 276 | ] 277 | }, 278 | { 279 | "cell_type": "code", 280 | "execution_count": null, 281 | "id": "3a75d38b-e2ee-4cd5-ab5d-46489f9978db", 282 | "metadata": {}, 283 | "outputs": [], 284 | "source": [ 285 | "Pkg.activate(\"MyPack/test\")\n", 286 | "Pkg.add(\"Test\")" 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "execution_count": null, 292 | "id": "4369ddb8-35f9-4ef0-b57e-3f5becc1cde7", 293 | "metadata": {}, 294 | "outputs": [], 295 | "source": [ 296 | "catFile(\"MyPack/test/Project.toml\")" 297 | ] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "id": "a6fccc51-b145-4210-a691-c2ddbfe9e855", 302 | "metadata": {}, 303 | "source": [ 304 | "The built-in `Test` package includes basic tools for testing. We'll write a quick test using `@testset`, `@test`, and `@test_throws`:" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": null, 310 | "id": "1871bcd8-c095-4a0a-97c3-977f82a7450d", 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "write(\"MyPack/test/runtests.jl\", \"\"\"\n", 315 | " import MyPack\n", 316 | " using Test\n", 317 | "\n", 318 | " @testset \"reportArg\" begin\n", 319 | " @test MyPack.modifyArg(42) == 47\n", 320 | " @test MyPack.modifyArg(\"Frank\") == \"Hello Frank\"\n", 321 | " \n", 322 | " @test_throws MethodError MyPack.modifyArg((1,2))\n", 323 | " end\n", 324 | " \"\"\")" 325 | ] 326 | }, 327 | { 328 | "cell_type": "markdown", 329 | "id": "1c241dbb-37bd-4157-a932-4c6ae1a76837", 330 | "metadata": {}, 331 | "source": [ 332 | "Let's reactivate the project environment (since the `test` is currently active) and run the tests\n", 333 | "```\n", 334 | "(test) pkg> activate MyPack\n", 335 | "(MyPack) pkg> test\n", 336 | "```" 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": null, 342 | "id": "f31fde8f-61b1-49fc-8521-47f00cc32dd6", 343 | "metadata": {}, 344 | "outputs": [], 345 | "source": [ 346 | "Pkg.activate(\"MyPack\")" 347 | ] 348 | }, 349 | { 350 | "cell_type": "code", 351 | "execution_count": null, 352 | "id": "2c120403-276b-4342-bfd3-3d51ee541f06", 353 | "metadata": {}, 354 | "outputs": [], 355 | "source": [ 356 | "Pkg.test()" 357 | ] 358 | }, 359 | { 360 | "cell_type": "markdown", 361 | "id": "23f3151c-824b-49d2-8a22-f4ceeb33fcc5", 362 | "metadata": {}, 363 | "source": [ 364 | "`Pkg.test()` sets up a clean environment for testing (in a temp directory, separate from the project or test environment). It automatically has `MyPack` as a dependency and includes whatever is in `test/Project.toml` (in this case, just `Test`)." 365 | ] 366 | }, 367 | { 368 | "cell_type": "code", 369 | "execution_count": null, 370 | "id": "5ec73596-8811-41fa-9504-c7202a7e5359", 371 | "metadata": {}, 372 | "outputs": [], 373 | "source": [] 374 | } 375 | ], 376 | "metadata": { 377 | "kernelspec": { 378 | "display_name": "Julia 1.11.6", 379 | "language": "julia", 380 | "name": "julia-1.11" 381 | }, 382 | "language_info": { 383 | "file_extension": ".jl", 384 | "mimetype": "application/julia", 385 | "name": "julia", 386 | "version": "1.11.6" 387 | } 388 | }, 389 | "nbformat": 4, 390 | "nbformat_minor": 5 391 | } 392 | -------------------------------------------------------------------------------- /notebooks/04_Control-Flow.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Control Flow\n", 8 | "\n", 9 | "In this lesson we will learn how to work with control statements. We will first learn how to use conditional blocks like `if ... else` blocks and then we will learn how to perform loops.\n", 10 | "\n", 11 | "## Contents\n", 12 | "\n", 13 | "- [if...else](##if...else)\n", 14 | "- [for loops](##for-loops)\n", 15 | "- [enumerate](##enumerate)\n", 16 | "- [break](##break)\n", 17 | "- [continue](##continue)\n", 18 | "- [while loops](##while-loops)\n", 19 | "- [Conclusion](##Conclusion)" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "## if...else\n", 27 | "\n", 28 | "When a program needs to take decisions according to certain conditions, the `if ... else` block is the default choice.\n", 29 | "\n", 30 | "Let’s suppose that we want to write a simple implementation of the absolute value of a number. The absolute value of a number is defined as the number itself, if the number is positive, or the opposite if the value is negative. This is the typical case where the `if ... else` construct is useful! We can write a simple absolute function in this way:" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": null, 36 | "metadata": {}, 37 | "outputs": [], 38 | "source": [ 39 | "function absolute(x)\n", 40 | " if x >= 0\n", 41 | " return x\n", 42 | " else\n", 43 | " return -x\n", 44 | " end\n", 45 | "end\n", 46 | "\n", 47 | "println(absolute(-1))\n", 48 | "println(absolute(1))" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": {}, 54 | "source": [ 55 | "As you can see, an `if ... else` block is closed with the word `end`, like a `function`.\n", 56 | "\n", 57 | "If we need to check more than one condition, we can bind together conditions using:\n", 58 | "\n", 59 | "- \"negation\" is written as `!`\n", 60 | "- “and” is written as `&&` \n", 61 | "- “or” is written as `||`\n", 62 | "\n", 63 | "**NOTE:** same as C or C++\n", 64 | "\n", 65 | "For example, if we want to check whether 3 is both minor than 4 and major than 1 we type:" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "if 1 < 3 && 3 < 4\n", 75 | " println(\"eureka! &&\")\n", 76 | "end" 77 | ] 78 | }, 79 | { 80 | "cell_type": "markdown", 81 | "metadata": {}, 82 | "source": [ 83 | "If we want to check if a value satisfies one of several different conditions, we can use the `elseif` statement: Julia will check if the first condition specified in the if is satisfied, if it is not met it moves on to the first `elseif` and so on:" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "function elseifFun(x)\n", 93 | " if x<1\n", 94 | " println(\"$x < 1\")\n", 95 | " elseif x < 3\n", 96 | " println(\"$x < 3\")\n", 97 | " elseif x < 100\n", 98 | " println(\"$x < 100\")\n", 99 | " else\n", 100 | " println(\"$x is really big!\")\n", 101 | " end\n", 102 | " return\n", 103 | "end\n", 104 | "\n", 105 | "elseifFun(42)\n", 106 | "elseifFun(100)\n", 107 | "elseifFun(0)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "## for loops\n", 115 | "\n", 116 | "**Ranges**: There is many ways to iterate over a list of values and perform some operation on each element.\n", 117 | "\n", 118 | " - `for i in min:max`\n", 119 | " - `for i = min:max`\n", 120 | "\n", 121 | "Ranges can be defined explicitly using the [`range` function](https://docs.julialang.org/en/v1/base/math/#Base.range).\n", 122 | "For example let’s suppose we want to print all the squares of the numbers comprised between 1 and 10, we can do it using a for loop:" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "function forloopFun()\n", 132 | "\n", 133 | " println(\"Using in\")\n", 134 | " for i in 1:10\n", 135 | " print(i^2, \" \")\n", 136 | " end\n", 137 | " println()\n", 138 | "\n", 139 | " println()\n", 140 | " println(\"Using =\")\n", 141 | " for i = 1:10\n", 142 | " print(i^2, \" \")\n", 143 | " end\n", 144 | " println()\n", 145 | "\n", 146 | " println()\n", 147 | " println(\"Using in and range function\")\n", 148 | " for i in range(1,stop=10)\n", 149 | " print(i^2, \" \")\n", 150 | " end\n", 151 | " println()\n", 152 | " \n", 153 | " println()\n", 154 | " println(\"Using length function for ranges\")\n", 155 | " my_array1 = collect(1:10) # 1, 2, ..., 10\n", 156 | " my_array2 = zeros(10)\n", 157 | " for i in 1:length(my_array1)\n", 158 | " my_array2[i] = my_array1[i]^2\n", 159 | " end\n", 160 | " println(my_array2)\n", 161 | " \n", 162 | " return\n", 163 | "end\n", 164 | "\n", 165 | "forloopFun()" 166 | ] 167 | }, 168 | { 169 | "cell_type": "markdown", 170 | "metadata": {}, 171 | "source": [ 172 | "`i` is the variable which contains the data which gets updated at each new cycle (in this case i holds the numbers from 1 to 10 respectively), while `1:10` is a *range*. A range is an iterable object which does exactly what its name suggests: it specifies the range on which the loop has to be performed (in this case 1 to 10).\n", 173 | "\n", 174 | "**Lists**: we can iterate element by element in data structures that can be represented as lists: arrays, tuples, dictionaries (key/value pairs or keys only). \n", 175 | "\n", 176 | "For example, let’s suppose we have an array and a dictionary:" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": null, 182 | "metadata": {}, 183 | "outputs": [], 184 | "source": [ 185 | "function forlist()\n", 186 | " persons = [\"Alice\", \"Bob\", \"Carla\", \"Daniel\"]\n", 187 | "\n", 188 | " for person in persons\n", 189 | " println(\"Hello $person, welcome to this Tutorial!\")\n", 190 | " end\n", 191 | " \n", 192 | " println()\n", 193 | " languages = Dict(\"Alice\" => \"Python\", \"Bob\" => \"Fortran\", \"Carla\" => \"C++\", \"Daniel\" => \"C\")\n", 194 | " \n", 195 | " # don't forget () in (key, value)\n", 196 | " println(\"Keys and Values:\")\n", 197 | " for (person, language) in languages\n", 198 | " println(\" $person uses $language\")\n", 199 | " end\n", 200 | " println()\n", 201 | " \n", 202 | " println(\"Keys:\")\n", 203 | " for key in keys(languages)\n", 204 | " println(\" $key\")\n", 205 | " end\n", 206 | " \n", 207 | " return\n", 208 | "end\n", 209 | "\n", 210 | "forlist()" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "## enumerate \n", 218 | "\n", 219 | "`enumerate` is a function which comes in handy when we need to iterate on an array (or similar) and we need to keep track of the number of iterations we have already performed.\n", 220 | "\n", 221 | "enumerate will return an iterator (which is something like an array which can be iterated in for loops). It will produce couples of the form `(i, x[i]).`" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "function enumerateFun()\n", 231 | " println(\"enumerate array\")\n", 232 | " x = [\"a\",\"b\",\"c\"]\n", 233 | " for couple in enumerate(x)\n", 234 | " println(couple)\n", 235 | " end\n", 236 | " println(\"\")\n", 237 | " \n", 238 | " languages = Dict(\"Alice\" => \"Python\", \"Bob\" => \"Fortran\", \"Carla\" => \"C++\", \"Daniel\" => \"C\")\n", 239 | " \n", 240 | " println(\"enumerate dictionary\")\n", 241 | " for value in enumerate(languages)\n", 242 | " print(value)\n", 243 | " println(\" i=\", value[1], \" key=\", value[2].first, \" value=\", value[2].second )\n", 244 | " end\n", 245 | " \n", 246 | " return\n", 247 | "end\n", 248 | "\n", 249 | "enumerateFun()" 250 | ] 251 | }, 252 | { 253 | "cell_type": "markdown", 254 | "metadata": {}, 255 | "source": [ 256 | "## break\n", 257 | "\n", 258 | "In the case we want to forcefully interrupt a `for` loop we can use the `break` statement, for example:" 259 | ] 260 | }, 261 | { 262 | "cell_type": "code", 263 | "execution_count": null, 264 | "metadata": {}, 265 | "outputs": [], 266 | "source": [ 267 | "for i in 1:100\n", 268 | " if i>10\n", 269 | " break\n", 270 | " else\n", 271 | " println(i^2)\n", 272 | " end\n", 273 | "end" 274 | ] 275 | }, 276 | { 277 | "cell_type": "markdown", 278 | "metadata": {}, 279 | "source": [ 280 | "Here we check `if i>10`, in that case we break the loop and finish the iteration, else we print `i^2`.\n", 281 | "\n", 282 | "## continue\n", 283 | "\n", 284 | "This is the opposite of `break`, `continue` will forcefully skip the current iteration and move to the next cycle:" 285 | ] 286 | }, 287 | { 288 | "cell_type": "code", 289 | "execution_count": null, 290 | "metadata": {}, 291 | "outputs": [], 292 | "source": [ 293 | "for i in 1:10\n", 294 | " if i % 3 == 0\n", 295 | " continue\n", 296 | " else\n", 297 | " println(i)\n", 298 | " end\n", 299 | "end" 300 | ] 301 | }, 302 | { 303 | "cell_type": "markdown", 304 | "metadata": {}, 305 | "source": [ 306 | "This loop prints all the numbers from 1 to 10 except the multiples of 3.\n", 307 | "\n", 308 | "## while loops\n", 309 | "\n", 310 | "When a loop needs to continue until a certain condition is met, a while loop is the preferable choice:" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": null, 316 | "metadata": {}, 317 | "outputs": [], 318 | "source": [ 319 | "function whileFun()\n", 320 | " i=0\n", 321 | " while i<5\n", 322 | " println(i)\n", 323 | " i += 1\n", 324 | " end\n", 325 | "end\n", 326 | "\n", 327 | "whileFun()" 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "metadata": {}, 333 | "source": [ 334 | "## NOTE: no switch\n", 335 | "\n", 336 | "If you need an actual pattern-matching construct, the best I've seen is from [MLStyle](https://thautwarm.github.io/MLStyle.jl/latest/syntax/pattern.html), which has a lot of other intersting things too." 337 | ] 338 | }, 339 | { 340 | "cell_type": "markdown", 341 | "metadata": {}, 342 | "source": [ 343 | "## Conclusion\n", 344 | "\n", 345 | "In this lesson we have learned how to let a program take “decisions” using `if ... elseif ... else` blocks, how to perform loops using for and while and how to have control on such loops using `break` and `continue`. We have then given an example of how enumerate can be used to help the process of filling an array.\n", 346 | "\n", 347 | "**NOTE:** Julia does not have support for `switch` statements\n", 348 | "**NOTE:** use the length function to get array length for ranges\n", 349 | "\n", 350 | "\n", 351 | "## Questions\n", 352 | "\n", 353 | "1. What function is used to get the \"length\" of a data structure?\n", 354 | "2. Can you iterate through dictionaries using enumerate?\n", 355 | "3. Does Julia support `switch` statements?" 356 | ] 357 | }, 358 | { 359 | "cell_type": "code", 360 | "execution_count": null, 361 | "metadata": {}, 362 | "outputs": [], 363 | "source": [] 364 | } 365 | ], 366 | "metadata": { 367 | "kernelspec": { 368 | "display_name": "Julia 1.11.6", 369 | "language": "julia", 370 | "name": "julia-1.11" 371 | }, 372 | "language_info": { 373 | "file_extension": ".jl", 374 | "mimetype": "application/julia", 375 | "name": "julia", 376 | "version": "1.11.6" 377 | } 378 | }, 379 | "nbformat": 4, 380 | "nbformat_minor": 4 381 | } 382 | -------------------------------------------------------------------------------- /notebooks/07_Types-and-Structs.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Composite Types: struct \n", 8 | "\n", 9 | "In this lesson we will learn to create your own composite types also known as structs. We will learn: \n", 10 | "\n", 11 | "- Differences between abstract and concrete types\n", 12 | "- How to define immutable and mutable types \n", 13 | "- How to create a type constructor. \n", 14 | "- Brief intro to multiple dispatch and see how types have a role in it.\n", 15 | "\n", 16 | "\n", 17 | "## Contents\n", 18 | "\n", 19 | "- [Types as Containers for Data](#Types-as-Containers-for-Data)\n", 20 | "- [Defining Types](#Abstract-Types)\n", 21 | "- [Multiple Dispatch](#Multiple-Dispatch)\n", 22 | "- [Type Constructor](#Type-Constructor)\n", 23 | "\n", 24 | "\n", 25 | "## Types as Containers for Data\n", 26 | "\n", 27 | "We can think of types as \"containers for data\" only. Moreover, it is possible to define a type hierarchy so that functions that work for parent type work also for the children (if they are written properly). A parent type can only be an `AbstractType` (like `Number`), while a child can be both an abstract or concrete type (like `Float32`). In the following tree diagram, types in round bubbles are abstract types, while the ones in square bubbles are concrete types.\n", 28 | "\n", 29 | "\"Drawing\"\n", 30 | "\n", 31 | "\n", 32 | "## Defining Types\n", 33 | "\n", 34 | "To declare an `abstract type` we use:" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": null, 40 | "metadata": {}, 41 | "outputs": [], 42 | "source": [ 43 | "abstract type Person\n", 44 | "end\n", 45 | "\n", 46 | "abstract type Musician <: Person\n", 47 | "end" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "You may find it surprising, but apparently musicians are people, so Musician is a sub-type of Person. There are many kind of musicians, for example rock-stars and classic musicians, so we define two new concrete types (in particular this kind of type is called a composite type):" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "mutable struct Rockstar <: Musician\n", 64 | " name::String\n", 65 | " instrument::String\n", 66 | " bandName::String\n", 67 | " headbandColor::String\n", 68 | " instrumentsPlayed::Int\n", 69 | "end\n", 70 | "\n", 71 | "struct ClassicMusician <: Musician\n", 72 | " name::String\n", 73 | " instrument::String\n", 74 | "end" 75 | ] 76 | }, 77 | { 78 | "cell_type": "markdown", 79 | "metadata": {}, 80 | "source": [ 81 | "Notably rock-stars love to change the colour of their headband, so we have made `Rockstar` a `mutable struct`, which is a concrete type whose elements value can be modified. On the contrary, classic musicians are known for their everlasting love for their instrument, which will never change, so we have made `ClassicMusician` an immutable concrete type, which is the default behavior for structs (Rust made a similar design decision).\n", 82 | "\n", 83 | "We can define another sub-type of `Person`, `Physicist`:" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": null, 89 | "metadata": {}, 90 | "outputs": [], 91 | "source": [ 92 | "mutable struct Physicist <: Person\n", 93 | " name::String\n", 94 | " sleepHours::Float64\n", 95 | " programmingLanguage::String\n", 96 | "end\n", 97 | "\n", 98 | "aurelio = Physicist(\"Aurelio\", 6, \"Julia\")\n", 99 | "println(\"Name: \", aurelio.name)\n", 100 | "println(\"Sleep hours: \", aurelio.sleepHours)\n", 101 | "println(\"Programming Language: \", aurelio.programmingLanguage)" 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "metadata": {}, 107 | "source": [ 108 | "Since Physicist is a `mutable struct` we can adjust sleep hours:" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": null, 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [ 117 | "aurelio.sleepHours = 8\n", 118 | "println(\"New Sleep hours: \", aurelio.sleepHours)" 119 | ] 120 | }, 121 | { 122 | "cell_type": "markdown", 123 | "metadata": {}, 124 | "source": [ 125 | "Unlike a non-mutable struct, in which resetting data would fail:" 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "aurelio_musician = ClassicMusician(\"Aurelio\", \"Violin\")\n", 135 | "aurelio_musician.instrument = \"Guitar\"" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "As you can see, I love violin and I just can’t change my instrument, as ClassicMusician is an `immutable struct`.\n", 143 | "\n", 144 | "I am not a rock-star, but my friend Ricky is one, so we’ll define:" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "ricky = Rockstar(\"Riccardo\", \"Voice\", \"Black Lotus\", \"red\", 2)\n", 154 | "println(ricky)\n", 155 | "println(\"Ricky's headband color: \", ricky.headbandColor)" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "## Multiple Dispatch\n", 163 | "\n", 164 | "It is possible to write functions that operate on both abstract and concrete types. For example, every person is likely to have a name, so we can define the following function:" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "function introduceMe(person::Person)\n", 174 | " println(\"Hello, my name is $(person.name).\")\n", 175 | "end\n", 176 | "\n", 177 | "introduceMe(ricky)\n", 178 | "introduceMe(aurelio)" 179 | ] 180 | }, 181 | { 182 | "cell_type": "markdown", 183 | "metadata": {}, 184 | "source": [ 185 | "While only musicians play instruments, so we can define the following function:" 186 | ] 187 | }, 188 | { 189 | "cell_type": "code", 190 | "execution_count": null, 191 | "metadata": {}, 192 | "outputs": [], 193 | "source": [ 194 | "function introduceMe(person::Musician)\n", 195 | " println(\"Hello, my name is $(person.name) and I play $(person.instrument).\")\n", 196 | "end\n", 197 | "\n", 198 | "introduceMe(ricky)\n", 199 | "introduceMe(aurelio)\n", 200 | "introduceMe(aurelio_musician)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "The `::SomeType` notation indicates to Julia that person has to be of the aforementioned type or a sub-type. Only the most strict type requirement is considered (which is the lowest type in the type tree), for example ricky is a Person, but **more importantly** he is a `Rockstar` (Rockstar is placed lower in the type tree), thus `introduceMe(person::Rockstar)` is called. In other words, the function with the closest type signature will be called.\n", 208 | "\n", 209 | "This is an example of **multiple dispatch**, which means that we have written a single function with different methods depending on the type of the variable. We will come back again to multiple dispatch in this lesson, as it is one of the most important features of Julia and is considered a more advanced topic, together with type annotations. As an anticipation `::Rockstar` is a type annotation, the compiler will check if person is a Rockstar (or a sub-type of it) and if that is true it will execute the function.\n", 210 | "\n", 211 | "- **NOTE 1: since Julia is not object-oriented it favors multiple dispatch + composition**\n", 212 | "- **NOTE 2: abstract types do not have \"members\" or \"fields\"** see [issue for the design decision](https://github.com/JuliaLang/julia/issues/4935)\n", 213 | "- **NOTE 3: concrete types do not have \"member\" functions, only constructors**\n", 214 | "\n", 215 | "## Type Constructor\n", 216 | "\n", 217 | "[Manual](https://docs.julialang.org/en/v1/manual/constructors/)\n", 218 | "\n", 219 | "When we created the previous types, two constructors were generated automatically (these are called default constructors). One accepts any arguments and calls convert to convert them to the types of the fields, and the other accepts arguments that match the field types exactly (`String` and `String` in the case of `ClassicMusician`). The reason both of these are generated is that this makes it easier to add new definitions without inadvertently replacing a default constructor.\n", 220 | "\n", 221 | "Sometimes it is more convenient to create custom constructor, so that it is possible to assign default values to certain variables, or perform some initial computations." 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": {}, 228 | "outputs": [], 229 | "source": [ 230 | "struct MyData\n", 231 | " x::Float64\n", 232 | " x2::Float64\n", 233 | " y::Float64\n", 234 | " z::Float64\n", 235 | "\n", 236 | " # inner constructor method (if you want to check for errors)\n", 237 | " function MyData(x::Float64, x2::Float64, y::Float64, z::Float64)\n", 238 | " if x < 0.0\n", 239 | " error(\"Can't use negative x value\")\n", 240 | " end\n", 241 | " new(x,x2,y,z) # `new` available only to inner constructor methods\n", 242 | " end\n", 243 | "end\n", 244 | "\n", 245 | "# outer constructor method (useful for custom or simplified constructors)\n", 246 | "function MyData(x::Float64, y::Float64)\n", 247 | " x2=x^2\n", 248 | " z = sin(x2+y)\n", 249 | " MyData(x, x2, y, z) # default constructor\n", 250 | "end\n", 251 | " \n", 252 | "\n", 253 | "println(\"Custom constructor: \", MyData(2.0, 3.0) )\n", 254 | "println(\"Default constructor: \", MyData(2.0, 4.0, 3.0, 0.6569865987187891) )" 255 | ] 256 | }, 257 | { 258 | "cell_type": "markdown", 259 | "metadata": {}, 260 | "source": [ 261 | "Sometimes it may be useful to use other types for x, x2 and y, so it is possible to use parametric `{T}` types (i.e. types that accept type information at construction time):" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": null, 267 | "metadata": {}, 268 | "outputs": [], 269 | "source": [ 270 | "struct MyData2{T<:Real}\n", 271 | " x::T\n", 272 | " x2::T\n", 273 | " y::T\n", 274 | " z::Float64\n", 275 | "end\n", 276 | "\n", 277 | "function MyData2(x::T, y::T) where {T<:Real} # deduce T\n", 278 | " x2=x^2\n", 279 | " z = sin(x2+y)\n", 280 | " MyData2{T}(x, x2, y, z) # use T for concrete type\n", 281 | "end\n", 282 | "\n", 283 | "a = MyData2(2.0,3.0)\n", 284 | "b = MyData2(2,3)\n", 285 | "\n", 286 | "println(\"a: \", a)\n", 287 | "println(\"b: \", b)" 288 | ] 289 | }, 290 | { 291 | "cell_type": "markdown", 292 | "metadata": {}, 293 | "source": [ 294 | "## Functors\n", 295 | "\n", 296 | "While types cannot have member functions, you can define functions that make an instance of a type \"callable\"." 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": null, 302 | "metadata": {}, 303 | "outputs": [], 304 | "source": [ 305 | "struct PrintFunc\n", 306 | " a::String\n", 307 | "end\n", 308 | "\n", 309 | "function (func::PrintFunc)(arg)\n", 310 | " println(func.a, \" \", arg)\n", 311 | "end\n", 312 | "\n", 313 | "func = PrintFunc(\"hello\")\n", 314 | "func(2)\n", 315 | "func(\"Philip\")" 316 | ] 317 | }, 318 | { 319 | "cell_type": "markdown", 320 | "metadata": {}, 321 | "source": [ 322 | "## Conclusions\n", 323 | "\n", 324 | "We have learned how to define abstract and concrete types, and how to define mutable and immutable structures. We have then learnt how it is possible to define functions that work on custom types and we have introduced multiple dispatch.\n", 325 | "\n", 326 | "## Questions\n", 327 | "\n", 328 | "1. Can we have members in an abstract type?\n", 329 | "2. Can we have functions in concrete types?\n", 330 | "3. Is multiple dispatch the same as function overload?" 331 | ] 332 | }, 333 | { 334 | "cell_type": "code", 335 | "execution_count": null, 336 | "metadata": {}, 337 | "outputs": [], 338 | "source": [] 339 | } 340 | ], 341 | "metadata": { 342 | "kernelspec": { 343 | "display_name": "Julia 1.11.6", 344 | "language": "julia", 345 | "name": "julia-1.11" 346 | }, 347 | "language_info": { 348 | "file_extension": ".jl", 349 | "mimetype": "application/julia", 350 | "name": "julia", 351 | "version": "1.11.6" 352 | }, 353 | "toc-autonumbering": false 354 | }, 355 | "nbformat": 4, 356 | "nbformat_minor": 4 357 | } 358 | -------------------------------------------------------------------------------- /distinctions/05_Modules_and_Packaging.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "d5ed20a2-700a-43dd-8fbb-79704619b1f1", 6 | "metadata": {}, 7 | "source": [ 8 | "# Modules\n", 9 | "\n", 10 | "Manual: [Modules](https://docs.julialang.org/en/v1/manual/modules/)\n", 11 | "\n", 12 | "- Provides scope boundary, like namespaces\n", 13 | "- Enables precompilation at load\n", 14 | "- May be directly associated with a package" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "id": "e096a295-defd-4c90-bf4e-ac48cdf7c388", 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "# Opening of module definition (closed with `end`)\n", 25 | "module MyMod\n", 26 | "\n", 27 | "# Export names that are \"public\"\n", 28 | "export foo\n", 29 | "\n", 30 | "function foo()\n", 31 | " #...\n", 32 | "end\n", 33 | "\n", 34 | "# Bring the module name into context\n", 35 | "# Explicitly access members: e.g., Random.default_rng()\n", 36 | "import Random\n", 37 | "\n", 38 | "# Bring exported names from package into current context\n", 39 | "# You can call `load` directly (rather than `YAML.load`)\n", 40 | "using YAML\n", 41 | "\n", 42 | "# Alias for an import (useful if the name `SpecialFunctions` already refers to something else)\n", 43 | "import SpecialFunctions as SF\n", 44 | "# Import just one name into current context\n", 45 | "import SpecialFunctions: cosint\n", 46 | "\n", 47 | "# Submodule relations\n", 48 | "module MySub1\n", 49 | "foo() = \"foo\"\n", 50 | "bar() = \"bar\"\n", 51 | "end\n", 52 | "\n", 53 | "module MySub2\n", 54 | "import ..MySub1\n", 55 | "baz() = MySub1.foo()\n", 56 | "end\n", 57 | "\n", 58 | "import .MySub1.bar\n", 59 | "\n", 60 | "end\n", 61 | ";" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "id": "900de802-507d-4352-a274-8403b3cc693b", 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "@show MyMod.MySub2.baz()\n", 72 | "@show MyMod.bar()\n", 73 | ";" 74 | ] 75 | }, 76 | { 77 | "cell_type": "markdown", 78 | "id": "910d3114-8c2b-4e70-8214-684490f5844a", 79 | "metadata": {}, 80 | "source": [ 81 | "# Package Development (Quick Start)\n", 82 | "\n", 83 | "This covers the basics of creating and developing a Julia package.\n", 84 | "For more details see the [`Pkg` docs](https://pkgdocs.julialang.org/v1/).\n", 85 | "\n", 86 | "*__NOTE__* : From the Julia REPL, you can enter the Pkg REPL by pressing `]` (and return to the Julia REPL with backspace). This allows typing commands instead of functions. We will show both representations for the operations covered here. The function version (actual Julia code) can be executed.\n", 87 | "\n", 88 | "*__WARNING__* : This notebook is for reference. It will work if run linearly from the beginning. But if you start over within the same session, you will have problems.\n", 89 | "\n", 90 | "It might make more sense to perform the operations in a terminal and a text editor." 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "id": "9aa16c5b-0609-4d2c-8da3-0f513a6337d1", 96 | "metadata": {}, 97 | "source": [ 98 | "## Creating a package\n", 99 | "\n", 100 | "When you create a package, you also create a dependency environment specific to your project. We'll look at this step-by-step.\n" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "id": "53026cbe-a97d-4176-aa33-8daafc378877", 106 | "metadata": {}, 107 | "source": [ 108 | "### Generate a package (\"project\")\n", 109 | "```\n", 110 | "(@v1.11) pkg> generate MyPack\n", 111 | "```" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "id": "5bad3016-0f99-4774-ae95-59b553fdd1f4", 118 | "metadata": {}, 119 | "outputs": [], 120 | "source": [ 121 | "import Pkg\n", 122 | "Pkg.generate(\"MyPack\")" 123 | ] 124 | }, 125 | { 126 | "cell_type": "markdown", 127 | "id": "8616962c-ee8f-4fc0-bede-43aa0fcca90f", 128 | "metadata": {}, 129 | "source": [ 130 | "### Package status\n", 131 | "```\n", 132 | "(@v1.11) pkg> status\n", 133 | "```" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": null, 139 | "id": "7594fe08-88a1-42f8-acd8-6b0a616a9633", 140 | "metadata": {}, 141 | "outputs": [], 142 | "source": [ 143 | "Pkg.status()" 144 | ] 145 | }, 146 | { 147 | "cell_type": "markdown", 148 | "id": "62976cef-f384-4e93-86ae-e7099bf84ea7", 149 | "metadata": {}, 150 | "source": [ 151 | "Note that we are actually in the `julia-basics` environment. By default at the terminal you would start in the default Julia (@v1.11 when this was written) environment." 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "id": "cbfcba74-5cfc-41a3-9651-af2bfc2b2bce", 157 | "metadata": {}, 158 | "source": [ 159 | "### Activate package environment\n", 160 | "```\n", 161 | "(@v1.11) pkg> activate MyPack\n", 162 | " Activating project at ...\n", 163 | "(MyPack) pkg> status\n", 164 | "```" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "id": "45b0484b-0c1c-45d5-9b56-0c2e3fceed4e", 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "Pkg.activate(\"MyPack\")" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "id": "2d9e53a6-765e-4d5e-afe5-93c88911a93e", 181 | "metadata": {}, 182 | "outputs": [], 183 | "source": [ 184 | "Pkg.status()" 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "id": "933208b5-5792-4fc7-b867-5aec25efedd3", 190 | "metadata": {}, 191 | "source": [ 192 | "### Add dependencies\n", 193 | "\n", 194 | "Notice how the project environment is empty, that is, there are no packages available to use. To use packages within your environment, you must add them as dependencies.\n", 195 | "\n", 196 | "```\n", 197 | "(MyPack) pkg> add ArgParse YAML StaticArrays\n", 198 | "```" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "id": "ccc20cb0-2f59-4261-ab1a-7f55090a815f", 205 | "metadata": {}, 206 | "outputs": [], 207 | "source": [ 208 | "Pkg.add([\"ArgParse\", \"YAML\", \"StaticArrays\"])" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": null, 214 | "id": "2db0fa46-788e-42fc-938d-505eda0d25ce", 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "Pkg.status()" 219 | ] 220 | }, 221 | { 222 | "cell_type": "markdown", 223 | "id": "56d4f56b-18ae-4abb-a65d-81deeb05fa77", 224 | "metadata": {}, 225 | "source": [ 226 | "Project information is kept in the `Project.toml` file. Let's take a look." 227 | ] 228 | }, 229 | { 230 | "cell_type": "code", 231 | "execution_count": null, 232 | "id": "eb983dfa-b723-4c1b-a649-bbccd9313f33", 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "function catFile(fname::AbstractString)\n", 237 | " for line in readlines(fname)\n", 238 | " println(line)\n", 239 | " end\n", 240 | "end" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": null, 246 | "id": "fd65c798-00eb-422f-a337-084db7490061", 247 | "metadata": {}, 248 | "outputs": [], 249 | "source": [ 250 | "catFile(\"MyPack/Project.toml\")" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "id": "2b5f5e9c-774b-4b1e-b490-688d76f30f79", 256 | "metadata": {}, 257 | "source": [ 258 | "### Write some code\n", 259 | "\n", 260 | "The top-level module associated with a project has the same name as the package. This was already generated for you in `src/MyPack.jl`:" 261 | ] 262 | }, 263 | { 264 | "cell_type": "code", 265 | "execution_count": null, 266 | "id": "1172186e-e5a5-4bd4-8af0-e4d0156cc5eb", 267 | "metadata": {}, 268 | "outputs": [], 269 | "source": [ 270 | "catFile(\"MyPack/src/MyPack.jl\")" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": null, 276 | "id": "16250e11-8581-4579-8d38-2823ba7881b9", 277 | "metadata": {}, 278 | "outputs": [], 279 | "source": [ 280 | "import MyPack\n", 281 | "MyPack.greet()" 282 | ] 283 | }, 284 | { 285 | "cell_type": "markdown", 286 | "id": "017c1c5f-d97f-4f29-b4fd-89c781463675", 287 | "metadata": {}, 288 | "source": [ 289 | "Let's replace the code in `src/MyPack.jl` to do something different." 290 | ] 291 | }, 292 | { 293 | "cell_type": "code", 294 | "execution_count": null, 295 | "id": "196f060d-4813-4649-963a-bce7fb6c9735", 296 | "metadata": {}, 297 | "outputs": [], 298 | "source": [ 299 | "write(\"MyPack/src/MyPack.jl\", \"\"\"\n", 300 | " module MyPack\n", 301 | "\n", 302 | " function modifyArg(arg::AbstractString)\n", 303 | " return \"Hello \" * arg\n", 304 | " end\n", 305 | "\n", 306 | " function modifyArg(arg::Number)\n", 307 | " return arg + 5\n", 308 | " end\n", 309 | " \n", 310 | " end\n", 311 | " \"\"\")" 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": null, 317 | "id": "bd5fa370-9f13-4415-8393-fab968e9001f", 318 | "metadata": {}, 319 | "outputs": [], 320 | "source": [ 321 | "MyPack.modifyArg(\"William\")" 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "execution_count": null, 327 | "id": "aa85bb08-41b7-4630-9387-d0db24513573", 328 | "metadata": {}, 329 | "outputs": [], 330 | "source": [ 331 | "MyPack.modifyArg(23)" 332 | ] 333 | }, 334 | { 335 | "cell_type": "markdown", 336 | "id": "8d8cbe90-a826-4197-bb9c-115bedd14438", 337 | "metadata": {}, 338 | "source": [ 339 | "## Testing\n", 340 | "\n", 341 | "In order for your package to be testable by the `Pkg` system, you must follow a convention. You need a `test` directory containing a `Project.toml` for your testing environment and a `runtests.jl` script as the entry point for your tests.\n", 342 | "\n", 343 | "The first two requirements can be handled from `Pkg`:\n", 344 | "```\n", 345 | "(MyPack) pkg> activate test\n", 346 | "(test) pkg> add Test\n", 347 | "```\n", 348 | "Here we generate the testing environment (and directory) and add the `Test` package as a testing dependency." 349 | ] 350 | }, 351 | { 352 | "cell_type": "code", 353 | "execution_count": null, 354 | "id": "3a75d38b-e2ee-4cd5-ab5d-46489f9978db", 355 | "metadata": {}, 356 | "outputs": [], 357 | "source": [ 358 | "Pkg.activate(\"MyPack/test\")\n", 359 | "Pkg.add(\"Test\")" 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "execution_count": null, 365 | "id": "4369ddb8-35f9-4ef0-b57e-3f5becc1cde7", 366 | "metadata": {}, 367 | "outputs": [], 368 | "source": [ 369 | "catFile(\"MyPack/test/Project.toml\")" 370 | ] 371 | }, 372 | { 373 | "cell_type": "markdown", 374 | "id": "a6fccc51-b145-4210-a691-c2ddbfe9e855", 375 | "metadata": {}, 376 | "source": [ 377 | "The built-in `Test` package includes basic tools for testing. We'll write a quick test using `@testset`, `@test`, and `@test_throws`:" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": null, 383 | "id": "1871bcd8-c095-4a0a-97c3-977f82a7450d", 384 | "metadata": {}, 385 | "outputs": [], 386 | "source": [ 387 | "write(\"MyPack/test/runtests.jl\", \"\"\"\n", 388 | " import MyPack\n", 389 | " using Test\n", 390 | "\n", 391 | " @testset \"reportArg\" begin\n", 392 | " @test MyPack.modifyArg(42) == 47\n", 393 | " @test MyPack.modifyArg(\"Frank\") == \"Hello Frank\"\n", 394 | " \n", 395 | " @test_throws MethodError MyPack.modifyArg((1,2))\n", 396 | " end\n", 397 | " \"\"\")" 398 | ] 399 | }, 400 | { 401 | "cell_type": "markdown", 402 | "id": "1c241dbb-37bd-4157-a932-4c6ae1a76837", 403 | "metadata": {}, 404 | "source": [ 405 | "Let's reactivate the project environment (since the `test` is currently active) and run the tests\n", 406 | "```\n", 407 | "(test) pkg> activate MyPack\n", 408 | "(MyPack) pkg> test\n", 409 | "```" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": null, 415 | "id": "f31fde8f-61b1-49fc-8521-47f00cc32dd6", 416 | "metadata": {}, 417 | "outputs": [], 418 | "source": [ 419 | "Pkg.activate(\"MyPack\")" 420 | ] 421 | }, 422 | { 423 | "cell_type": "code", 424 | "execution_count": null, 425 | "id": "2c120403-276b-4342-bfd3-3d51ee541f06", 426 | "metadata": {}, 427 | "outputs": [], 428 | "source": [ 429 | "Pkg.test()" 430 | ] 431 | }, 432 | { 433 | "cell_type": "markdown", 434 | "id": "23f3151c-824b-49d2-8a22-f4ceeb33fcc5", 435 | "metadata": {}, 436 | "source": [ 437 | "`Pkg.test()` sets up a clean environment for testing (in a temp directory, separate from the project or test environment). It automatically has `MyPack` as a dependency and includes whatever is in `test/Project.toml` (in this case, just `Test`)." 438 | ] 439 | }, 440 | { 441 | "cell_type": "code", 442 | "execution_count": null, 443 | "id": "5ec73596-8811-41fa-9504-c7202a7e5359", 444 | "metadata": {}, 445 | "outputs": [], 446 | "source": [] 447 | } 448 | ], 449 | "metadata": { 450 | "kernelspec": { 451 | "display_name": "Julia 1.11.6", 452 | "language": "julia", 453 | "name": "julia-1.11" 454 | }, 455 | "language_info": { 456 | "file_extension": ".jl", 457 | "mimetype": "application/julia", 458 | "name": "julia", 459 | "version": "1.11.6" 460 | } 461 | }, 462 | "nbformat": 4, 463 | "nbformat_minor": 5 464 | } 465 | -------------------------------------------------------------------------------- /notebooks/02_Functions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Functions\n", 8 | "\n", 9 | "[Manual](https://docs.julialang.org/en/v1/manual/functions/)\n", 10 | "\n", 11 | "**Table of Contents**\n", 12 | " - [Defining Functions](#Defining-Functions)\n", 13 | " - [Void Functions](#Void-Functions)\n", 14 | " - [Anonymous Functions](#Anonymous-Functions)\n", 15 | " - [Optional and Keyword Arguments](#Optional-and-Keyword-Arguments)\n", 16 | " - [Documenting Functions](#Documenting-Functions)\n", 17 | " - [Conclusion](#Conclusion)\n", 18 | " \n", 19 | "Functions are the main building blocks in Julia. Every operation on variables and other elements is performed through functions, even the mathematical operators (e.g. `+`) are functions in an infix form.\n", 20 | "\n", 21 | "\n", 22 | "## Defining Functions\n", 23 | "\n", 24 | "A typical function looks like this: **NOTE: Julia doesn't enforce indentation as Python**" 25 | ] 26 | }, 27 | { 28 | "cell_type": "code", 29 | "execution_count": null, 30 | "metadata": {}, 31 | "outputs": [], 32 | "source": [ 33 | "function plusTwo(x)\n", 34 | " return x + 2\n", 35 | "end\n", 36 | "\n", 37 | "println( \"5 + 2 = \", plusTwo(5) )\n", 38 | "println( \"1 + 2 = \", plusTwo(1) )\n", 39 | "\n", 40 | "# we can specify types\n", 41 | "function plusTwo(x::Int32)::Int32\n", 42 | " return x+2\n", 43 | "end\n", 44 | "\n", 45 | "println( \"5 + 2 = \", plusTwo(5) )\n", 46 | "println( \"1 + 2 = \", plusTwo(1) )" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "Although the previous is the most common way to write functions, it is sometimes convenient to use the shorthand version (think of the REPL):" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": null, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "plusTwo(x) = x+2\n", 63 | "\n", 64 | "println( \"2 + 2 = \", plusTwo(2) )\n", 65 | "println( \"3 + 2 = \", plusTwo(3) )\n", 66 | "\n", 67 | "# we can constrain types\n", 68 | "plusTwo(x::Real) = x+2\n", 69 | "\n", 70 | "println( \"2 + 2 = \", plusTwo(2) )\n", 71 | "println( \"3 + 2 = \", plusTwo(3) )\n" 72 | ] 73 | }, 74 | { 75 | "cell_type": "markdown", 76 | "metadata": {}, 77 | "source": [ 78 | "A few important things:\n", 79 | "- Functions defined with the keyword **function** are finalized with **end**.\n", 80 | "- Functions always return the result of their last expression. It's generally best to have an explicit return statement.\n", 81 | "- Julia **doesn't have member functions** ( foo.bar() ), functions can be scoped with modules (think Fortran).\n", 82 | "- Function arguments and return types are optional, but highly recommended\n", 83 | "- Function arguments can limit types using Abstract* types (see example below)\n", 84 | "- Functions can have multiple signatures (overloaded), it's the basis for multiple dispatch\n", 85 | "- Function arguments are *\"passed by reference\"*. Just like in Fortran, single values won't be modified, but array contents will.\n", 86 | "- It's a convention that if a function modifies its arguments it ends with `!`\n", 87 | "- The dot (`.`) operator is syntactic sugar for element by element operations (vectorization), see [article](https://julialang.org/blog/2017/01/moredots/). It's also called broadcasting." 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "# modifies v\n", 97 | "function add!(v::AbstractArray, c)\n", 98 | " if size(v,1) >= 1 # bounds check\n", 99 | " v[1] = v[1] + c\n", 100 | " end\n", 101 | " return\n", 102 | "end\n", 103 | "\n", 104 | "function run()\n", 105 | " v = [1,2,3]\n", 106 | " add!(v,2)\n", 107 | " println(\"v is now: \", v) \n", 108 | " return\n", 109 | "end\n", 110 | "\n", 111 | "run()\n", 112 | "\n", 113 | "\n", 114 | "function add!(v::AbstractArray, c)\n", 115 | " # dot operates element-by-element (think of dot product)\n", 116 | " # must be next to operator (=, +)\n", 117 | " v .= v .+ c\n", 118 | " # could also be\n", 119 | " # v .+= c\n", 120 | "end\n", 121 | "\n", 122 | "function run()\n", 123 | " v = [1,2,3]\n", 124 | " add!(v,1) \n", 125 | " println(\"v is now: \", v) \n", 126 | " return\n", 127 | "end\n", 128 | "\n", 129 | "run()\n" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "## Void Functions\n", 137 | "\n", 138 | "Julia functions always return a value. `void` functions (`subroutines` in Fortran), those that don't return anything, don't truly exist. Instead, they can return `nothing` an instance of `Nothing`. In this case the REPL won't print the return value." 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": null, 144 | "metadata": {}, 145 | "outputs": [], 146 | "source": [ 147 | "function say_hi()\n", 148 | " println(\"Hello from ORNL!\")\n", 149 | " return\n", 150 | "end\n", 151 | "\n", 152 | "say_hi()\n", 153 | "println(\"return type: \", typeof(say_hi()))" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "If the function returns no value `return` can be omitted. In general, ~it is also possible to omit the return statement even in regular functions and Julia will return the last computed value~. **NOTE: bad idea**. However, in my opinion, in this case it is better to return explicitly a value, for clarity’s sake." 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "function say_hi()\n", 170 | " println(\"Hello from ORNL without return!\")\n", 171 | "end\n", 172 | "\n", 173 | "say_hi()\n", 174 | "@show typeof(say_hi())" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "metadata": {}, 180 | "source": [ 181 | "## Anonymous Functions\n", 182 | "\n", 183 | "It is also possible to create anonymous functions (like lambdas in Python or C++) using the following structure:" 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "execution_count": null, 189 | "metadata": {}, 190 | "outputs": [], 191 | "source": [ 192 | "plusThree = (x, y) -> x + y +3\n", 193 | "\n", 194 | "println( \"5 + 5 + 3 = \", plusThree(5, 5) )" 195 | ] 196 | }, 197 | { 198 | "cell_type": "markdown", 199 | "metadata": {}, 200 | "source": [ 201 | "**NOTE: It is not recommended to use anonymous functions unless they are really simple.** It is generally better to write functions using the `function/end` keywords unless you need to write a small wrapper around another function and pass it to a third function. (Think of lambda functions in C++ and Python)\n", 202 | "\n", 203 | "```\n", 204 | "The following example is a bit more advanced and it includes topics which we have not yet studied, like integrals and how to install and use an external package. You can always come back later to read this example as it is not fundamental at this point of the course but you may find it useful one day. The take home message is never use anonymous functions unless you know what you are doing.\n", 205 | "```\n", 206 | "\n", 207 | "For example, let’s consider a function `f` of three variables `x`, `y` and `z`. Let’s suppose we want to fix two variables (y and z) and integrate f over x, we could do it with:" 208 | ] 209 | }, 210 | { 211 | "cell_type": "code", 212 | "execution_count": null, 213 | "metadata": {}, 214 | "outputs": [], 215 | "source": [ 216 | "import QuadGK\n", 217 | "\n", 218 | "f(x,y,z) = (x^2 + 2y)*z\n", 219 | "\n", 220 | "result = QuadGK.quadgk(x->f(x,42,4), 3, 4)\n", 221 | "println(\"Integration and error usgin Gauss quadrature: \", result)" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | "or use an intermediate function" 229 | ] 230 | }, 231 | { 232 | "cell_type": "code", 233 | "execution_count": null, 234 | "metadata": {}, 235 | "outputs": [], 236 | "source": [ 237 | "f(x,y,z) = (x^2 + 2y)*z\n", 238 | "g(x) = f(x,42,4)\n", 239 | "\n", 240 | "result = QuadGK.quadgk(g, 3, 4)\n", 241 | "println(\"Integration and error using Gauss quadrature: \", result)" 242 | ] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": {}, 247 | "source": [ 248 | "## Optional and Keyword Arguments\n", 249 | "\n", 250 | "### Optional arguments\n", 251 | "Sometimes a parameter may have a default value which can be specified so that the user doesn’t need to always type it. For example let’s write a function which converts our “weight” as measured on Earth (in kg) to the one measured on another planet." 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": null, 257 | "metadata": {}, 258 | "outputs": [], 259 | "source": [ 260 | "function myWeight(weightOnEarth, g=9.81)\n", 261 | " return weightOnEarth*g/9.81\n", 262 | "end\n", 263 | "\n", 264 | "println( \"My weight on Earth: \", myWeight(90), \" kg\" )\n", 265 | "println( \"My weight on Mars: \", myWeight(90, 3.721), \" kg\" )" 266 | ] 267 | }, 268 | { 269 | "cell_type": "markdown", 270 | "metadata": {}, 271 | "source": [ 272 | "As the name suggests positional arguments must be used in the right order, we cannot specify g before `weightOnEarth` and, as opposed to other languages like Python, in Julia we cannot change the order of the arguments even if we specify the name of the parameter. If we want optional arguments with no fixed position, we need to use **keyword arguments**." 273 | ] 274 | }, 275 | { 276 | "cell_type": "markdown", 277 | "metadata": {}, 278 | "source": [ 279 | "### Keyword arguments \n", 280 | "Keyword arguments are separated from positional arguments by a semicolon ; and must always be addressed by their name, although their order is irrelevant. They can be either optional and not, but usually we use keyword arguments for optional parameters." 281 | ] 282 | }, 283 | { 284 | "cell_type": "code", 285 | "execution_count": null, 286 | "metadata": {}, 287 | "outputs": [], 288 | "source": [ 289 | "# positional arguments ; keyword arguments\n", 290 | "function my_long_function(a, b=2; c, d=3)\n", 291 | " return a + b + c + d\n", 292 | "end" 293 | ] 294 | }, 295 | { 296 | "cell_type": "markdown", 297 | "metadata": {}, 298 | "source": [ 299 | "Here `a` and `b` are positional arguments, while `c` and `d` are keyword arguments. Keyword arguments must always be specified either by default or explicitly." 300 | ] 301 | }, 302 | { 303 | "cell_type": "code", 304 | "execution_count": null, 305 | "metadata": {}, 306 | "outputs": [], 307 | "source": [ 308 | "# Usage Examples:\n", 309 | "\n", 310 | "# a=1, b=2 (default), c=3, d=3 (default)\n", 311 | "println( my_long_function(1, c=3) )\n", 312 | "\n", 313 | "# a=1, b=2, c=3, d=3 (default)\n", 314 | "println( my_long_function(1, 2, c=3) )\n", 315 | "\n", 316 | "# a=1, b=2, c=3, d=5, order doesn't matter for keyword arguments\n", 317 | "println( my_long_function(1, 2, d=5, c=3) )" 318 | ] 319 | }, 320 | { 321 | "cell_type": "code", 322 | "execution_count": null, 323 | "metadata": {}, 324 | "outputs": [], 325 | "source": [ 326 | "# Error, c is not defined, Keyword argument must be defined\n", 327 | "my_long_function(1, 2, d=5)" 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "metadata": {}, 333 | "source": [ 334 | "**Tip**: prefer positional arguments for performance (function called several times) as it's closer to underlying libraries (C, Fortran, C++)" 335 | ] 336 | }, 337 | { 338 | "cell_type": "markdown", 339 | "metadata": {}, 340 | "source": [ 341 | "## Documenting Functions\n", 342 | "\n", 343 | "Julia supports function API documentation using Markdown, similar to Python docstring. Function documentation is done above a function using the \"\"\" \"\"\" opening and closing symbols. While the macro `@doc` (or the `?function_name` in the REPL) exposes the documentation information if available." 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": null, 349 | "metadata": {}, 350 | "outputs": [], 351 | "source": [ 352 | "# Example: show built-in sin documentation\n", 353 | "@doc sin" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": null, 359 | "metadata": {}, 360 | "outputs": [], 361 | "source": [ 362 | "\"\"\"\n", 363 | "add(x,y)\n", 364 | "\n", 365 | "Compute the sum of 2 numbers `x + y`\n", 366 | " \n", 367 | "# Arguments:\n", 368 | "- x: input any number\n", 369 | "- y: input any number\n", 370 | "\n", 371 | "# Examples\n", 372 | "```\n", 373 | "julia> add(2,4)\n", 374 | "6\n", 375 | "```\n", 376 | "\"\"\"\n", 377 | "function add(x,y)\n", 378 | " return x+y\n", 379 | "end\n", 380 | "\n", 381 | "@doc add" 382 | ] 383 | }, 384 | { 385 | "cell_type": "markdown", 386 | "metadata": {}, 387 | "source": [ 388 | "## Opening Function Source Code\n", 389 | "\n", 390 | "You can open the source code of a julia function with `@edit`:" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": null, 396 | "metadata": {}, 397 | "outputs": [], 398 | "source": [ 399 | "@edit 5 + 2" 400 | ] 401 | }, 402 | { 403 | "cell_type": "markdown", 404 | "metadata": {}, 405 | "source": [ 406 | "## Conclusion\n", 407 | "\n", 408 | "In this lesson we learned how to define functions and use positional as well as keyword arguments.\n", 409 | "\n", 410 | "**Questions:**\n", 411 | "\n", 412 | "1. How many way exist to define a function in Julia?\n", 413 | "2. Can functions be member functions in Julia?\n", 414 | "3. What's the difference between Optional and Keyword arguments?\n", 415 | "4. How do you document your function API in Julia?" 416 | ] 417 | }, 418 | { 419 | "cell_type": "code", 420 | "execution_count": null, 421 | "metadata": {}, 422 | "outputs": [], 423 | "source": [] 424 | } 425 | ], 426 | "metadata": { 427 | "kernelspec": { 428 | "display_name": "Julia 1.11.6", 429 | "language": "julia", 430 | "name": "julia-1.11" 431 | }, 432 | "language_info": { 433 | "file_extension": ".jl", 434 | "mimetype": "application/julia", 435 | "name": "julia", 436 | "version": "1.11.6" 437 | } 438 | }, 439 | "nbformat": 4, 440 | "nbformat_minor": 4 441 | } 442 | -------------------------------------------------------------------------------- /data/plot_size_case4.csv: -------------------------------------------------------------------------------- 1 | plots_cells,plots_size,plots_size_L0,plots_size_L1,plots_size_L2,caseID,caseDir 2 | 262144,7.3006443e7,7.1357875e7,633563.0,1.004312e6,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00000 3 | 524288,1.46129626e8,1.4271578e8,1.267155e6,2.12535e6,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00010 4 | 786432,2.19252805e8,2.14073685e8,1.900747e6,3.246385e6,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00020 5 | 1048576,2.92375985e8,2.85431586e8,2.534339e6,4.367422e6,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00030 6 | 1310720,3.65499165e8,3.56789487e8,3.167931e6,5.488459e6,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00040 7 | 1572864,4.39257949e8,4.28147388e8,3.801524e6,7.244805e6,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00050 8 | 1835008,5.1301672e8,4.99505289e8,4.435117e6,9.001147e6,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00060 9 | 2097152,5.88028779e8,5.70863194e8,5.556132e6,1.1523442e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00070 10 | 2359296,6.63958462e8,6.42221099e8,6.677147e6,1.4963015e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00080 11 | 2621440,7.39888123e8,7.13579004e8,7.798158e6,1.8402574e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00090 12 | 2883584,8.17497609e8,7.84936911e8,9.55446e6,2.288662e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00100 13 | 3145728,8.96306961e8,8.56294816e8,1.1310761e7,2.8569915e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00110 14 | 3407872,9.75116309e8,9.27652721e8,1.3067063e7,3.4253205e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00120 15 | 3670016,1.056014476e9,9.99010626e8,1.5589307e7,4.1259508e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00130 16 | 3932160,1.138394413e9,1.070368531e9,1.8111544e7,4.9747008e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00140 17 | 4194304,1.223292986e9,1.141726436e9,2.155103e7,5.9836033e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00150 18 | 4456448,1.308191549e9,1.213084341e9,2.4990519e7,6.9925049e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00160 19 | 4718592,1.394854419e9,1.284442246e9,2.8430007e7,8.1777341e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00170 20 | 4980736,1.481517289e9,1.355800151e9,3.1869489e7,9.3629641e7,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00180 21 | 5242880,1.571104297e9,1.427158056e9,3.6353442e7,1.07362012e8,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00190 22 | 5505024,1.662680729e9,1.498515961e9,4.0837399e7,1.23084358e8,case4_cfl3_maxl2,sedov_2d_cyl_in_cart_plt00200 23 | 262144,7.564582e7,7.1357875e7,633563.0,1.121009e6,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00000 24 | 524288,1.51291876e8,1.4271578e8,1.267155e6,2.242048e6,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00010 25 | 786432,2.26937927e8,2.14073685e8,1.900747e6,3.363083e6,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00020 26 | 1048576,3.03501673e8,2.8543159e8,2.534339e6,4.484118e6,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00030 27 | 1310720,3.80065419e8,3.56789495e8,3.167931e6,5.605153e6,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00040 28 | 1572864,4.58308994e8,4.281474e8,3.801524e6,7.361504e6,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00050 29 | 1835008,5.37752524e8,4.99505305e8,4.435117e6,9.117855e6,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00060 30 | 2097152,6.21254138e8,5.7086321e8,5.556132e6,1.1640159e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00070 31 | 2359296,7.07274412e8,6.42221115e8,6.677147e6,1.5079727e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00080 32 | 2621440,7.95059071e8,7.1357902e8,7.798162e6,1.8519296e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00090 33 | 2883584,8.88392721e8,7.84936927e8,9.554464e6,2.3003336e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00100 34 | 3145728,9.85084317e8,8.56294832e8,1.1310759e7,2.8686633e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00110 35 | 3407872,1.084097344e9,9.27652737e8,1.3067054e7,3.4369916e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00120 36 | 3670016,1.190196724e9,9.99010642e8,1.5589292e7,4.1376196e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00130 37 | 3932160,1.303371526e9,1.070368547e9,1.811153e7,4.9863691e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00140 38 | 4194304,1.42203588e9,1.141726452e9,2.155101e7,5.995269e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00150 39 | 4456448,1.543856956e9,1.213084357e9,2.4990493e7,7.0041697e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00160 40 | 4718592,1.670691284e9,1.284442262e9,2.8429977e7,8.1893982e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00170 41 | 4980736,1.800961246e9,1.355800167e9,3.1869445e7,9.3746248e7,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00180 42 | 5242880,1.941366925e9,1.427158072e9,3.6353394e7,1.07478595e8,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00190 43 | 5505024,2.087621645e9,1.498515977e9,4.0837347e7,1.23200938e8,case4_cfl3_maxl4,sedov_2d_cyl_in_cart_plt00200 44 | 262144,7.3006438e7,7.1357875e7,633563.0,1.004312e6,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00000 45 | 524288,1.46129613e8,1.4271578e8,1.267155e6,2.125347e6,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00010 46 | 786432,2.19252783e8,2.14073685e8,1.900743e6,3.246382e6,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00020 47 | 1048576,2.92375955e8,2.8543159e8,2.534331e6,4.367417e6,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00030 48 | 1310720,3.65499118e8,3.56789491e8,3.167919e6,5.488447e6,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00040 49 | 1572864,4.39257886e8,4.28147396e8,3.801509e6,7.244782e6,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00050 50 | 1835008,5.14269929e8,4.99505301e8,4.922524e6,9.767071e6,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00060 51 | 2097152,5.90199596e8,5.70863206e8,6.043539e6,1.3206639e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00070 52 | 2359296,6.66129256e8,6.42221111e8,7.16455e6,1.6646199e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00080 53 | 2621440,7.44938595e8,7.13579016e8,8.920845e6,2.2329494e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00090 54 | 2883584,8.25836761e8,7.84936921e8,1.144309e7,2.9335801e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00100 55 | 3145728,9.08216687e8,8.56294826e8,1.3965321e7,3.7823301e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00110 56 | 3407872,9.90596608e8,9.27652731e8,1.6487551e7,4.6310799e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00120 57 | 3670016,1.075495135e9,9.99010636e8,1.9927028e7,5.6399796e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00130 58 | 3932160,1.162158009e9,1.070368541e9,2.3366506e7,6.8252108e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00140 59 | 4194304,1.251744981e9,1.141726446e9,2.7850461e7,8.1984446e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00150 60 | 4456448,1.3433214e9,1.213084351e9,3.2334414e7,9.7706789e7,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00160 61 | 4718592,1.434897822e9,1.284442258e9,3.6818376e7,1.1342912e8,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00170 62 | 4980736,1.529832095e9,1.355800163e9,4.2501521e7,1.31310026e8,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00180 63 | 5242880,1.627087735e9,1.427158068e9,4.8184663e7,1.51511257e8,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00190 64 | 5505024,1.728103211e9,1.498515973e9,5.5190814e7,1.74149597e8,case4_cfl4_maxl2,sedov_2d_cyl_in_cart_plt00200 65 | 262144,7.564582e7,7.1357875e7,633563.0,1.121009e6,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00000 66 | 524288,1.51291872e8,1.4271578e8,1.267155e6,2.242044e6,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00010 67 | 786432,2.26937911e8,2.14073685e8,1.900743e6,3.363079e6,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00020 68 | 1048576,3.03501627e8,2.8543159e8,2.534331e6,4.484114e6,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00030 69 | 1310720,3.80065345e8,3.56789495e8,3.167919e6,5.605149e6,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00040 70 | 1572864,4.59508843e8,4.281474e8,3.801508e6,7.361486e6,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00050 71 | 1835008,5.41528523e8,4.99505305e8,4.922523e6,9.883787e6,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00060 72 | 2097152,6.27548771e8,5.7086321e8,6.043538e6,1.3323351e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00070 73 | 2359296,7.18893075e8,6.42221115e8,7.799837e6,1.7807388e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00080 74 | 2621440,8.12226707e8,7.13579022e8,9.556132e6,2.2291425e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00090 75 | 2883584,9.11239763e8,7.84936927e8,1.1312427e7,2.7974719e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00100 76 | 3145728,1.017339156e9,8.56294832e8,1.3834671e7,3.4981009e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00110 77 | 3407872,1.130513955e9,9.27652737e8,1.6356909e7,4.3468513e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00120 78 | 3670016,1.252335026e9,9.99010642e8,1.9796391e7,5.3557512e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00130 79 | 3932160,1.379169371e9,1.070368547e9,2.3235867e7,6.5409807e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00140 80 | 4194304,1.515914553e9,1.141726452e9,2.7719816e7,7.9142142e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00150 81 | 4456448,1.656320232e9,1.213084357e9,3.2203773e7,9.2874484e7,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00160 82 | 4718592,1.806514128e9,1.284442262e9,3.6687726e7,1.08596822e8,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00170 83 | 4980736,1.968421727e9,1.355800169e9,4.2370863e7,1.26477745e8,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00180 84 | 5242880,2.137036881e9,1.427158074e9,4.8054002e7,1.46678981e8,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00190 85 | 5505024,2.310185854e9,1.498515979e9,5.3737142e7,1.6688021e8,case4_cfl4_maxl4,sedov_2d_cyl_in_cart_plt00200 86 | 262144,7.3006444e7,7.1357875e7,633563.0,1.004312e6,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00000 87 | 524288,1.4612962e8,1.4271578e8,1.267151e6,2.125347e6,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00010 88 | 786432,2.19252797e8,2.14073685e8,1.90074e6,3.246382e6,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00020 89 | 1048576,2.92375972e8,2.8543159e8,2.534329e6,4.367413e6,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00030 90 | 1310720,3.65499138e8,3.56789495e8,3.167917e6,5.488436e6,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00040 91 | 1572864,4.39257928e8,4.281474e8,3.801506e6,7.244788e6,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00050 92 | 1835008,5.14269996e8,4.99505305e8,4.922521e6,9.767092e6,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00060 93 | 2097152,5.90199663e8,5.7086321e8,6.043536e6,1.3206653e7,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00070 94 | 2359296,6.69009004e8,6.42221115e8,7.799832e6,1.8889944e7,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00080 95 | 2621440,7.49907145e8,7.1357902e8,1.0322077e7,2.5896223e7,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00090 96 | 2883584,8.32287098e8,7.84936925e8,1.2844321e7,3.4383743e7,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00100 97 | 3145728,9.17185654e8,8.5629483e8,1.6283802e7,4.4472756e7,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00110 98 | 3407872,1.003848496e9,9.27652735e8,1.9723267e7,5.6325043e7,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00120 99 | 3670016,1.093435479e9,9.9901064e8,2.4207223e7,7.0057384e7,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00130 100 | 3932160,1.1850119e9,1.070368545e9,2.869117e7,8.5779724e7,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00140 101 | 4194304,1.279946184e9,1.14172645e9,3.4374312e7,1.03660636e8,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00150 102 | 4456448,1.377201817e9,1.213084355e9,4.0057451e7,1.23861856e8,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00160 103 | 4718592,1.478217291e9,1.28444226e9,4.7063598e7,1.46500192e8,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00170 104 | 4980736,1.581793417e9,1.355800165e9,5.4069745e7,1.71699393e8,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00180 105 | 5242880,1.685369522e9,1.42715807e9,6.1075884e7,1.96898581e8,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00190 106 | 5505024,1.793167235e9,1.498515975e9,6.9563235e7,2.24837519e8,case4_cfl5_maxl2,sedov_2d_cyl_in_cart_plt00200 107 | 262144,7.564582e7,7.1357875e7,633563.0,1.121009e6,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00000 108 | 524288,1.51291875e8,1.4271578e8,1.267151e6,2.242044e6,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00010 109 | 786432,2.2693793e8,2.14073685e8,1.900739e6,3.363079e6,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00020 110 | 1048576,3.03501667e8,2.8543159e8,2.534328e6,4.484114e6,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00030 111 | 1310720,3.81745236e8,3.56789495e8,3.167916e6,6.240464e6,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00040 112 | 1572864,4.61188738e8,4.281474e8,3.801505e6,7.996807e6,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00050 113 | 1835008,5.44690336e8,4.99505305e8,4.92252e6,1.0519105e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00060 114 | 2097152,6.32474995e8,5.7086321e8,6.043535e6,1.3958659e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00070 115 | 2359296,7.25808652e8,6.42221115e8,7.799836e6,1.8442706e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00080 116 | 2621440,8.29347469e8,7.1357902e8,1.0322081e7,2.5449026e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00090 117 | 2883584,9.39668627e8,7.84936925e8,1.2844325e7,3.393655e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00100 118 | 3145728,1.058333017e9,8.5629483e8,1.6283803e7,4.4025583e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00110 119 | 3407872,1.185167389e9,9.27652735e8,1.9723288e7,5.5877886e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00120 120 | 3670016,1.321912567e9,9.9901064e8,2.4207249e7,6.9610233e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00130 121 | 3932160,1.468167239e9,1.070368545e9,2.8691198e7,8.533257e7,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00140 122 | 4194304,1.625827145e9,1.141726452e9,3.4374324e7,1.03213487e8,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00150 123 | 4456448,1.78773472e9,1.213084357e9,4.0057457e7,1.21094397e8,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00160 124 | 4718592,1.960883685e9,1.284442262e9,4.5740596e7,1.41295623e8,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00170 125 | 4980736,2.147262452e9,1.355800167e9,5.2746754e7,1.63933961e8,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00180 126 | 5242880,2.346228859e9,1.427158072e9,5.9752902e7,1.89133167e8,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00190 127 | 5505024,2.55463911e9,1.498515977e9,6.824025e7,2.17072118e8,case4_cfl5_maxl4,sedov_2d_cyl_in_cart_plt00200 128 | 262144,7.3006444e7,7.1357875e7,633563.0,1.004312e6,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00000 129 | 524288,1.46129628e8,1.4271578e8,1.267155e6,2.125351e6,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00010 130 | 786432,2.19252808e8,2.14073685e8,1.900747e6,3.246386e6,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00020 131 | 1048576,2.92375976e8,2.85431586e8,2.534339e6,4.367413e6,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00030 132 | 1310720,3.65993716e8,3.56789491e8,3.167932e6,5.982776e6,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00040 133 | 1572864,4.39752498e8,4.28147396e8,3.801525e6,7.739116e6,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00050 134 | 1835008,5.15682145e8,4.99505301e8,4.922536e6,1.1178658e7,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00060 135 | 2097152,5.93291607e8,5.70863208e8,6.678838e6,1.5662681e7,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00070 136 | 2359296,6.74189766e8,6.42221113e8,9.201084e6,2.2668979e7,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00080 137 | 2621440,7.56569711e8,7.13579018e8,1.1723323e7,3.1156486e7,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00090 138 | 2883584,8.41468248e8,7.84936923e8,1.5162807e7,4.1245478e7,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00100 139 | 3145728,9.31055227e8,8.56294828e8,1.9646763e7,5.4977816e7,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00110 140 | 3407872,1.02263166e9,9.27652733e8,2.4130718e7,7.0700161e7,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00120 141 | 3670016,1.117565936e9,9.99010638e8,2.9813851e7,8.8581075e7,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00130 142 | 3932160,1.214821566e9,1.070368543e9,3.5496998e7,1.08782284e8,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00140 143 | 4194304,1.315837061e9,1.141726448e9,4.2503158e7,1.31420631e8,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00150 144 | 4456448,1.419413198e9,1.213084353e9,4.9509315e7,1.56619837e8,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00160 145 | 4718592,1.527210909e9,1.284442258e9,5.7996654e7,1.84558788e8,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00170 146 | 4980736,1.637862335e9,1.355800163e9,6.6483992e7,2.15350987e8,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00180 147 | 5242880,1.753085848e9,1.427158068e9,7.657285e7,2.49114903e8,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00190 148 | 5505024,1.871465964e9,1.498515973e9,8.6661713e7,2.86034699e8,case4_cfl6_maxl2,sedov_2d_cyl_in_cart_plt00200 149 | 262144,7.564582e7,7.1357875e7,633563.0,1.121009e6,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00000 150 | 524288,1.51291875e8,1.4271578e8,1.267151e6,2.242044e6,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00010 151 | 786432,2.2693793e8,2.14073685e8,1.900739e6,3.363079e6,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00020 152 | 1048576,3.03501668e8,2.8543159e8,2.534328e6,4.484114e6,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00030 153 | 1310720,3.81745236e8,3.56789495e8,3.167916e6,6.240464e6,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00040 154 | 1572864,4.61188737e8,4.281474e8,3.801505e6,7.996807e6,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00050 155 | 1835008,5.44690335e8,4.99505305e8,4.92252e6,1.0519105e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00060 156 | 2097152,6.32474994e8,5.7086321e8,6.043535e6,1.3958659e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00070 157 | 2359296,7.25808652e8,6.42221115e8,7.799836e6,1.8442706e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00080 158 | 2621440,8.2934747e8,7.1357902e8,1.0322081e7,2.5449026e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00090 159 | 2883584,9.39668632e8,7.84936925e8,1.2844325e7,3.393655e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00100 160 | 3145728,1.058333023e9,8.5629483e8,1.6283803e7,4.4025583e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00110 161 | 3407872,1.185167395e9,9.27652735e8,1.9723288e7,5.5877886e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00120 162 | 3670016,1.321912575e9,9.9901064e8,2.4207249e7,6.9610233e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00130 163 | 3932160,1.468167248e9,1.070368545e9,2.8691198e7,8.533257e7,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00140 164 | 4194304,1.625827155e9,1.141726452e9,3.4374324e7,1.03213487e8,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00150 165 | 4456448,1.787734729e9,1.213084357e9,4.0057457e7,1.21094397e8,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00160 166 | 4718592,1.960883693e9,1.284442262e9,4.5740596e7,1.41295623e8,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00170 167 | 4980736,2.147262459e9,1.355800167e9,5.2746754e7,1.63933961e8,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00180 168 | 5242880,2.346228865e9,1.427158072e9,5.9752902e7,1.89133167e8,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00190 169 | 5505024,2.554639116e9,1.498515977e9,6.824025e7,2.17072118e8,case4_cfl6_maxl4,sedov_2d_cyl_in_cart_plt00200 170 | -------------------------------------------------------------------------------- /notebooks/03_Data-Structures.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Data Structures\n", 8 | "\n", 9 | "In this lesson we will study how data can be collected and stored in memory. In particular we will deal with vectors, matrices, n-dimensional arrays, tuples and dictionaries.\n", 10 | "\n", 11 | "Contents:\n", 12 | "- [Arrays](##Arrays)\n", 13 | "- [Tuples](##Tuples)\n", 14 | "- [Dictionaries](##Dictionaries)\n", 15 | "- [Conclusion](##Conclusion)" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "## Arrays\n", 23 | "\n", 24 | "In this section we describe the out-of-the-box support for multidimensional arrays.\n", 25 | "In general, Julia follows a model that is very similar to Fortran arrays, while there are high-level functionality that resembles Python's numpy.\n", 26 | "\n", 27 | "A few things:\n", 28 | "\n", 29 | "- Julia uses 1-based index \n", 30 | "- Arrays are memory contiguous and column-based (first index changes the fastest).\n", 31 | "- Array must be of the same type (otherwise, they are tuples).\n", 32 | "\n", 33 | "### 1D Vector\n", 34 | "A vector is a list of ordered data which share a common type (be it `Int`, `Float`, `String`, or `Any`). Furthermore a vector is a one-dimensional array, and often “vector” and “array” are used a synonyms. Contrarily to the mathematical definition of a vector, in programming a vector is simply a list of values and has no a priori geometrical meaning.\n", 35 | "\n", 36 | "In Julia, to create a vector we use the following syntax:\n" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": null, 42 | "metadata": {}, 43 | "outputs": [], 44 | "source": [ 45 | "function vectorInit()\n", 46 | " a = [1,2,3,4,5]\n", 47 | " b = Float32[1.2, 3,4,5] # typed\n", 48 | " c::Array{String,1} = [\"Hello\", \"it's me\", \"Philip\"]\n", 49 | " println(a, \" type: \", typeof(a))\n", 50 | " println(b, \" type: \", typeof(b))\n", 51 | " println(c, \" type: \", typeof(c))\n", 52 | " println(\"\")\n", 53 | " \n", 54 | " println(first(a), \" is a's first element\")\n", 55 | " println(last(a), \" is a's last element\")\n", 56 | " println(\"\")\n", 57 | " \n", 58 | " println(first(c), \" is c's first element\")\n", 59 | " println(last(c), \" is c's last element\")\n", 60 | "end\n", 61 | "\n", 62 | "vectorInit()" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "We can access the members of an array using the indexing syntax: `array_name[index]`, for example, if we want to retrieve the third element of c we type `c[3]`. Contrary to other programming languages, in Julia **indices start at 1**, there is not much to say, it is just like that." 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "function vectorIndexAccess()\n", 79 | " a = [1,2,3,4,5]\n", 80 | " println(\"a first: \", a[1], \" ; a last: \", a[5] )\n", 81 | "end\n", 82 | "\n", 83 | "vectorIndexAccess()" 84 | ] 85 | }, 86 | { 87 | "cell_type": "markdown", 88 | "metadata": {}, 89 | "source": [ 90 | "Common functionality, see [here](https://julia.school/julia/arrays/) for more examples:" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": null, 96 | "metadata": {}, 97 | "outputs": [], 98 | "source": [ 99 | "function vectorFun()\n", 100 | " a = [1,2,3,4,5]\n", 101 | " println(a, \" type: \", typeof(a))\n", 102 | " \n", 103 | " append!(a, [6, 7])\n", 104 | " println(\"add item to the end: \", a )\n", 105 | " \n", 106 | " pop!(a)\n", 107 | " println(\"remove last item: \", a )\n", 108 | " \n", 109 | " popfirst!(a)\n", 110 | " println(\"remove first item: \", a )\n", 111 | " \n", 112 | " insert!(a, 1, 10)\n", 113 | " println(\"insert: \", a )\n", 114 | " insert!(a, 2, 10)\n", 115 | " println(\"insert: \", a )\n", 116 | " insert!(a, 3, 10)\n", 117 | " println(\"insert: \", a )\n", 118 | " insert!(a, 4, 10)\n", 119 | " println(\"insert: \", a )\n", 120 | " insert!(a, 5, 10)\n", 121 | " println(\"insert: \", a )\n", 122 | " \n", 123 | "end\n", 124 | "\n", 125 | "vectorFun()" 126 | ] 127 | }, 128 | { 129 | "cell_type": "markdown", 130 | "metadata": {}, 131 | "source": [ 132 | "### 2D Matrix\n", 133 | "\n", 134 | "2D arrays is how matrices are defined in Julia. The `;` character is used to separate **COLUMNS** (like Fortran, Matlab and R, Julia is column-major, first index is the fastest). Access can be done using the syntax: \n", 135 | "- `A[column,row]`" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": null, 141 | "metadata": {}, 142 | "outputs": [], 143 | "source": [ 144 | "function matrixFun()\n", 145 | " A = [1 2 3; 4 5 6; 8 9 10]\n", 146 | " @show size(A)\n", 147 | " @show size(A, 2)\n", 148 | " @show length(A)\n", 149 | " println(\"A:\", A, \" typeof(A): \", typeof(A))\n", 150 | " println(\"1st row, 2nd column: \", A[1,2])\n", 151 | " println(\"slice 3rd row, all columns: \", A[3,:]) # slow slicing\n", 152 | " println(\"slice 2nd columns, all rows: \", A[:,2]) # fast slicing\n", 153 | "end\n", 154 | "\n", 155 | "matrixFun()" 156 | ] 157 | }, 158 | { 159 | "cell_type": "markdown", 160 | "metadata": {}, 161 | "source": [ 162 | "Notice the difference between `size` and `length`." 163 | ] 164 | }, 165 | { 166 | "cell_type": "markdown", 167 | "metadata": {}, 168 | "source": [ 169 | "### N-Dimensional Arrays\n", 170 | "\n", 171 | "Sometimes we need to create tables with more than 2 dimensions. In this case usually the tables tend to be big, so there is no explicit way to create an n-dimensional array. The suggested practice is to create an array first, and then fill it either manually of using a loop. For several initialization options (zeros, ones, undefined, etc.). Please consult the documentation on [Construction and Initialization](https://docs.julialang.org/en/v1/manual/arrays/#Construction-and-Initialization).\n", 172 | "\n", 173 | "As an example, let’s suppose we want to create a `2x3x4` table, we would do it with `zeros(2,3,4)`. Let’s suppose we want to fill it with the product of the indexes, we can do it in the following way:" 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "execution_count": null, 179 | "metadata": {}, 180 | "outputs": [], 181 | "source": [ 182 | "function ndArrayFun()\n", 183 | " ndarray = zeros(Int64, 2,3,4)\n", 184 | " println(\"ndarray size: \", size(ndarray), \" type: \", typeof(ndarray) )\n", 185 | " println(\"ndarray values: \\n\", ndarray)\n", 186 | "\n", 187 | " for k in 1:4\n", 188 | " for j in 1:3\n", 189 | " for i in 1:2\n", 190 | " ndarray[i,j,k] = i*j*k\n", 191 | " end\n", 192 | " end\n", 193 | " end\n", 194 | " println(\"\\n\")\n", 195 | " println(\"ndarray values: \\n\", ndarray)\n", 196 | " \n", 197 | " println(\"\\n\")\n", 198 | " println(\"Slicing: \\n\")\n", 199 | " println(\"ndarray(:,:,1): \\n\", ndarray[:,:,1]) # slow\n", 200 | " println(\"\\n\")\n", 201 | " println(\"ndarray(1,:,:): \\n\", ndarray[1,:,:]) # fast\n", 202 | " return\n", 203 | "end\n", 204 | "\n", 205 | "ndArrayFun()" 206 | ] 207 | }, 208 | { 209 | "cell_type": "markdown", 210 | "metadata": {}, 211 | "source": [ 212 | "**NOTE:** Please not that Julia stores values in memory differently from Python: in Julia to obtain fast loops we need to iter first over columns (which means that the first index must vary first and so on). For this reason if we plan to store, for example, 42 2x2 matrices, we need to create an array of size 2x2x42 (while in Python we would have created a 42x2x2 table)." 213 | ] 214 | }, 215 | { 216 | "cell_type": "markdown", 217 | "metadata": {}, 218 | "source": [ 219 | "## Tuples\n", 220 | "\n", 221 | "A tuple is a fixed size group of variables which may share a common type but don’t need to.\n", 222 | "\n", 223 | "Unlike arrays, you cannot increase the size of a tuple once it has been created. Tuples are created using the following syntax:" 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "execution_count": null, 229 | "metadata": {}, 230 | "outputs": [], 231 | "source": [ 232 | "function tuplesInit()\n", 233 | " a = (1,2,3.) # default types\n", 234 | " println(a, \" typeof(a): \", typeof(a) )\n", 235 | " \n", 236 | " b = 1, 2, 3 # default types \n", 237 | " println(b, \" typeof(b): \", typeof(b) )\n", 238 | " \n", 239 | " c::Tuple{Float32,Float64} = (1., 2.)\n", 240 | " println(c, \" typeof(c): \", typeof(c) )\n", 241 | " \n", 242 | " d::Tuple{String, Float32, Int16} = (\"William\", 2., 1)\n", 243 | " println(d, \" typeof(d): \", typeof(d) )\n", 244 | "\n", 245 | " e = (10,) # still need comma for single-element tuple\n", 246 | " f = (10) # (this is just 10)\n", 247 | " println(e, \" typeof(e): \", typeof(e))\n", 248 | " println(f, \" typeof(f): \", typeof(f))\n", 249 | " return\n", 250 | "end\n", 251 | "\n", 252 | "tuplesInit()" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "So tuples can be created by using regular parentheses or none at all! Tuples are really handy, as it is possible to “unpack” a tuple over many values:" 260 | ] 261 | }, 262 | { 263 | "cell_type": "code", 264 | "execution_count": null, 265 | "metadata": {}, 266 | "outputs": [], 267 | "source": [ 268 | "function tupleUnpack()\n", 269 | " tuple1 = (1, 2, 3)\n", 270 | " a, b, c = tuple1\n", 271 | " # variables can be referenced with $variable, use \\$ to escape\n", 272 | " println(\"$a $b $c\")\n", 273 | " return\n", 274 | "end\n", 275 | "\n", 276 | "tupleUnpack()" 277 | ] 278 | }, 279 | { 280 | "cell_type": "markdown", 281 | "metadata": {}, 282 | "source": [ 283 | "It is also possible to use tuples to emulate multiple return values from functions:" 284 | ] 285 | }, 286 | { 287 | "cell_type": "code", 288 | "execution_count": null, 289 | "metadata": {}, 290 | "outputs": [], 291 | "source": [ 292 | "function tupleReturn()::Tuple{Int32,Int32,Int32}\n", 293 | " return 42, 43, 44\n", 294 | "end\n", 295 | "\n", 296 | "a, b, c = tupleReturn()\n", 297 | "print(\"$a $b $c\")" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [ 304 | "### Splatting\n", 305 | "\n", 306 | "It is possible to “unpack” a tuple and pass its arguments to a function with the following syntax:" 307 | ] 308 | }, 309 | { 310 | "cell_type": "code", 311 | "execution_count": null, 312 | "metadata": {}, 313 | "outputs": [], 314 | "source": [ 315 | "function tupleSplat(a, b, c)\n", 316 | " return a*b*c\n", 317 | "end\n", 318 | "\n", 319 | "tuple1 = (1,2,3)\n", 320 | "println(\"1x2x3 = \", tupleSplat(tuple1...))" 321 | ] 322 | }, 323 | { 324 | "cell_type": "markdown", 325 | "metadata": {}, 326 | "source": [ 327 | "So the `...` after a tuple will unpack it! This is useful but addictive, use it only if needed as it is better for clarity (and to avoid multiple dispatch errors) to call a function with its single parameters.\n", 328 | "\n", 329 | "### Names Tuples\n", 330 | "\n", 331 | "Named tuples are like tuples but with a name identifier for a single value, for example:\n" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "metadata": {}, 338 | "outputs": [], 339 | "source": [ 340 | "function tupleNamed()\n", 341 | " tuple = (a = 1, b = \"hello\")\n", 342 | " @show typeof(tuple)\n", 343 | " println( \"tuple[:a] = \", tuple.a )\n", 344 | " println( \"tuple[:b] = \", tuple[:b] )\n", 345 | " return\n", 346 | "end\n", 347 | "\n", 348 | "tupleNamed()" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "## Dictionaries\n", 356 | "\n", 357 | "A dictionary is a collection of keys and values. They are unordered (which means that the order of the keys is random) and are really useful when you need to organise, for example, a dataset. For ordered dictionaries see the [OrderedCollections.jl package](https://juliacollections.github.io/OrderedCollections.jl/latest/index.html).\n", 358 | "\n", 359 | "Let’s suppose we want to create an address book. A single entry should be able to store all the fundamental characteristics needed to identify a friend: the name of the contact, the phone number and the shoe size!\n", 360 | "I can even make a dictionary containing other dictionaries or add to an existing dictionary:\n", 361 | "\n", 362 | "**NOTE:** as dictionaries become complex use a package for \"pretty printing\". We use [PrettyPrinting.jl](https://github.com/thautwarm/PrettyPrint.jl) for our example." 363 | ] 364 | }, 365 | { 366 | "cell_type": "code", 367 | "execution_count": null, 368 | "metadata": {}, 369 | "outputs": [], 370 | "source": [ 371 | "using PrettyPrinting\n", 372 | "\n", 373 | "function dictInit()\n", 374 | " person1 = Dict(\"Name\" => \"Aurelio\", \"Phone\" => 123456789, \"Shoe-size\" => 40)\n", 375 | " pprintln(person1)\n", 376 | " \n", 377 | " person2 = Dict(\"Name\" => \"Elena\", \"Phone\" => 123456789, \"Shoe-size\" => 36)\n", 378 | " pprintln(person2)\n", 379 | " println()\n", 380 | " \n", 381 | " # dictionary containing dictionaries as values\n", 382 | " addressBook = Dict(\"Aurelio\" => person1, \"Elena\" => person2)\n", 383 | " println(\"Dictionary containing dictionaries as values: \")\n", 384 | " pprintln(addressBook)\n", 385 | " \n", 386 | " # add into existing dictionary\n", 387 | " println()\n", 388 | " person3 = Dict(\"Name\" => \"Vittorio\", \"Phone\" => 123456789, \"Shoe-size\" => 42)\n", 389 | " addressBook[\"Vittorio\"] = person3\n", 390 | " println(\"Dictionary containing dictionaries as values with added entry: \")\n", 391 | " pprintln(addressBook)\n", 392 | " return\n", 393 | "end\n", 394 | "\n", 395 | "dictInit()" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "We can access an element of the dictionary using its key or the `dict[key]` operator or the `get` or `get!` functions:\n", 403 | "\n", 404 | "- `get`: returns a default value if key is found, if not found return the default entry\n", 405 | "- `get!`: returns a default value if key is found, if not found return the default entry and adds to the dictionary\n", 406 | "\n", 407 | "**NOTE**: use the `get` function for safety as the intention is more clear. If `key` is not found in `dict[key]` it throws an error." 408 | ] 409 | }, 410 | { 411 | "cell_type": "code", 412 | "execution_count": null, 413 | "metadata": {}, 414 | "outputs": [], 415 | "source": [ 416 | "function dictGet()\n", 417 | " person3 = Dict(\"Name\" => \"Vittorio\", \"Phone\" => 123456789, \"Shoe-size\" => 42)\n", 418 | " # get(Dictionary, Key, Default if not found)\n", 419 | " value = get(person3, \"Name\", \"\")\n", 420 | " println(\"Found: \", value)\n", 421 | " println(\"Found: \", person3[\"Name\"])\n", 422 | " \n", 423 | " # get(Dictionary, Key, Default if not found)\n", 424 | " value = get(person3, \"City\", \"Oak Ridge\")\n", 425 | " println(\"get Not found: \", value)\n", 426 | " pprintln(person3)\n", 427 | " println()\n", 428 | " \n", 429 | " # get!(Dictionary, Key, add Default if not found)\n", 430 | " value = get!(person3, \"City\", \"Oak Ridge\")\n", 431 | " println(\"get! Not found, but added: \", value)\n", 432 | " pprintln(person3)\n", 433 | " \n", 434 | " return\n", 435 | "end\n", 436 | "\n", 437 | "dictGet()" 438 | ] 439 | }, 440 | { 441 | "cell_type": "markdown", 442 | "metadata": {}, 443 | "source": [ 444 | "## Conclusion\n", 445 | "\n", 446 | "In this lesson we have learned how to operate on arrays, tuples and dictionaries. Those structures are the basics of in-memory storage for any data. Arrays are lightweight and useful solutions to pass blocks of data, so use them whenever needed! Contrarily to C++, Julia has a built in garbage collector, so you don’t have to care about freeing memory and deleting pointers, as Julia will take care of it!\n", 447 | "\n", 448 | "\n", 449 | "## Questions\n", 450 | "\n", 451 | "1. Are Julia arrays column-major or row-major?\n", 452 | "2. Do Julia arrays start at 0 or 1?\n", 453 | "3. Are dictionaries ordered or unordered?\n", 454 | "4. Can tuples have multiple types?" 455 | ] 456 | }, 457 | { 458 | "cell_type": "code", 459 | "execution_count": null, 460 | "metadata": {}, 461 | "outputs": [], 462 | "source": [] 463 | } 464 | ], 465 | "metadata": { 466 | "kernelspec": { 467 | "display_name": "Julia 1.11.6", 468 | "language": "julia", 469 | "name": "julia-1.11" 470 | }, 471 | "language_info": { 472 | "file_extension": ".jl", 473 | "mimetype": "application/julia", 474 | "name": "julia", 475 | "version": "1.11.6" 476 | } 477 | }, 478 | "nbformat": 4, 479 | "nbformat_minor": 4 480 | } 481 | --------------------------------------------------------------------------------